{ ***************************************************************************** * * * This file is part of the Lazarus Component Library (LCL) * * * * See the file COPYING.modifiedLGPL, included in this distribution, * * for details about the copyright. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * ***************************************************************************** Thanks to Markus Waldenburg. Ported this unit to fpGUI by Graeme Geldenhuys. TStringHashList is supposed to be faster than FPC's TFPObjectHashTable. This unit is used in gfx_pofile unit. } unit gfx_stringhashlist; {$mode objfpc}{$H+} interface uses Classes, SysUtils, gfx_constants; type PStringHashItem = ^TStringHashItem; TStringHashItem = record HashValue: Cardinal; Key: String; Data: Pointer; end; PStringHashItemList = ^PStringHashItem; TStringHashList = class(TObject) private FList: PStringHashItemList; FCount: Integer; fCaseSensitive: Boolean; function CompareString(const Value1, Value2: String): Boolean; function CompareValue(const Value1, Value2: Cardinal): Integer; function GetData(const S: String): Pointer; procedure SetCaseSensitive(const Value: Boolean); procedure Delete(Index: Integer); procedure SetData(const S: String; const AValue: Pointer); protected function HashOf(const Key: string): Cardinal; procedure Insert(Index: Integer; Item: PStringHashItem); public constructor Create(CaseSensitivity: boolean); destructor Destroy; override; function Add(const S: String): Integer; function Add(const S: String; ItemData: Pointer): Integer; procedure Clear; function Find(const S: String): Integer; function Remove(const S: String): Integer; property CaseSensitive: Boolean read fCaseSensitive write SetCaseSensitive; property Count: Integer read FCount; property Data[const S: String]: Pointer read GetData write SetData; default; property List: PStringHashItemList read FList; end; implementation var UpperCaseChars: array[char] of char; { TStringHashList } function TStringHashList.Add(const S: String): Integer; begin Result:=Add(S,nil); end; function TStringHashList.Add(const S: String; ItemData: Pointer): Integer; var Item: PStringHashItem; First, Last, I: Integer; Val: Cardinal; Larger: boolean; begin New(Item); Val:= HashOf(S); Item^.HashValue := Val; Item^.Key := S; Item^.Data := ItemData; if FCount > 0 then begin First:=0; Last:= FCount-1; Larger:=False; while First<=Last do begin I:=(First+Last)shr 1; Case CompareValue(Val, fList[I]^.HashValue)<=0 of True: begin Last:=I-1; Larger:=False; end; False: begin First:=I+1; Larger:=True; end; end; end; Case Larger of True: Result:=I+1; False: Result:=I; end; end else Result:=0; Insert(Result,Item); end; procedure TStringHashList.Clear; var I: Integer; begin if fCount = 0 then exit; for I:= 0 to fCount -1 do Dispose(fList[I]); if FList<>nil then begin FreeMem(FList); FList:=nil; end; fCount:= 0; end; function TStringHashList.CompareString(const Value1, Value2: String): Boolean; var I, Len: Integer; P1,P2: PChar; begin Result:= False; P1:= PChar(Value1); Len:= Length(Value1); P2:= PChar(Value2); if Len = Length(Value2) then begin Result:= True; case fCaseSensitive of True: for I:= Len -1 downto 0 do if P1[I] <> P2[I] then begin Result:= False; break; end; False: for I:= Len -1 downto 0 do if UpperCaseChars[P1[I]] <> UpperCaseChars[P2[I]] then begin Result:= False; break; end; end; end; end; function TStringHashList.CompareValue(const Value1, Value2: Cardinal): Integer; begin Result:= 0; if Value1 > Value2 then Result:= 1 else if Value1 < Value2 then Result:= -1; end; function TStringHashList.GetData(const S: String): Pointer; var i: integer; begin i:=Find(S); if i>=0 then Result:=FList[i]^.Data else Result:=nil; end; procedure TStringHashList.Delete(Index: Integer); begin if (Index >= 0) and (Index < FCount) then begin dec(FCount); if Index < FCount then System.Move(FList[Index + 1], FList[Index], (FCount - Index) * SizeOf(PStringHashItem)); end; end; procedure TStringHashList.SetData(const S: String; const AValue: Pointer); var i: integer; begin i:=Find(S); if i>=0 then FList[i]^.Data:=AValue else Add(S,AValue); end; destructor TStringHashList.Destroy; begin Clear; inherited Destroy; end; function TStringHashList.Find(const S: String): Integer; var Value: Integer; First, Last, Temp, I: Integer; begin Value:= HashOf(s); Result:= -1; First:= 0; Last:= Count -1; while First <= Last do begin Temp:= (First + Last) div 2; case CompareValue(Value, FList[Temp]^.HashValue) of 1: First:= Temp +1; 0: begin Result:= Temp; if CompareString(S, FList[Temp]^.Key) then exit else break; end; -1: Last:= Temp-1; end; end; if Result <> -1 then begin Result:= -1; First:= Temp -1; if First > 0 then while CompareValue(Value, FList[First]^.HashValue) = 0 do dec(First); inc(First); Last:= Temp +1; if Last < Count -1 then while CompareValue(Value, FList[Last]^.HashValue) = 0 do inc(Last); dec(Last); for I:= First to Last do if CompareString(S, FList[I]^.Key) then begin Result:= I; Exit; end; end; end; function TStringHashList.HashOf(const Key: string): Cardinal; var P: PChar; I, Len: Integer; begin P:= PChar(Key); Len:= Length(Key); Result := Len; // use the last 30 characters to compute the hash case fCaseSensitive of True: for I:= Len -1 downto 0 do inc(Result, cardinal(ord(P[I])) shl I); False: for I:= Len -1 downto 0 do inc(Result, cardinal(ord(UpperCaseChars[P[I]])) shl I); end; end; procedure TStringHashList.Insert(Index: Integer; Item: PStringHashItem); begin ReallocMem(FList, (fCount +1) * SizeOf(PStringHashItem)); if Index > fCount then Index:= fCount; if Index < 0 then Index:= 0; if Index < FCount then System.Move(FList[Index], FList[Index + 1], (FCount - Index) * SizeOf(PStringHashItem)); FList[Index] := Item; Inc(FCount); end; constructor TStringHashList.Create(CaseSensitivity: boolean); begin fCaseSensitive:=CaseSensitivity; inherited Create; end; function TStringHashList.Remove(const S: String): Integer; begin Result:= Find(S); if Result > -1 then begin Dispose(fList[Result]); Delete(Result); end; end; procedure TStringHashList.SetCaseSensitive(const Value: Boolean); begin if fCaseSensitive <> Value then begin if Count > 0 then begin raise EListError.Create(rsErrListMustBeEmpty); exit; end; fCaseSensitive := Value; end; end; //------------------------------------------------------------------------------ procedure InternalInit; var c: char; begin for c:=Low(char) to High(char) do begin UpperCaseChars[c]:=upcase(c); end; end; initialization InternalInit; end.