AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Netzwerkprotokoll zum Übertragen von Variant Streams
Thema durchsuchen
Ansicht
Themen-Optionen

Netzwerkprotokoll zum Übertragen von Variant Streams

Ein Thema von Zacherl · begonnen am 4. Mai 2011 · letzter Beitrag vom 10. Mai 2011
Antwort Antwort
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
Benutzerbild von Zacherl
Zacherl

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

AW: Netzwerkprotokoll zum Übertragen von Variant Streams

  Alt 5. Mai 2011, 13:10
push
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.008 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#3

AW: Netzwerkprotokoll zum Übertragen von Variant Streams

  Alt 10. Mai 2011, 10:37
Ohne mir den Source genau anzuschauen kann ich dir schon sagen, dass du dir mit so einem Protokoll keinen Gefallen tust, wenn du mal verschiedene Versionen miteinander kompatibel machen willst. Sobald sich irgendwelche Internas ändern (wie zum Beispiel mit Unicode geschehen, oder wie mit NativeInt unter verschiedenen Systemen geschehen wird) ist es nicht mehr zu gebrauchen, außer du bringst beide Seiten wieder auf einen Stand.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

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

AW: Netzwerkprotokoll zum Übertragen von Variant Streams

  Alt 10. Mai 2011, 12:18
Das ist kein Problem. Ansi und Unicode Strings werden differenziert. Ebenso bleiben bei den 64 Bit Delphi Versionen die Datentypen wie Integer, etc. ja 4 Byte groß. Die neuen NativeInt Typen kann ich einfach nachrüsten.

Wobei ich diese eh nicht verwenden werde, wenn eine Seite noch für 32 Bit kompiliert wurde. Insofern müsste ich eh alles neu builden.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.008 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#5

AW: Netzwerkprotokoll zum Übertragen von Variant Streams

  Alt 10. Mai 2011, 14:26
Sofern du dich auf Delphi auf beiden Seiten festlegen willst, und gegenüber möglichen Änderungen deiner Daten (neues Feld hinzugekommen, Datentyp hat sich geändert) anfällig sein möchtest, ist das sicherlich ganz brauchbar. Ich kann aber aus Erfahrung sagen, dass solch ein Protokoll die schlimmste Krätze sein kann, wenn man mit unterschiedlichen Versionen kompatibel sein möchte.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

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

AW: Netzwerkprotokoll zum Übertragen von Variant Streams

  Alt 10. Mai 2011, 15:28
Das glaube ich gerne, habe da auch ein wenig Erfahrung mit. Das Protokoll ist allerdings für ein kleines Projekt entstanden, bei dem beide Seiten mit Delphi programmiert sind. Im Prinzip ging es mir nur um einfachen Zugriff (ohne ständige Typkonvertierungen) und möglichst wenig Protokoll Overhead.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:39 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz