262 lines
6.8 KiB
Plaintext
262 lines
6.8 KiB
Plaintext
|
|
|||
|
{ Copyright (c) 1989 by Borland International, Inc. }
|
|||
|
|
|||
|
unit TCHash;
|
|||
|
{ Turbo Pascal 5.5 object-oriented example hash tables.
|
|||
|
This unit is used by TCALC.PAS.
|
|||
|
See TCALC.DOC for an more information about this example.
|
|||
|
}
|
|||
|
|
|||
|
{$S-}
|
|||
|
|
|||
|
interface
|
|||
|
|
|||
|
uses TCUtil;
|
|||
|
|
|||
|
{ This unit allows you to implement hash tables. Each hash table is composed
|
|||
|
of a number of "buckets", each of which points to a linked list of data
|
|||
|
entries. The bucket that a particular data entry goes into is determined
|
|||
|
by the HashValue function. }
|
|||
|
|
|||
|
const
|
|||
|
MaxBuckets = 1000;
|
|||
|
MaxHashItemSize = 256;
|
|||
|
|
|||
|
type
|
|||
|
BucketRange = 1..MaxBuckets;
|
|||
|
HashItemSizeRange = 1..MaxHashItemSize;
|
|||
|
HashItemData = array[0..Pred(MaxHashItemSize)] of Byte;
|
|||
|
HashItemDataPtr = ^HashItemData;
|
|||
|
HashItemPtr = ^HashItem;
|
|||
|
HashItem = record
|
|||
|
Next : HashItemPtr;
|
|||
|
Data : HashItemData;
|
|||
|
end;
|
|||
|
HashItemArray = array[BucketRange] of HashItemPtr;
|
|||
|
HashTable = object
|
|||
|
Buckets : BucketRange;
|
|||
|
Items : Longint;
|
|||
|
CurrItem : HashItemPtr;
|
|||
|
CurrBucket : BucketRange;
|
|||
|
HashData : ^HashItemArray;
|
|||
|
constructor Init(InitBuckets : BucketRange);
|
|||
|
destructor Done;
|
|||
|
function Add : Boolean;
|
|||
|
procedure Delete(Deleted : Pointer);
|
|||
|
function FirstItem : HashItemPtr;
|
|||
|
function NextItem : HashItemPtr;
|
|||
|
function Change : Boolean;
|
|||
|
function Search : HashItemPtr;
|
|||
|
function HashValue : Word; virtual;
|
|||
|
function Found(Item : HashItemPtr) : Boolean; virtual;
|
|||
|
procedure CreateItem(var Item : HashItemPtr); virtual;
|
|||
|
function ItemSize : HashItemSizeRange; virtual;
|
|||
|
function CurrItemSize(Item : HashItemPtr) : HashItemSizeRange; virtual;
|
|||
|
end;
|
|||
|
|
|||
|
implementation
|
|||
|
|
|||
|
constructor HashTable.Init(InitBuckets : BucketRange);
|
|||
|
{ Initialize a new hash table with a certain number of buckets }
|
|||
|
begin
|
|||
|
GetMem(HashData, InitBuckets * SizeOf(HashItemPtr));
|
|||
|
if HashData = nil then
|
|||
|
Fail;
|
|||
|
Buckets := InitBuckets;
|
|||
|
FillChar(HashData^, Buckets * SizeOf(HashItemPtr), 0);
|
|||
|
Items := 0;
|
|||
|
end; { HashTable.Init }
|
|||
|
|
|||
|
destructor HashTable.Done;
|
|||
|
{ Removes a hash table from memory }
|
|||
|
var
|
|||
|
P, D : HashItemPtr;
|
|||
|
Counter : Word;
|
|||
|
begin
|
|||
|
for Counter := 1 to Buckets do
|
|||
|
begin
|
|||
|
P := HashData^[Counter];
|
|||
|
while P <> nil do
|
|||
|
begin
|
|||
|
D := P;
|
|||
|
P := P^.Next;
|
|||
|
FreeMem(D, CurrItemSize(D) + SizeOf(HashItemPtr));
|
|||
|
end;
|
|||
|
end;
|
|||
|
FreeMem(HashData, Buckets * SizeOf(HashItemPtr));
|
|||
|
end; { HashTable.Done }
|
|||
|
|
|||
|
function HashTable.Add : Boolean;
|
|||
|
{ Adds a new item to a hash table }
|
|||
|
var
|
|||
|
H, A : HashItemPtr;
|
|||
|
V : BucketRange;
|
|||
|
begin
|
|||
|
Add := False;
|
|||
|
V := Succ(HashValue mod Buckets);
|
|||
|
H := HashData^[V];
|
|||
|
A := H;
|
|||
|
while H <> nil do
|
|||
|
begin
|
|||
|
H := H^.Next;
|
|||
|
if H <> nil then
|
|||
|
A := H;
|
|||
|
end;
|
|||
|
if A = nil then { Item will be the first element in the list }
|
|||
|
begin
|
|||
|
GetMem(HashData^[V], ItemSize + SizeOf(HashItemPtr));
|
|||
|
A := HashData^[V];
|
|||
|
if A = nil then
|
|||
|
Exit;
|
|||
|
end
|
|||
|
else begin { Add item and end of list }
|
|||
|
GetMem(A^.Next, ItemSize + SizeOf(HashItemPtr));
|
|||
|
if A^.Next = nil then
|
|||
|
Exit;
|
|||
|
A := A^.Next;
|
|||
|
end;
|
|||
|
CreateItem(A);
|
|||
|
A^.Next := nil;
|
|||
|
Inc(Items);
|
|||
|
Add := True;
|
|||
|
end; { HashTable.Add }
|
|||
|
|
|||
|
procedure HashTable.Delete(Deleted : Pointer);
|
|||
|
{ Deletes an item from a hash table, and returns the deleted item }
|
|||
|
var
|
|||
|
H, D : HashItemPtr;
|
|||
|
V : BucketRange;
|
|||
|
begin
|
|||
|
V := Succ(HashValue mod Buckets);
|
|||
|
H := HashData^[V];
|
|||
|
D := H;
|
|||
|
while (H <> nil) and (not Found(H)) do
|
|||
|
begin
|
|||
|
H := H^.Next;
|
|||
|
if not Found(H) then
|
|||
|
D := H;
|
|||
|
end;
|
|||
|
if H = nil then { The item was not found }
|
|||
|
begin
|
|||
|
if Deleted <> nil then
|
|||
|
FillChar(Deleted^, ItemSize, 0);
|
|||
|
Exit;
|
|||
|
end
|
|||
|
else begin
|
|||
|
if H = HashData^[V] then
|
|||
|
HashData^[V] := HashData^[V]^.Next
|
|||
|
else
|
|||
|
D^.Next := H^.Next;
|
|||
|
if Deleted <> nil then { Fill Deleted with the item's data }
|
|||
|
Move(H^.Data, Deleted^, ItemSize);
|
|||
|
FreeMem(H, CurrItemSize(H) + SizeOf(HashItemPtr));
|
|||
|
end;
|
|||
|
Dec(Items);
|
|||
|
end; { HashTable.Delete }
|
|||
|
|
|||
|
function HashTable.FirstItem : HashItemPtr;
|
|||
|
{ Returns the first item in a hash table. to find all of the items in a
|
|||
|
hash table, call FirstItem to get the first one and then call NextItem to
|
|||
|
get the rest }
|
|||
|
var
|
|||
|
Counter : Word;
|
|||
|
begin
|
|||
|
for Counter := 1 to Buckets do
|
|||
|
begin
|
|||
|
CurrBucket := Counter;
|
|||
|
CurrItem := HashData^[Counter];
|
|||
|
if CurrItem <> nil then
|
|||
|
begin
|
|||
|
FirstItem := CurrItem;
|
|||
|
Exit;
|
|||
|
end;
|
|||
|
end;
|
|||
|
FirstItem := nil;
|
|||
|
end; { HashTable.FirstItem }
|
|||
|
|
|||
|
function HashTable.NextItem : HashItemPtr;
|
|||
|
{ Returns the next item in a hash table - called after FirstItem }
|
|||
|
begin
|
|||
|
CurrItem := CurrItem^.Next;
|
|||
|
if CurrItem <> nil then
|
|||
|
begin
|
|||
|
NextItem := CurrItem;
|
|||
|
Exit;
|
|||
|
end;
|
|||
|
while CurrBucket < Buckets do
|
|||
|
begin
|
|||
|
Inc(CurrBucket);
|
|||
|
CurrItem := HashData^[CurrBucket];
|
|||
|
if CurrItem <> nil then
|
|||
|
begin
|
|||
|
NextItem := CurrItem;
|
|||
|
Exit;
|
|||
|
end;
|
|||
|
end;
|
|||
|
NextItem := nil;
|
|||
|
end; { HashTable.NextItem }
|
|||
|
|
|||
|
function HashTable.Change : Boolean;
|
|||
|
{ Changes the data of a hash item }
|
|||
|
var
|
|||
|
H : HashItemPtr;
|
|||
|
begin
|
|||
|
H := HashData^[Succ(HashValue mod Buckets)];
|
|||
|
while (H <> nil) and (not Found(H)) do
|
|||
|
H := H^.Next;
|
|||
|
if H <> nil then
|
|||
|
begin
|
|||
|
CreateItem(H);
|
|||
|
Change := True;
|
|||
|
end
|
|||
|
else
|
|||
|
Change := Add;
|
|||
|
end; { HashTable.Change }
|
|||
|
|
|||
|
function HashTable.Search : HashItemPtr;
|
|||
|
{ Searches for a particular hash item }
|
|||
|
var
|
|||
|
H : HashItemPtr;
|
|||
|
begin
|
|||
|
H := HashData^[Succ(HashValue mod Buckets)];
|
|||
|
while (H <> nil) and (not Found(H)) do
|
|||
|
H := H^.Next;
|
|||
|
Search := H;
|
|||
|
end; { HashTable.Search }
|
|||
|
|
|||
|
function HashTable.HashValue : Word;
|
|||
|
{ Returns a hash value - must be written by the user }
|
|||
|
begin
|
|||
|
Abstract('HashTable.HashValue');
|
|||
|
end; { HashTable.HashValue }
|
|||
|
|
|||
|
function HashTable.Found(Item : HashItemPtr) : Boolean;
|
|||
|
{ Returns a boolean value indicating whether the current hash item is the
|
|||
|
one being searched for - must be written by the user }
|
|||
|
begin
|
|||
|
Abstract('HashTable.Found');
|
|||
|
end; { HashTable.Found }
|
|||
|
|
|||
|
procedure HashTable.CreateItem(var Item : HashItemPtr);
|
|||
|
{ Creates a hash item - must be written by the user }
|
|||
|
begin
|
|||
|
Abstract('HashTable.CreateItem');
|
|||
|
end; { HashTable.CreateItem }
|
|||
|
|
|||
|
function HashTable.ItemSize : HashItemSizeRange;
|
|||
|
{ Returns the size of a hash item. If the hash item size is variable, this
|
|||
|
is based on whatever the item being searched for, added, or deleted is -
|
|||
|
must be written by the user }
|
|||
|
begin
|
|||
|
Abstract('HashTable.ItemSize');
|
|||
|
end; { HashTable.ItemSize }
|
|||
|
|
|||
|
function HashTable.CurrItemSize(Item : HashItemPtr) : HashItemSizeRange;
|
|||
|
{ Returns the size of a particular item. This needs to be written only if
|
|||
|
the size of hash items is variable (strings, etc.) }
|
|||
|
begin
|
|||
|
CurrItemSize := ItemSize;
|
|||
|
end; { HashTable.CurrItemSize }
|
|||
|
|
|||
|
end.
|
|||
|
|