stringdelphimetadata

TStringMetaData access by record in Delphi 11.1.5


I want to see the meta data for strings. I defined a record type and a utility class for this:

type
  PStringMetaData = ^TStringMetaData;
  TStringMetaData = packed record
    codePage : word; // -12
    charSize : word; // -10
    refCount : integer; //-8
    length : integer; //-4
    chars : array [0..0] of char; // 0
  end;

TStringUtility = class
  public
    class function getMetaData( const str_ : string ) : PStringMetaData;
end;

class function TStringUtility.getMetaData( const str_ : string ) : PStringMetaData;
begin
  Result := PStringMetaData( pchar( str_ ) - 12 );
end;

But the TForm1.Button1Click does not work as I expected.

procedure TForm1.Button1Click(Sender: TObject);
var
  str, str2: string;
  psmd : PStringMetaData;
begin
  str := 'Hello, World!';
  psmd := TStringUtility.getMetaData( str );
  showmessage('Reference count: ' + StringRefCount( str ).ToString );
  str2 := str;
  showmessage('Reference count: ' + StringRefCount( str ).ToString );
end;

The StringRefCount function gives back -1. What is the reason?


Solution

  • The reason is how strings are handled internally.

    The string 'Hello, World!' is a string literal that the compiler puts into the binary. It is marked with a refcount of -1 to indicate that it is a string literal.

    When assigning a string to a local string variable, the compiler inserts a call to System._UStrLAsg, resulting in the local variable containing the reference to the string literal. This is an optimization because a local variable cannot escape the current routine and thus it is safe to point to the string literal.

    Only when assigning to a field, a global variable, or a function result, the compiler uses a call to System._UStrAsg which handles the special case of refcount -1 and then allocates a new string and copies the content of the string literal into it.

    Furthermore, your TStringMetaData declaration is wrong - the fields are in incorrect order and the PChar field is simply wrong. See the documentation for details.