Lindawb 0 Posted February 10, 2022 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
Attila Kovacs 629 Posted February 10, 2022 I can't help you understand it but I'd suggest you to check the SetString() methods. Share this post Link to post
qubits 20 Posted February 11, 2022 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
stijnsanders 35 Posted February 11, 2022 (edited) 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 February 11, 2022 by stijnsanders Share this post Link to post
Remy Lebeau 1396 Posted February 11, 2022 (edited) 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 February 11, 2022 by Remy Lebeau Share this post Link to post
Remy Lebeau 1396 Posted February 11, 2022 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
Lindawb 0 Posted February 23, 2022 (edited) 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 February 23, 2022 by Lindawb Share this post Link to post
Remy Lebeau 1396 Posted February 25, 2022 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
Lindawb 0 Posted February 26, 2022 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