unit StringPool; {$IFDEF FPC}{$MODE Delphi}{$ENDIF} interface type TStringBucket = record Hash: Cardinal; Value: string; end; PStringBucket = ^TStringBucket; TStringBuckets = array of TStringBucket; TStringPool = class private FBuckets: TStringBuckets; FCount: Integer; FGrowth: Integer; FCapacity: Integer; procedure Grow; public procedure StringIntern(var s: string); procedure Clear; property Count: Integer read FCount; end; implementation { TStringPool } procedure TStringPool.Clear; begin SetLength(FBuckets, 0); FCount := 0; FGrowth := 0; FCapacity := 0; end; procedure TStringPool.Grow; var i, j, n: Integer; oldBuckets: TStringBuckets; begin if FCapacity = 0 then FCapacity := 32 else FCapacity := FCapacity * 2; FGrowth := (FCapacity * 3) div 4 - FCount; oldBuckets := FBuckets; FBuckets := nil; SetLength(FBuckets, FCapacity); n := FCapacity - 1; for i := 0 to High(oldBuckets) do begin if oldBuckets[i].Hash = 0 then Continue; j := oldBuckets[i].Hash and (FCapacity - 1); while FBuckets[j].Hash <> 0 do j := (j + 1) and n; FBuckets[j].Hash := oldBuckets[i].Hash; FBuckets[j].Value := oldBuckets[i].Value; end; end; procedure TStringPool.StringIntern(var s: string); {$OVERFLOWCHECKS OFF} function HashString(const s: string): Cardinal; inline; var i: Integer; begin // modified FNV-1a using length as seed Result := Length(s); for i := 1 to Result do Result := (Result xor Ord(s[i])) * 16777619; end; {$OVERFLOWCHECKS ON} var hash: Cardinal; i: Integer; bucket: PStringBucket; begin if s = '' then Exit; if FGrowth = 0 then Grow; hash := HashString(s) shr 6; i := hash and (FCapacity - 1); repeat bucket := @FBuckets[i]; if (bucket.Hash = hash) and (bucket.Value = s) then begin s := bucket.Value; Exit; end else if bucket.Hash = 0 then begin bucket.Hash := hash; bucket.Value := s; Inc(FCount); Dec(FGrowth); Exit; end; i := (i + 1) and (FCapacity - 1); until False; end; end.