Contributor: KKTOS { THashTable unit - Delphi 1 version by kktos, May 1997. This code is FREEWARE. *** Please, if you enhance it, mail me at kktos@sirius.fr *** } unit HashTabl; interface uses Classes; type TDeleteType= (dtDelete, dtDetach); { Class THashList, from Delphi 2 TList source used internally, but you can use it for any purpose } THashItem= record key: longint; obj: TObject; end; PHashItemList = ^THashItemList; THashItemList = array[0..0] of THashItem; THashList = class(TObject) private Flist: PHashItemList; Fcount: integer; Fcapacity: integer; memSize: longint; FdeleteType: TDeleteType; protected procedure Error; function Get(Index: Integer): THashItem; procedure Grow; procedure Put(Index: Integer; const Item: THashItem); procedure SetCapacity(NewCapacity: Integer); procedure SetCount(NewCount: Integer); public constructor Create; destructor Destroy; override; function Add(const Item: THashItem): Integer; procedure Clear(dt: TDeleteType); procedure Detach(Index: Integer); procedure Delete(Index: Integer); function Expand: THashList; function IndexOf(key: longint): Integer; procedure Pack; property DeleteType: TDeleteType read FdeleteType write FdeleteType; property Capacity: Integer read FCapacity write SetCapacity; property Count: Integer read FCount write SetCount; property Items[Index: Integer]: THashItem read Get write Put; default; end; { Class THashTable the real hashtable. } THashTable= class(TObject) private Ftable: THashList; procedure Error; function getCount: integer; procedure setCount(count: integer); function getCapacity: integer; procedure setCapacity(capacity: integer); function getItem(index: integer): TObject; procedure setItem(index: integer; obj: TObject); function getDeleteType: TDeleteType; procedure setDeleteType(dt: TDeleteType); public constructor Create; destructor Destroy; override; procedure Add(const key: string; value: TObject); function Get(const key: string): TObject; procedure Detach(const key: string); procedure Delete(const key: string); procedure Clear(dt: TDeleteType); procedure Pack; property DeleteType: TDeleteType read getDeleteType write setDeleteType; property Count: integer read getCount write setCount; property Capacity: Integer read getCapacity write setCapacity; property Items[index: Integer]: TObject read getItem write setItem; property Table: THashList read Ftable; end; function hash(key: Pointer; length: longint; level: longint): longint; implementation uses SysUtils, Consts; type longArray= packed array[0..3] of byte; longArrayPtr= ^longArray; array12= packed array[0..11] of byte; array12Ptr= ^array12; longPtr= ^longint; { --- Class THashList --- brute copy of TList D2 source, with some minors changes no comment, see TList } {-----------------------------------------------------------------------------} constructor THashList.Create; begin FdeleteType:= dtDelete; FCapacity:= 0; FCount:= 0; memSize:= 4; Flist:= AllocMem(memSize); SetCapacity(100); end; {-----------------------------------------------------------------------------} destructor THashList.Destroy; begin Clear(FdeleteType); FreeMem(FList, memSize); end; {-----------------------------------------------------------------------------} function THashList.Add(const Item: THashItem): Integer; begin Result := FCount; if(Result = FCapacity) then Grow; FList^[Result].key:= Item.key; FList^[Result].obj:= Item.obj; Inc(FCount); end; {-----------------------------------------------------------------------------} procedure THashList.Clear(dt: TDeleteType); var i: integer; begin if(dt=dtDelete) then for i := FCount - 1 downto 0 do if(Items[i].obj <> nil) then Items[i].obj.Free; {FreeMem(FList, memSize); memSize:= 4; Flist:= AllocMem(memSize);} FCapacity:= 0; FCount:= 0; end; {-----------------------------------------------------------------------------} { know BC++ ? remember TArray::Detach? if not, Detach remove the item from the list without disposing the object } procedure THashList.Detach(Index: Integer); begin if((Index < 0) or (Index >= FCount)) then Error; Dec(FCount); if(Index < FCount) then System.Move(FList^[Index + 1], FList^[Index], (FCount - Index) * SizeOf(THashItem)); end; {-----------------------------------------------------------------------------} { know BC++ ? remember TArray::Destroy ? renames delete 'cause destroy... if not, Delete remove the item from the list AND dispose the object } procedure THashList.Delete(Index: Integer); begin if((Index < 0) or (Index >= FCount)) then Error; Dec(FCount); if(Index < FCount) then begin FList^[Index].obj.Free; System.Move(FList^[Index + 1], FList^[Index], (FCount - Index) * SizeOf(THashItem)); end; end; {-----------------------------------------------------------------------------} procedure THashList.Error; begin raise EListError.CreateRes(SListIndexError); end; {-----------------------------------------------------------------------------} function THashList.Expand: THashList; begin if(FCount = FCapacity) then Grow; Result:= Self; end; {-----------------------------------------------------------------------------} function THashList.Get(Index: Integer): THashItem; begin if((Index < 0) or (Index >= FCount)) then Error; Result.key:= FList^[Index].key; Result.obj:= FList^[Index].obj; end; {-----------------------------------------------------------------------------} procedure THashList.Grow; var Delta: Integer; begin if FCapacity > 8 then Delta := 16 else if FCapacity > 4 then Delta := 8 else Delta := 4; SetCapacity(FCapacity + Delta); end; {-----------------------------------------------------------------------------} function THashList.IndexOf(key: longint): Integer; begin Result := 0; while (Result < FCount) and (FList^[Result].key <> key) do Inc(Result); if Result = FCount then Result:= -1; end; {-----------------------------------------------------------------------------} procedure THashList.Put(Index: Integer; const Item: THashItem); begin if (Index < 0) or (Index >= FCount) then Error; FList^[Index].key:= Item.key; FList^[Index].obj:= Item.obj; end; {-----------------------------------------------------------------------------} procedure THashList.Pack; var i: Integer; begin for i := FCount - 1 downto 0 do if Items[i].obj = nil then Delete(i); end; {-----------------------------------------------------------------------------} procedure THashList.SetCapacity(NewCapacity: Integer); begin if((NewCapacity < FCount) or (NewCapacity > MaxListSize)) then Error; if(NewCapacity <> FCapacity) then begin FList:= ReallocMem(FList, memSize, NewCapacity * SizeOf(THashItem)); memSize:= NewCapacity * SizeOf(THashItem); FCapacity:= NewCapacity; end; end; {-----------------------------------------------------------------------------} procedure THashList.SetCount(NewCount: Integer); begin if((NewCount < 0) or (NewCount > MaxListSize)) then Error; if(NewCount > FCapacity) then SetCapacity(NewCount); if(NewCount > FCount) then FillChar(FList^[FCount], (NewCount - FCount) * SizeOf(THashItem), 0); FCount:= NewCount; end; { --- Class THashTable --- it's just a list of THashItems. you provide a key (string) and an object; a unique numeric key (longint) is compute (see hash); when you get an object, you provide string key, and as fast as possible the object is here. Really fast; Really smart, because of string keys. } {-----------------------------------------------------------------------------} constructor THashTable.Create; begin inherited Create; Ftable:= THashList.Create; end; {-----------------------------------------------------------------------------} destructor THashTable.Destroy; begin Ftable.Free; inherited Destroy; end; {-----------------------------------------------------------------------------} procedure THashTable.Error; begin raise EListError.CreateRes(SListIndexError); end; {-----------------------------------------------------------------------------} { Add 'value' object with key 'key' } procedure THashTable.Add(const key: string; value: TObject); var item: THashItem; begin item.key:= hash(pointer(longint(@key)+1),length(key),0); item.obj:= value; Ftable.Add(item); end; {-----------------------------------------------------------------------------} { Get object with key 'key' } function THashTable.Get(const key: string): TObject; var index: integer; begin index:= Ftable.IndexOf(hash(pointer(longint(@key)+1),length(key),0)); if(index<0) then Error; result:= Ftable[index].obj; end; {-----------------------------------------------------------------------------} { Detach (remove item, do not dispose object) object with key 'key' } procedure THashTable.Detach(const key: string); var index: integer; begin index:= Ftable.IndexOf(hash(pointer(longint(@key)+1),length(key),0)); if(index>=0) then Ftable.Detach(index); end; {-----------------------------------------------------------------------------} { Delete (remove item, dispose object) object with key 'key' } procedure THashTable.Delete(const key: string); var index: integer; begin index:= Ftable.IndexOf(hash(pointer(longint(@key)+1),length(key),0)); if(index>=0) then Ftable.Delete(index); end; {-----------------------------------------------------------------------------} { Clear the list; i.e: remove all the items (detach or delete depending of 'dt') } procedure THashTable.Clear(dt: TDeleteType); begin Ftable.Clear(dt); end; {-----------------------------------------------------------------------------} procedure THashTable.Pack; begin Ftable.Pack; end; {-----------------------------------------------------------------------------} function THashTable.getCount: integer; begin result:= Ftable.Count; end; procedure THashTable.setCount(count: integer); begin Ftable.Count:= count; end; function THashTable.getCapacity: integer; begin result:= Ftable.Capacity; end; procedure THashTable.setCapacity(capacity: integer); begin Ftable.Capacity:= capacity; end; function THashTable.getDeleteType: TDeleteType; begin result:= Ftable.DeleteType; end; procedure THashTable.setDeleteType(dt: TDeleteType); begin Ftable.DeleteType:= dt; end; function THashTable.getItem(index: integer): TObject; begin result:= Ftable[index].obj; end; {-----------------------------------------------------------------------------} procedure THashTable.setItem(index: integer; obj: TObject); var item: THashItem; begin item.key:= Ftable[index].key; item.obj:= obj; Ftable[index]:= item; end; {-----------------------------------------------------------------------------} { original code from lookup2.c, by Bob Jenkins, December 1996 http://ourworld.compuserve.com/homepages/bob_jenkins/ PLEASE, let me know if there is problem with it, or if you have a better one. THANKS. } function hash(key: Pointer; length: longint; level: longint): longint; var a,b,c: longint; len: longint; k: array12Ptr; lp: longPtr; begin k:= array12Ptr(key); len:= length; a:= $9E3779B9; b:= a; c:= level; if((longint(key) and 3) <> 0) then begin while(len>=12) do begin {unaligned} inc(a, (longint(k^[00]) +(longint(k^[01]) shl 8) + (longint(k^[02]) shl 16) + (longint(k^[03]) shl 24))); inc(b, (longint(k^[04]) +(longint(k^[05]) shl 8) + (longint(k^[06]) shl 16) + (longint(k^[07]) shl 24))); inc(c, (longint(k^[08]) +(longint(k^[09]) shl 8) + (longint(k^[10]) shl 16) + (longint(k^[11]) shl 24))); {mix(a,b,c);} inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 13); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 8); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 13); inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 12); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 16); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 5); inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 3); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 10); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 15); inc(longint(k),12); dec(len,12); end; end else begin while(len>=12) do begin {aligned} lp:= longPtr(k); inc(a, lp^); inc(lp,4); inc(b, lp^); inc(lp,4); inc(c, lp^); {mix(a,b,c);} inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 13); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 8); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 13); inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 12); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 16); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 5); inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 3); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 10); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 15); inc(longint(k),12); dec(len,12); end; end; inc(c,length); if(len>=11) then inc(c, (longint(k^[10]) shl 24)); if(len>=10) then inc(c, (longint(k^[9]) shl 16)); if(len>=9) then inc(c, (longint(k^[8]) shl 8)); if(len>=8) then inc(b, (longint(k^[7]) shl 24)); if(len>=7) then inc(b, (longint(k^[6]) shl 16)); if(len>=6) then inc(b, (longint(k^[5]) shl 8)); if(len>=5) then inc(b, longint(k^[4])); if(len>=4) then inc(a, (longint(k^[3]) shl 24)); if(len>=3) then inc(a, (longint(k^[2]) shl 16)); if(len>=2) then inc(a, (longint(k^[1]) shl 8)); if(len>=1) then inc(a, longint(k^[0])); {mix(a,b,c);} inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 13); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 8); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 13); inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 12); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 16); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 5); inc(a , b xor $FFFFFFFF + 1); inc(a , c xor $FFFFFFFF + 1); a:= a xor (c shr 3); inc(b , c xor $FFFFFFFF + 1); inc(b , a xor $FFFFFFFF + 1); b:= b xor (a shl 10); inc(c , a xor $FFFFFFFF + 1); inc(c , b xor $FFFFFFFF + 1); c:= c xor (b shr 15); result:= longint(c); end; end.