PDA

View Full Version : Reading and writing Variants efficently?



jdarling
21-11-2006, 03:47 PM
Ok, I know how to do this, I am just hoping that there is an easier way before I write the long case statements required :). Basically for my TTCPServer for SDL I want to add support for Variants. This means writing the size of the variant and then the data block. Why go through all this trouble and not just write a full sized variant? Simple effeciency on the pipe. If I can get away with writing 2 bytes (integer) instead of 255 bytes (string) then I'm better off. Not to mention that SizeOf doesn't work on AnsiString Variants :(.

Seems like you used to be able to do something like:
var
varData : Tvardata;
varSize : Cardinal;
varPtr : Pointer;
begin
varData := TVarData(V);
varSize := varData.Size;
varPtr := @varData.vbytes[0];
end;

But this doesn't seem to work any more, of course I'm a bit rusty on Variants. Right now I'm not looking at variant arrays, I know they require loops and scanning of the entire variant record type. Any ideas on an effecient way instead of hand coding every single frigging thing?

jdarling
21-11-2006, 10:53 PM
Ok, here is my first attempt (that fails). Can anyone spot what I'm doing wrong?

uses
Variants;

Function GetVariantSize(v : Variant): Integer;
var
vt: TVarType;
Begin
vt := TVarData(v).vtype;
Result := -1;
Case vt Of
varSmallint : Result := Sizeof( SmallInt );
varInteger : Result := Sizeof( Integer );
varSingle : Result := Sizeof( Single );
varDouble : Result := Sizeof( Double );
varCurrency : Result := Sizeof( Currency );
varDate : Result := Sizeof( TDateTime );
//varOleStr : Result := Sizeof( Widestring );
varDispatch : Result := Sizeof( Pointer );
varError : Result := Sizeof( Cardinal );
varBoolean : Result := Sizeof( WordBool );
varVariant : Result := Sizeof( Variant );
varUnknown : Result := Sizeof( Pointer );
varShortInt : Result := Sizeof( ShortInt );
varByte : Result := Sizeof( Byte );
varWord : Result := Sizeof( Word );
varLongWord : Result := Sizeof( LongWord );
varInt64 : Result := Sizeof( Int64 );
varOleStr,// : Result := Sizeof( Widestring );
varString : Result := Length(AnsiString(v));
End; { Case }
End;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
v2,
v : Variant;
vd: TVarData;
vt: tvartype;
i : Integer;
ms: TMemoryStream;
procedure WriteBlock(p : Pointer; siz : Cardinal);
begin
ms.WriteBuffer(siz, sizeof(siz));
ms.WriteBuffer(p, siz);
end;
procedure ReadBlock(p : Pointer);
var
siz : Cardinal;
begin
ms.ReadBuffer(siz, sizeof(siz));
ms.ReadBuffer(p, siz);
end;
procedure WriteString(s:AnsiString);
begin
WriteBlock(@s[1], length(s));
end;
function ReadString : AnsiString;
var
siz : Cardinal;
begin
ms.ReadBuffer(siz, sizeof(siz));
setlength(ReadString, siz);
ms.ReadBuffer(ReadString[1], siz);
end;
begin
v := 'asdf';//123;
vd := TVarData(v);
vt := vd.vtype;

ms := TMemoryStream.Create;
try
WriteBlock(@vt, SizeOf(vt));
case vt of
varString, varOleStr : WriteString(v);
else
WriteBlock(@TVarData(v).vwords[0], GetVariantSize(v));
end;

ms.Position := 0;
ReadBlock(@vt);
case vt of
varString, varOleStr : v2 := ReadString;
else
TVarData(v2).vType := vt;
ReadBlock(@TVarData(v2).vwords[0]);
end;
Memo1.lines.Add(v2);
finally
ms.Free;
end;
end;

VilleK
22-11-2006, 02:11 PM
You need a couple of extra "^", this is working:

procedure TForm1.Button1Click(Sender: TObject);
var
v2,
v : Variant;
vd: TVarData;
vt: tvartype;
i : Integer;
ms: TMemoryStream;
procedure WriteBlock(p : Pointer; siz : Cardinal);
begin
ms.WriteBuffer(siz, sizeof(siz));
ms.WriteBuffer(p^, siz);
end;
procedure ReadBlock(p : Pointer);
var
siz : Cardinal;
begin
ms.ReadBuffer(siz, sizeof(siz));
ms.ReadBuffer(p^, siz);
end;
procedure WriteString(s:AnsiString);
begin
WriteBlock(@s[1], length(s));
end;
function ReadString : AnsiString;
var
siz : Cardinal;
begin
ms.ReadBuffer(siz, sizeof(siz));
setlength(Result, siz);
ms.ReadBuffer(Result[1], siz);
end;
begin
v := 'asdf';//123;
vd := TVarData(v);
vt := vd.vtype;

ms := TMemoryStream.Create;
try
WriteBlock(@vt, SizeOf(vt));
case vt of
varString, varOleStr : WriteString(v);
else
WriteBlock(@TVarData(v).vwords[0], GetVariantSize(v));
end;

ms.Position := 0;
ReadBlock(@vt);
case vt of
varString, varOleStr : v2 := ReadString;
else
TVarData(v2).vType := vt;
ReadBlock(@TVarData(v2).vwords[0]);
end;
Button1.Caption := v2;
finally
ms.Free;
end;
end;