Unit HashStr;

INTERFACE

TYPE
 PString = ^ShortString;

 PTHashNode = ^THashNode;
 THashNode = Record
  hash            :LongInt;
  key             :PString;
  data            :PString;
  next            :PTHashNode;
 end;

 PTHashNodeArray = ^THashNodeArray;
 THashNodeArray = Array[0..0] of PTHashNode;

 POHashStr = ^OHashStr;
 OHashStr = Object
  Constructor  Create(const Buckets: Integer);

  Procedure    Add(const key,s: ShortString);
{  Procedure    Del(const s: String);}
  Procedure    DelBucket(const Bucket: Integer);
  Function     Get(const key: ShortString): PString;
  Function     HashString(s: ShortString): LongInt; Virtual;
  Function     BucketForHash(const Hash: LongInt): Integer;
  Function     GetBucketCount: LongInt;
  Function     GetBucketLoad(const Bucket: Integer): LongInt;

  Destructor   Destroy;

  private
   Function     GetAndSetup(const key,data: ShortString;var thehash: LongInt;var thebucket: Integer): PString;

  private
   HashList           :PTHashNodeArray;
   BucketCnt          :Word;

   ABucket            :Integer;
   AHash              :LongInt;     { local }

   CurrNode,
   ANode              :PTHashNode;
 end;

IMPLEMENTATION
Uses CRC32;

Constructor OHashStr.Create(const Buckets: Integer);
var
 i              :Integer;
begin
 BucketCnt:=Buckets;
 GetMem(HashList,Buckets*SizeOf(PTHashNodeArray));
 for i:=0 to BucketCnt-1 do HashList^[i]:=nil; { mark all lists as empty }
end;

Function    OHashStr.GetBucketCount: LongInt;
begin
 GetBucketCount:=BucketCnt;
end;

Function    OHashStr.GetBucketLoad(const Bucket: Integer): LongInt;
var
 i              :LongInt;
begin
 i:=0;
 if (Bucket>0) and (Bucket<BucketCnt) then
 begin
  CurrNode:=HashList^[Bucket];
  While CurrNode<>nil do
  begin
   Inc(i);
   CurrNode:=CurrNode^.next;
  end;
 end;
 GetBucketLoad:=i;
end;

Procedure   OHashStr.Add(const key,s: ShortString);
begin
 if GetAndSetup(key,s,AHash,ABucket)<>nil then exit;

 { make new node to enter into table }
 New(ANode);
 GetMem(ANode^.key,Length(key)+1);
 GetMem(ANode^.data,Length(s)+1);
 ANode^.hash:=AHash;
 ANode^.key^:=key;
 ANode^.data^:=s;
 ANode^.next:=nil;

 CurrNode:=HashList^[ABucket];
 if CurrNode=nil then HashList^[ABucket]:=ANode else
 begin
  While CurrNode^.next<>nil do CurrNode:=CurrNode^.next;
  CurrNode^.next:=ANode;
 end;

end;

{Procedure   OHashStr.Del(const s: String);
begin
 AHash:=HashString(s);
end;}

Procedure   OHashStr.DelBucket(const Bucket: Integer);
begin
 CurrNode:=HashList^[Bucket];
 While CurrNode<>nil do
 begin
  ANode:=CurrNode^.next; { next node }
  FreeMem(CurrNode^.Key,Length(CurrNode^.Key^)+1);
  FreeMem(CurrNode^.Data,Length(CurrNode^.Data^)+1);
  Dispose(CurrNode);
  CurrNode:=ANode;
 end;
 HashList^[Bucket]:=nil;
end;


Function    OHashStr.Get(const key: ShortString): PString;
begin
 Get:=nil;
 AHash:=HashString(key);
 ABucket:=BucketForHash(AHash);
 ANode:=HashList^[ABucket];
 While ANode<>nil do
 begin
  if (ANode^.Hash=AHash) and (ANode^.key^=key) then
  begin
   Get:=ANode^.data;
   Break;
  end;
  ANode:=ANode^.next;
 end;
end;

Function    OHashStr.GetAndSetup(const key,data: ShortString;var thehash: LongInt;var thebucket: Integer): PString;
begin
 GetAndSetup:=nil;
 thehash:=HashString(key);
 thebucket:=BucketForHash(thehash);
 ANode:=HashList^[thebucket];
 While ANode<>nil do
 begin
  if (ANode^.Hash=thehash) and (ANode^.key^=key) then
  begin
   GetAndSetup:=ANode^.data;
   Break;
  end;
  ANode:=ANode^.next;
 end;
end;

Function    OHashStr.HashString(s: ShortString): LongInt;
begin
 HashString:=CRC32String(s);
end;

Function    OHashStr.BucketForHash(const Hash: LongInt): Integer;
begin
 BucketForHash:=(Hash and $7FFFFFFF) mod BucketCnt;
end;

Destructor  OHashStr.Destroy;
var
 i              :Integer;
begin
 for i:=0 to BucketCnt-1 do DelBucket(i);
 FreeMem(HashList,BucketCnt*SizeOf(Pointer));
 HashList:=nil;
end;

END.

