如何解决这段代码中的字节排序问题?

时间:2012-01-28 15:58:26

标签: delphi endianness

为了读取特定格式的索引文件,我在不考虑字节顺序的情况下编写了以下代码:

unit uCBI;

interface

uses
  SysUtils,
  Classes,
  Generics.Collections;

type
  TIndexList = class
  private
    FIndexList:TList<Cardinal>;

    FOwnedStream:Boolean;
    FMemoryStream: TMemoryStream;
    function GetCount: Integer;

  protected

  public
    constructor Create(AStream:TMemoryStream; OwnedStream:Boolean=True);
    destructor Destroy; override;

    function Add(const Value: Cardinal): Integer;
    procedure Clear;

    procedure SaveToFile(AFileName:TFileName);
    procedure LoadFromFile(AFileName:TFileName);

    property Count: Integer read GetCount;
  end;

implementation

{ TIndexList }

function TIndexList.Add(const Value: Cardinal): Integer;
begin
  Result := FIndexList.Add(Value)
end;

procedure TIndexList.Clear;
begin
  FIndexList.Clear;
end;

constructor TIndexList.Create(AStream: TMemoryStream; OwnedStream: Boolean);
begin
  FMemoryStream := AStream;
  FOwnedStream := OwnedStream;
  FIndexList := TList<Cardinal>.Create;
end;

destructor TIndexList.Destroy;
begin
  if (FOwnedStream and Assigned(FMemoryStream)) then
    FMemoryStream.Free;

  FIndexList.Free;
  //
  inherited;
end;

function TIndexList.GetCount: Integer;
begin
  Result := FIndexList.Count;
end;

procedure TIndexList.LoadFromFile(AFileName: TFileName);
var
  lMemoryStream:TMemoryStream;
  lCount:Cardinal;
begin
  lMemoryStream := TMemoryStream.Create;

  try
    lMemoryStream.LoadFromFile(AFileName);
    lMemoryStream.ReadBuffer(lCount,SizeOf(Cardinal));

    if (lCount = Cardinal((lMemoryStream.Size-1) div SizeOf(Cardinal))) then
    begin
      FMemoryStream.Clear;
      lMemoryStream.Position :=0;
      FMemoryStream.CopyFrom(lMemoryStream,lMemoryStream.Size)
    end else
      raise Exception.CreateFmt('Corrupted CBI file: %s',[ExtractFileName(AFileName)]);
  finally
    lMemoryStream.Free;
  end;
end;

procedure TIndexList.SaveToFile(AFileName: TFileName);
var
  lCount:Cardinal;
  lItem:Cardinal;
begin
  FMemoryStream.Clear;

  lCount := FIndexList.Count;
  FMemoryStream.WriteBuffer(lCount,SizeOf(Cardinal));

  for lItem in FIndexList do
  begin
    FMemoryStream.WriteBuffer(lItem,SizeOf(Cardinal));
  end;
  //
  FMemoryStream.SaveToFile(AFileName);
end;

end.

它测试了它,似乎在需要时运行良好。当我用真实的样本文件进行大量测试时,我感到很惊讶。事实上,传统格式是使用Amiga计算机设计的,具有不同的字节顺序。

我的问题:

我该如何解决?

我想保持代码不变,并且想知道装饰的 TMemorySream 是否可以在大端和小端之间透明切换。

1 个答案:

答案 0 :(得分:3)

要更改红衣主教的'endianness',您可以使用以下内容:

function EndianChange(Value: Cardinal): Cardinal;
var
  A1: array [0..3] of Byte absolute Value;
  A2: array [0..3] of Byte absolute Result;
  I: Integer;

begin
  for I:= 0 to 3 do begin
    A2[I]:= A1[3 - I];
  end;
end;

如果您想保持代码不变,您可以编写自己的TMemoryStream后代,并使用上述函数覆盖其ReadWrite方法,如下所示:

function TMyMemoryStream.Read(var Buffer; Count: Integer): Longint;
var
  P: PCardinal;
  I, N: Integer;

begin
  inherited;
  P:= @Buffer;
  Assert(Count and 3 = 0);
  N:= Count shr 2;
  while N > 0 do begin
    P^:= EndianChange(P^);
    Inc(P);
    Dec(N);
  end;
end;