| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- {
- Double Commander
- -------------------------------------------------------------------------
- Modified version of StringHashList unit with UTF-8 support
- Copyright (C) 2019 Alexander Koblov ([email protected])
- This file is based on stringhashlist.pas from the LazUtils package
- See the file COPYING.modifiedLGPL.txt, 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.
- }
- unit DCStringHashListUtf8;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils,
- // LazUtils
- LazUtilsStrConsts;
- type
- PStringHashItem = ^TStringHashItem;
- TStringHashItem = record
- HashValue: Cardinal;
- Key: String;
- Data: Pointer;
- end;
- PStringHashItemList = ^PStringHashItem;
- { TStringHashListUtf8 }
- TStringHashListUtf8 = class(TObject)
- private
- FList: PStringHashItemList;
- FCount: Integer;
- fNormalize: Boolean;
- fCaseSensitive: Boolean;
- function BinarySearch(HashValue: Cardinal): Integer;
- function CompareString(const Low, Key: String): Boolean;
- function CompareValue(const Value1, Value2: Cardinal): Integer;
- procedure FindHashBoundaries(HashValue: Cardinal; StartFrom: Integer; out First, Last: Integer);
- function GetData(const S: String): Pointer;
- procedure SetNormalize(AValue: Boolean);
- 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;
- procedure Remove(Index: Integer);
- function Find(const S: String): Integer;
- function Find(const S: String; Data: Pointer): Integer;
- function Remove(const S: String): Integer;
- function Remove(const S: String; Data: Pointer): Integer;
- procedure FindBoundaries(StartFrom: Integer; out First, Last: Integer);
- property Normalize: Boolean read fNormalize write SetNormalize;
- 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
- uses
- LazUTF8, DCOSUtils;
- { TStringHashListUtf8 }
- function TStringHashListUtf8.Add(const S: String): Integer;
- begin
- Result:=Add(S,nil);
- end;
- function TStringHashListUtf8.Add(const S: String; ItemData: Pointer): Integer;
- var
- Text: String;
- Item: PStringHashItem;
- First, Last, I: Integer;
- Val: Cardinal;
- Larger: boolean;
- begin
- if fCaseSensitive then
- Text := S
- else begin
- Text:= UTF8LowerCase(S);
- end;
- if fNormalize then
- begin
- Text:= NormalizeFileName(Text);
- end;
- New(Item);
- Val:= HashOf(Text);
- 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;
- function TStringHashListUtf8.BinarySearch(HashValue: Cardinal): Integer;
- var
- First, Last, Temp: Integer;
- begin
- Result:= -1;
- First:= 0;
- Last:= Count -1;
- while First <= Last do
- begin
- Temp:= (First + Last) div 2;
- case CompareValue(HashValue, FList[Temp]^.HashValue) of
- 1: First:= Temp + 1;
- 0: exit(Temp);
- -1: Last:= Temp-1;
- end;
- end;
- end;
- procedure TStringHashListUtf8.Clear;
- var
- I: Integer;
- begin
- for I:= 0 to fCount -1 do
- Dispose(fList[I]);
- if FList<>nil then begin
- FreeMem(FList);
- FList:=nil;
- end;
- fCount:= 0;
- end;
- procedure TStringHashListUtf8.Remove(Index: Integer);
- begin
- if (Index >= 0) and (Index < FCount) then
- begin
- Dispose(fList[Index]);
- Delete(Index);
- end;
- end;
- function TStringHashListUtf8.CompareString(const Low, Key: String): Boolean;
- var
- P: Pointer;
- Len: Integer;
- LKey: String;
- begin
- P:= Pointer(Low);
- Len:= Length(Low);
- if not fNormalize then
- begin
- LKey:= Key;
- end
- else begin
- LKey:= NormalizeFileName(Key);
- end;
- if fCaseSensitive then
- begin
- Result:= (Len = Length(LKey));
- if Result then Result:= (CompareByte(P^, Pointer(LKey)^, Len) = 0);
- end
- else begin
- LKey:= UTF8LowerCase(LKey);
- Result:= (Len = Length(LKey));
- if Result then Result:= (CompareByte(P^, Pointer(LKey)^, Len) = 0);
- end;
- end;
- function TStringHashListUtf8.CompareValue(const Value1, Value2: Cardinal): Integer;
- begin
- Result:= 0;
- if Value1 > Value2 then
- Result:= 1
- else if Value1 < Value2 then
- Result:= -1;
- end;
- function TStringHashListUtf8.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 TStringHashListUtf8.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 TStringHashListUtf8.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;
- procedure TStringHashListUtf8.SetNormalize(AValue: Boolean);
- begin
- if fNormalize <> AValue then
- begin
- if Count > 0 then
- begin
- raise EListError.Create(lrsListMustBeEmpty);
- end;
- fNormalize := AValue;
- end;
- end;
- destructor TStringHashListUtf8.Destroy;
- begin
- Clear;
- inherited Destroy;
- end;
- function TStringHashListUtf8.Find(const S: String): Integer;
- var
- Text: String;
- Value: Cardinal;
- First, Last, I: Integer;
- begin
- if fCaseSensitive then
- Text := S
- else begin
- Text:= UTF8LowerCase(S);
- end;
- if fNormalize then
- begin
- Text:= NormalizeFileName(Text);
- end;
- Value:= HashOf(Text);
- Result:= BinarySearch(Value);
- if (Result <> -1) and not CompareString(Text, FList[Result]^.Key) then
- begin
- FindHashBoundaries(Value, Result, First, Last);
- Result:= -1;
- for I := First to Last do
- if CompareString(Text, FList[I]^.Key) then
- begin
- Result:= I;
- Exit;
- end;
- end;
- end;
- function TStringHashListUtf8.Find(const S: String; Data: Pointer): Integer;
- var
- Text: String;
- Value: Cardinal;
- First, Last, I: Integer;
- begin
- if fCaseSensitive then
- Text := S
- else begin
- Text:= UTF8LowerCase(S);
- end;
- if fNormalize then
- begin
- Text:= NormalizeFileName(Text);
- end;
- Value:= HashOf(Text);
- Result:= BinarySearch(Value);
- if (Result <> -1) and
- not (CompareString(Text, FList[Result]^.Key) and (FList[Result]^.Data = Data)) then
- begin
- FindHashBoundaries(Value, Result, First, Last);
- Result:= -1;
- for I := First to Last do
- if CompareString(Text, FList[I]^.Key) and (FList[I]^.Data = Data) then
- begin
- Result:= I;
- Exit;
- end;
- end;
- end;
- procedure TStringHashListUtf8.FindHashBoundaries(HashValue: Cardinal; StartFrom: Integer; out First, Last: Integer);
- begin
- First:= StartFrom -1;
- //Find first matching hash index
- while (First >= 0) and (CompareValue(HashValue, FList[First]^.HashValue) = 0) do
- dec(First);
- if (First < 0) or ((CompareValue(HashValue, FList[First]^.HashValue) <> 0)) then
- inc(First);
- //Find the last matching hash index
- Last:= StartFrom +1;
- while (Last <= (FCount - 1)) and (CompareValue(HashValue, FList[Last]^.HashValue) = 0) do
- inc(Last);
- if (Last > (FCount - 1)) or (CompareValue(HashValue, FList[Last]^.HashValue) <> 0) then
- dec(Last);
- end;
- function TStringHashListUtf8.HashOf(const Key: string): Cardinal;
- var
- P: PAnsiChar;
- I, Len: Integer;
- begin
- P:= PAnsiChar(Key);
- Len:= Length(Key);
- Result := Len;
- {$PUSH}{$R-}{$Q-} // no range, no overflow checks
- for I := Len - 1 downto 0 do
- Inc(Result, Cardinal(Ord(P[I])) shl I);
- {$POP}
- end;
- procedure TStringHashListUtf8.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 TStringHashListUtf8.Create(CaseSensitivity: boolean);
- begin
- fNormalize:= FileNameNormalized;
- fCaseSensitive:= CaseSensitivity;
- inherited Create;
- end;
- function TStringHashListUtf8.Remove(const S: String): Integer;
- begin
- Result:= Find(S);
- if Result > -1 then
- begin
- Dispose(fList[Result]);
- Delete(Result);
- end;
- end;
- function TStringHashListUtf8.Remove(const S: String; Data: Pointer): Integer;
- begin
- Result:= Find(S, Data);
- if Result > -1 then
- begin
- Dispose(fList[Result]);
- Delete(Result);
- end;
- end;
- procedure TStringHashListUtf8.FindBoundaries(StartFrom: Integer; out First,
- Last: Integer);
- begin
- FindHashBoundaries(FList[StartFrom]^.HashValue, StartFrom, First, Last);
- end;
- procedure TStringHashListUtf8.SetCaseSensitive(const Value: Boolean);
- begin
- if fCaseSensitive <> Value then
- begin
- if Count > 0 then
- begin
- raise EListError.Create(lrsListMustBeEmpty);
- exit;
- end;
- fCaseSensitive := Value;
- end;
- end;
- end.
|