This sets the length of FStrings to 1 less than FBuckets. In TStringIntern.InternAdd, TStringInterm.Grow is only triggered when the size reaches FCapacity (now 17):
if FCount = FCapacity then
Grow;
Index := FCount;
Inc(FCount);
Bucket := @FBuckets[(AHash and $7FFFFFFF) mod FCapacity];
with FStrings[Index] do
begin
Next := Bucket^;
Hash := AHash;
Pointer(Name) := Pointer(S);
Inc(PLongint(@PByte(Name)[-8])^);
end;
This means that when FCount reaches 16, writes are performed to FStrings[16], which is beyond the memory allocated during Init.
TStringIntern.Init contains the following code:
FCount := 0; FCapacity := 16; GetMem(FStrings, FCapacity * SizeOf(FStrings[0])); FCapacity := 17; GetMem(FBuckets, FCapacity * SizeOf(FBuckets[0])); FillChar(FBuckets[0], FCapacity * SizeOf(FBuckets[0]), $FF);
This sets the length of FStrings to 1 less than FBuckets. In TStringIntern.InternAdd, TStringInterm.Grow is only triggered when the size reaches FCapacity (now 17):
if FCount = FCapacity then Grow; Index := FCount; Inc(FCount);
Bucket := @FBuckets[(AHash and $7FFFFFFF) mod FCapacity]; with FStrings[Index] do begin Next := Bucket^; Hash := AHash; Pointer(Name) := Pointer(S); Inc(PLongint(@PByte(Name)[-8])^); end;
This means that when FCount reaches 16, writes are performed to FStrings[16], which is beyond the memory allocated during Init.