Jump to content
Lindawb

vtString and vtWideString & vtAnsiString

Recommended Posts

Hello, please I need to understand how pointer works in this example

 

if

    for I := 0 to High(Values) do
        with Values do case VType of   

vtString:    //works fine
                begin
                S[P]:=ansiChar(Length(VString^));
                System.Move(VString^[1],S[P+1],Length(VString^));
                Inc(P,Length(VString^)+1);
                ls:=I=High(Values);

                end;

  vtAnsiString:   // not working , something wrong
                begin
               S[P]:=ansiChar(sizeof(VAnsiString));
                System.Move(AnsiString(Values.VAnsiString),S[P+1],sizeof(VAnsiString));
                Inc(P,sizeof(VAnsiString)+1);
                ls:=I=High(Values);

      end;

 

Thank you

 

Share this post


Link to post

lol, a test.. ok, i'll stab..

 

if the top works. then VString is in fact a point and has to be de-referenced hence the ^,

but the bottom does not treat VAnsiString as a pointer and it fails, so maybe it's also a pointer too..

 

make the bottom look just like the top except VAnsiString instead of VString and give it a wirl..

note, only commenting on //works fine //not working because it all looks like one big IF to me.. 🙂

 

Share this post


Link to post

I see in System.pas that VAnsiString is of type pointer and not of type AnsiString. I'm not sure you can 'just' cast it into type AnsiString (which happens to also be a pointer internally), but assuming you can:

SizeOf(VAnsiString)

will probably always be 4 (on 32-bits, 8 on 64-bits), I guess you'll need

Length(AnsiString(VAnsiString))

then

Move(AnsiString(VAnsiString),

... will actually read the pointer value (and whatever happens to be in memory after that) not the string data itself.

You have to dereference, either explicitly:

Move(VAnsiString^,

... or I prefer to do this as if I'm letting Move know where the start of the string data is:

Move(AnsiString(VAnsiString)[1],

...

And you use SizeOf for the Inc again, change that to Length, of better still use an extra local variable so you only need 1 call to Length (and the compiler can optimize register allocation for it)

Edited by stijnsanders

Share this post


Link to post
19 hours ago, Lindawb said:

Hello, please I need to understand how pointer works in this example

What is 'S' in this code?  It appears to be a pointer into an array of AnsiChars.  The code appears to be building up an inline array of length-prefixed ANSI strings.  Maybe for transmission/storage somewhere?

 

In any case...

 

vtString indicates a ShortString.  Since ShortString is a fixed-length string, the TVarRec.VString field is a pointer to an actual ShortString variable.  The code is handling the VString field correctly, dereferencing that pointer to access the ShortString, and thus its length and characters.

 

vtAnsiString indicates an AnsiString.  An AnsiString is a dynamic length string, and an AnsiString variable is just a pointer to the AnsiString's payload.  As such, the TVarRec.VAnsiString field is a pointer to an AnsiString's payload, NOT a pointer to an AnsiString variable.  The code is not handling the VAnsiString field correctly, most importantly its use of sizeof() is wrong to determine the AnsiString's length.  You need to typecast the VAnsiString pointer to an AnsiString and then query its Length() instead.

 

Try this:

var Len: Integer;
...
for I := Low(Values) to High(Values) do
  with Values[I] do
    case VType of   
      vtString:
      begin
        Len := Length(VString^); // will never exceed 255
        S[P] := AnsiChar(Len);
        System.Move(VString^[1], S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      vtAnsiString:
      begin
        Len := Length(AnsiString(VAnsiString));
        if Len > 255 raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(AnsiString(VAnsiString))^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      ...

    end;

WideString and UnicodeString are also dynamic length string types implemented as pointers, and so vtWideString and vtUnicodeString are handled similarly to vtAnsiString, in that you simply type-cast the TVarRec.VWideString and TVarRec.VUnicodeString pointers as-is to WideString and UnicodeString, respectively.  Just know that you will need an additional cast to AnsiString for those values to fit in with the rest of this code, eg:

var
  Len: Integer;
  Tmp: AnsiString;
...
for I := Low(Values) to High(Values) do
  with Values[I] do
    case VType of   
      ...

      vtWideString:
      begin
        Tmp := AnsiString(WideString(VWideString));
        Len := Length(Tmp);
        if Len > 255 raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(Tmp)^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      vtUnicodeString:
      begin
        Tmp := AnsiString(UnicodeString(VUnicodeString));
        Len := Length(Tmp);
        if Len > 255 raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(Tmp)^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      ...

    end;

 

Edited by Remy Lebeau

Share this post


Link to post
9 hours ago, stijnsanders said:

I see in System.pas that VAnsiString is of type pointer and not of type AnsiString. I'm not sure you can 'just' cast it into type AnsiString (which happens to also be a pointer internally), but assuming you can

Yes, you can.  You can see the RTL doing exactly that, for instance in the SysUtils.(Wide)FormatBuf() functions.

9 hours ago, stijnsanders said:

I guess you'll need


Length(AnsiString(VAnsiString))

Yes.

9 hours ago, stijnsanders said:

 


Move(AnsiString(VAnsiString),

... will actually read the pointer value (and whatever happens to be in memory after that) not the string data itself.

Yes.

9 hours ago, stijnsanders said:

You have to dereference, either explicitly:


Move(VAnsiString^,

Just know that if the AnsiString is empty, the pointer will be nil, so don't dereference it.

9 hours ago, stijnsanders said:

... or I prefer to do this as if I'm letting Move know where the start of the string data is:


Move(AnsiString(VAnsiString)[1],

I prefer to use this instead:

Move(PAnsiChar(AnsiString(VAnsiString))^,

If the AnsiString is empty (ie, a nil pointer), the PAnsiChar cast will return a non-nil pointer to a #0 AnsiChar in static memory. So the dereference is always valid.  Also, this isn't affected by 0-based vs 1-based string indexing.

9 hours ago, stijnsanders said:

And you use SizeOf for the Inc again, change that to Length

Yes.

Share this post


Link to post
On 2/11/2022 at 12:40 PM, Remy Lebeau said:

What is 'S' in this code?  It appears to be a pointer into an array of AnsiChars.  The code appears to be building up an inline array of length-prefixed ANSI strings.  Maybe for transmission/storage somewhere?

 

In any case...

 

vtString indicates a ShortString.  Since ShortString is a fixed-length string, the TVarRec.VString field is a pointer to an actual ShortString variable.  The code is handling the VString field correctly, dereferencing that pointer to access the ShortString, and thus its length and characters.

 

vtAnsiString indicates an AnsiString.  An AnsiString is a dynamic length string, and an AnsiString variable is just a pointer to the AnsiString's payload.  As such, the TVarRec.VAnsiString field is a pointer to an AnsiString's payload, NOT a pointer to an AnsiString variable.  The code is not handling the VAnsiString field correctly, most importantly its use of sizeof() is wrong to determine the AnsiString's length.  You need to typecast the VAnsiString pointer to an AnsiString and then query its Length() instead.

 

Try this:


var Len: Integer;
...
for I := Low(Values) to High(Values) do
  with Values[I] do
    case VType of   
      vtString:
      begin
        Len := Length(VString^); // will never exceed 255
        S[P] := AnsiChar(Len);
        System.Move(VString^[1], S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      vtAnsiString:
      begin
        Len := Length(AnsiString(VAnsiString));
        if Len > 255 raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(AnsiString(VAnsiString))^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      ...

    end;

WideString and UnicodeString are also dynamic length string types implemented as pointers, and so vtWideString and vtUnicodeString are handled similarly to vtAnsiString, in that you simply type-cast the TVarRec.VWideString and TVarRec.VUnicodeString pointers as-is to WideString and UnicodeString, respectively.  Just know that you will need an additional cast to AnsiString for those values to fit in with the rest of this code, eg:


var
  Len: Integer;
  Tmp: AnsiString;
...
for I := Low(Values) to High(Values) do
  with Values[I] do
    case VType of   
      ...

      vtWideString:
      begin
        Tmp := AnsiString(WideString(VWideString));
        Len := Length(Tmp);
        if Len > 255 raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(Tmp)^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      vtUnicodeString:
      begin
        Tmp := AnsiString(UnicodeString(VUnicodeString));
        Len := Length(Tmp);
        if Len > 255 raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(Tmp)^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      ...

    end;

 

Hello ,

sorry , the S is shortstring;

    var
        I:integer;
        P:byte;
        S:ShortString;
        v,d:double;
        OldPos:integer;
        ls:boolean;

all worked except  the vtUnicodeString:, looks the length of P more than expected . any idea ?

 

Thank you so much

 

 

Edited by Lindawb

Share this post


Link to post
On 2/23/2022 at 11:35 AM, Lindawb said:

sorry , the S is shortstring;

There has to be more involved than just that.  Since the code is indexing into S using P, and it is incrementing P after copying AnsiChars into S, and the code is looped, that implies that the code is storing more than 1 string value into that ShortString.  If it were just a matter of converting 1 (Ansi|Unicode|Wide)String to 1 ShortString, the code could be much simpler, using a normal assignment and letting the compiler do all of the hard work.

On 2/23/2022 at 11:35 AM, Lindawb said:

all worked except  the vtUnicodeString:, looks the length of P more than expected . any idea ?

Can you be more specific?  I know for a fact that the code I showed earlier is handling UnicodeString correctly.  Though, now that I can see your variable declarations and see that P is a Byte not an Integer, there is an extra validation that the code should be doing when copying AnsiChars into S to make sure S doesn't overflow, eg:

var
  I: Integer;
  P: Byte;
  S: ShortString;
  ls: Boolean; 
  ...
  Len: Integer;
  Tmp: AnsiString;
...
for I := Low(Values) to High(Values) do
  with Values[I] do
    case VType of   
      vtString:
      begin
        Len := Length(VString^);
        if (Integer(P) + 1 + Len) > 256 then raise ...;
        S[P] := AnsiChar(Len);
        System.Move(VString^[1], S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      vtAnsiString:
      begin
        Tmp := AnsiString(VAnsiString);
        Len := Length(Tmp);
        if (Integer(P) + 1 + Len) > 256 then raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(Tmp)^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      vtWideString:
      begin
        Tmp := AnsiString(WideString(VWideString));
        Len := Length(Tmp);
        if (Integer(P) + 1 + Len) > 256 then raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(Tmp)^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      vtUnicodeString:
      begin
        Tmp := AnsiString(UnicodeString(VUnicodeString));
        Len := Length(Tmp);
        if (Integer(P) + 1 + Len) > 256 then raise ...;
        S[P] := AnsiChar(Len);
        System.Move(PAnsiChar(Tmp)^, S[P+1], Len);
        Inc(P, Len + 1);
        ls := I = High(Values);
      end;

      ...

    end;

 

Share this post


Link to post

Thank you so much for your time and knowledge , as I posted in my other question this is part of the zip file

All I needed to Delphi XE10 new vType

vtUnicodeString , vtAnsiString, vtWideString
but looks I need vtUnicodeString more than anything else.
I added your vtUnicodeString size error..

 

Thanks again

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×