Einzelnen Beitrag anzeigen

Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#1

Netzwerkprotokoll zum Übertragen von Variant Streams

  Alt 4. Mai 2011, 13:45
Delphi-Version: 2010
Hallo zusammen,

nun da ich das Problem mit TVarRec / Variant gelöst habe, presentiere ich hier mal die vorläufige Version des Protokolls. Die Unit besteht aus zwei Klassen:
Der Encoder dient zum Erstellen des Pakets. Über die Methode Append() können Daten angehangen werden.
Der Decoder kann Datensätze anhand ihrer Indizes aus dem fertigen Paket auslesen. Die Daten werden als Variant zurückgegeben.

Praktisch könnte man also sagen, es handelt sich um eine Art Stream für Variants.

Der Code sieht etwas umständlich aus. Meine Frage daher, ob jemand spontan noch Verbesserungsvorschläge hat. Insbesondere auch zur Performance.
Achso, vielleicht noch zum generellen Aufbau des Protokolls. Vor jedem Datensatz wird ein Byte geschrieben, welches den Typ der folgenden Daten enthält. Bei Strings folgt diesem Typ Identifier zusätzlich noch ein 4 Byte Cardinal Wert, welcher die Länge des Strings beinhaltet. Danach folgen die eigentlichen Daten.

Delphi-Quellcode:
unit dxVariantStream;

interface

uses
  Windows;

type
  TdxVariantEncoder = class(TObject)
  private
    FData: Pointer;
    FSize: Int64;
  protected
    function CalculateDataSize(V: Variant): Cardinal;
    function WriteValue(Offset: Cardinal; V: Variant): Cardinal;
  public
    procedure Append(Value: Variant); overload;
    procedure Append(Values: array of Variant); overload;
    constructor Create;
    destructor Destroy; override;
    property Data: Pointer read FData;
    property Size: Int64 read FSize;
  end;

  TdxVariantDecoder = class(TObject)
  private
    FIndexMap: array of Cardinal;
    FItemCount: Integer;
    FData: Pointer;
  protected
    function GetVariantItem(I: Integer): Variant;
  public
    constructor Create(Data: Pointer; Size: Int64);
    destructor Destroy; override;
    property Items[Index: Integer]: Variant read GetVariantItem; default;
    property ItemCount: Integer read FItemCount;
  end;

implementation

uses
  dxException;

type
  ECommandParserException = class(Exception);

{ TCommandEncoder }

procedure TdxVariantEncoder.Append(Value: Variant);
var
  Offset: Cardinal;
begin
  Offset := FSize;
  FSize := FSize + CalculateDataSize(Value);
  if Assigned(FData) then
  begin
    ReallocMem(FData, FSize);
  end else
  begin
    GetMem(FData, FSize);
  end;
  WriteValue(Offset, Value);
end;

procedure TdxVariantEncoder.Append(Values: array of Variant);
var
  BufferSize, Offset: Cardinal;
  I: Integer;
begin
  BufferSize := 0;
  for I := Low(Values) to High(Values) do
  begin
    Inc(BufferSize, CalculateDataSize(Values[I]));
  end;
  Offset := FSize;
  FSize := FSize + BufferSize;
  if Assigned(FData) then
  begin
    ReallocMem(FData, FSize);
  end else
  begin
    GetMem(FData, FSize);
  end;
  for I := Low(Values) to High(Values) do
  begin
    Inc(Offset, WriteValue(Offset, Values[I]));
  end;
end;

constructor TdxVariantEncoder.Create;
begin
  inherited Create;
  FSize := 0;
end;

destructor TdxVariantEncoder.Destroy;
begin
  if Assigned(FData) then
  begin
    FreeMem(FData);
  end;
  inherited;
end;

function TdxVariantEncoder.WriteValue(Offset: Cardinal; V: Variant): Cardinal;
var
  VarData: TVarData;
begin
  Result := 0;
  VarData := TVarData(V);
  case VarData.VType of
    varSmallInt:
      begin
        PByte(Cardinal(FData) + Offset)^ := $0;
        PSmallInt(Cardinal(FData) + Offset + 1)^ := VarData.VSmallInt;
        Result := 1 + SizeOf(SmallInt);
      end;
    varInteger:
      begin
        PByte(Cardinal(FData) + Offset)^ := $1;
        PInteger(Cardinal(FData) + Offset + 1)^ := VarData.VInteger;
        Result := 1 + SizeOf(Integer);
      end;
    varSingle:
      begin
        PByte(Cardinal(FData) + Offset)^ := $2;
        PSingle(Cardinal(FData) + Offset + 1)^ := VarData.VSingle;
        Result := 1 + SizeOf(Single);
      end;
    varDouble:
      begin
        PByte(Cardinal(FData) + Offset)^ := $3;
        PDouble(Cardinal(FData) + Offset + 1)^ := VarData.VDouble;
        Result := 1 + SizeOf(Double);
      end;
    varCurrency:
      begin
        PByte(Cardinal(FData) + Offset)^ := $4;
        PCurrency(Cardinal(FData) + Offset + 1)^ := VarData.VCurrency;
        Result := 1 + SizeOf(Currency);
      end;
    varDate:
      begin
        PByte(Cardinal(FData) + Offset)^ := $5;
        PDateTime(Cardinal(FData) + Offset + 1)^ := VarData.VDate;
        Result := 1 + SizeOf(TDateTime);
      end;
    varBoolean:
      begin
        PByte(Cardinal(FData) + Offset)^ := $6;
        PWordBool(Cardinal(FData) + Offset + 1)^ := VarData.VBoolean;
        Result := 1 + SizeOf(WordBool);
      end;
    varShortInt:
      begin
        PByte(Cardinal(FData) + Offset)^ := $7;
        PShortInt(Cardinal(FData) + Offset + 1)^ := VarData.VShortInt;
        Result := 1 + SizeOf(ShortInt);
      end;
    varByte:
      begin
        PByte(Cardinal(FData) + Offset)^ := $8;
        PByte(Cardinal(FData) + Offset + 1)^ := VarData.VByte;
        Result := 1 + SizeOf(Byte);
      end;
    varWord:
      begin
        PByte(Cardinal(FData) + Offset)^ := $9;
        PWord(Cardinal(FData) + Offset + 1)^ := VarData.VWord;
        Result := 1 + SizeOf(Word);
      end;
    varLongWord:
      begin
        PByte(Cardinal(FData) + Offset)^ := $A;
        PLongWord(Cardinal(FData) + Offset + 1)^ := VarData.VLongWord;
        Result := 1 + SizeOf(LongWord);
      end;
    varInt64:
      begin
        PByte(Cardinal(FData) + Offset)^ := $B;
        PInt64(Cardinal(FData) + Offset + 1)^ := VarData.VInt64;
        Result := 1 + SizeOf(Int64);
      end;
    varUInt64:
      begin
        PByte(Cardinal(FData) + Offset)^ := $C;
        PUInt64(Cardinal(FData) + Offset + 1)^ := VarData.VUInt64;
        Result := 1 + SizeOf(UInt64);
      end;
    varString:
      begin
        PByte(Cardinal(FData) + Offset)^ := $D;
        Result := Length(AnsiString(VarData.VString)) * SizeOf(AnsiChar);
        PCardinal(Cardinal(FData) + Offset + 1)^ := Result;
        CopyMemory(Pointer(Cardinal(FData) + Offset + 5), VarData.VString,
          Result);
        Inc(Result, 5);
      end;
    varUString:
      begin
        PByte(Cardinal(FData) + Offset)^ := $E;
        Result := Length(AnsiString(VarData.VUString)) * SizeOf(WideChar);
        PCardinal(Cardinal(FData) + Offset + 1)^ := Result;
        CopyMemory(Pointer(Cardinal(FData) + Offset + 5), VarData.VUString,
          Result);
        Inc(Result, 5);
      end;
  end;
end;

function TdxVariantEncoder.CalculateDataSize(V: Variant): Cardinal;
var
  VarData: TVarData;
begin
  Result := 0;
  VarData := TVarData(V);
  case VarData.VType of
    varSmallInt: Result := 1 + SizeOf(SmallInt);
    varInteger: Result := 1 + SizeOf(Integer);
    varSingle: Result := 1 + SizeOf(Single);
    varDouble: Result := 1 + SizeOf(Double);
    varCurrency: Result := 1 + SizeOf(Currency);
    varDate: Result := 1 + SizeOf(TDateTime);
    varBoolean: Result := 1 + SizeOf(WordBool);
    varShortInt: Result := 1 + SizeOf(ShortInt);
    varByte: Result := 1 + SizeOf(Byte);
    varWord: Result := 1 + SizeOf(Word);
    varLongWord: Result := 1 + SizeOf(LongWord);
    varInt64: Result := 1 + SizeOf(Int64);
    varUInt64: Result := 1 + SizeOf(UInt64);
    varString: Result :=
      5 + Length(AnsiString(VarData.VString)) * SizeOf(AnsiChar);
    varUString: Result :=
      5 + Length(AnsiString(VarData.VUString)) * SizeOf(WideChar);
  end;
end;

{ TCommandDecoder }

constructor TdxVariantDecoder.Create(Data: Pointer; Size: Int64);
var
  Offset: Cardinal;
begin
  inherited Create;
  FData := Data;
  FItemCount := 0;
  Offset := 0;
  while (Offset < Size) do
  begin
    Inc(FItemCount);
    SetLength(FIndexMap, FItemCount);
    FIndexMap[FItemCount - 1] := Offset;
    case PByte(Cardinal(FData) + Offset)^ of
      $0: Inc(Offset, 1 + SizeOf(SmallInt));
      $1: Inc(Offset, 1 + SizeOf(Integer));
      $2: Inc(Offset, 1 + SizeOf(Single));
      $3: Inc(Offset, 1 + SizeOf(Double));
      $4: Inc(Offset, 1 + SizeOf(Currency));
      $5: Inc(Offset, 1 + SizeOf(TDateTime));
      $6: Inc(Offset, 1 + SizeOf(WordBool));
      $7: Inc(Offset, 1 + SizeOf(ShortInt));
      $8: Inc(Offset, 1 + SizeOf(Byte));
      $9: Inc(Offset, 1 + SizeOf(Word));
      $A: Inc(Offset, 1 + SizeOf(LongWord));
      $B: Inc(Offset, 1 + SizeOf(Int64));
      $C: Inc(Offset, 1 + SizeOf(UInt64));
      $D: Inc(Offset, 5 + PCardinal(DWord(FData) + Offset + 1)^);
      $E: Inc(Offset, 5 + PCardinal(DWord(FData) + Offset + 1)^);
    end;
  end;
end;

destructor TdxVariantDecoder.Destroy;
begin

  inherited;
end;

function TdxVariantDecoder.GetVariantItem(I: Integer): Variant;
var
  DataType: Byte;
  VarData: TVarData;
  A: AnsiString;
  W: WideString;
  Size: Cardinal;
begin
  if (not (I in [Low(FIndexMap) .. High(FIndexMap)])) then
  begin
    raise ECommandParserException.Create('Data table index out of bounds.');
  end;
  DataType := PByte(DWord(FData) + FIndexMap[I])^;
  case DataType of
    $0: Result := PSmallInt(Cardinal(FData) + FIndexMap[I] + 1)^;
    $1: Result := PInteger(Cardinal(FData) + FIndexMap[I] + 1)^;
    $2: Result := PSingle(Cardinal(FData) + FIndexMap[I] + 1)^;
    $3: Result := PDouble(Cardinal(FData) + FIndexMap[I] + 1)^;
    $4: Result := PCurrency(Cardinal(FData) + FIndexMap[I] + 1)^;
    $5: Result := PDateTime(Cardinal(FData) + FIndexMap[I] + 1)^;
    $6: Result := PWordBool(Cardinal(FData) + FIndexMap[I] + 1)^;
    $7: Result := PShortInt(Cardinal(FData) + FIndexMap[I] + 1)^;
    $8: Result := PByte(Cardinal(FData) + FIndexMap[I] + 1)^;
    $9: Result := PWord(Cardinal(FData) + FIndexMap[I] + 1)^;
    $A: Result := PLongWord(Cardinal(FData) + FIndexMap[I] + 1)^;
    $B: Result := PInt64(Cardinal(FData) + FIndexMap[I] + 1)^;
    $C: Result := PUInt64(Cardinal(FData) + FIndexMap[I] + 1)^;
    $D:
      begin
        VarData.VType := varString;
        Size := PCardinal(Cardinal(FData) + FIndexMap[I] + 1)^;
        SetLength(A, Size div SizeOf(AnsiChar));
        CopyMemory(@A[1], Pointer(Cardinal(FData) + FIndexMap[I] + 5), Size);
        Result := A;
      end;
    $E:
      begin
        VarData.VType := varUString;
        Size := PCardinal(Cardinal(FData) + FIndexMap[I] + 1)^;
        SetLength(W, Size div SizeOf(WideChar));
        CopyMemory(@W[1], Pointer(Cardinal(FData) + FIndexMap[I] + 5), Size);
        Result := W;
      end;
  end;
end;

end.
Hier noch ein kurzes Beispiel, wie die Klassen verwendet werden:
Delphi-Quellcode:
procedure TForm6.Button1Click(Sender: TObject);
var
  Encoder: TdxVariantEncoder;
  Decoder: TdxVariantDecoder;
begin
  Encoder := TdxVariantEncoder.Create;
  try
    Encoder.Append([1, -120000, 'testwide', AnsiString('testansi'), false]);
    Encoder.Append(Word(2));
    Encoder.Append([Int64(33), 123.444]);
    Decoder := TdxVariantDecoder.Create(Encoder.Data, Encoder.Size);
    try
      ShowMessage(Decoder[3]); // testansi
      ShowMessage(Decoder[7]); // 123.444
    finally
      Decoder.Free;
    end;
  finally
    Encoder.Free;
  end;
end;
Viele Grüße
Zacherl
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl ( 4. Mai 2011 um 16:07 Uhr)
  Mit Zitat antworten Zitat