Как исправить проблему с порядком байтов в этом фрагменте кода? - PullRequest
1 голос
/ 28 января 2012

Чтобы прочитать индексный файл в определенном формате, я приготовил следующий фрагмент кода без учета порядка следования байтов:

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 Ответ

3 голосов
/ 28 января 2012

Чтобы изменить 'порядковый номер' кардиналов, вы можете использовать следующее:

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 и переопределить его Read и Write методы, используя вышеуказанную функцию, например:

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;
...