lazarus-ccr/components/flashfiler/sourcelaz/ffhash.pas
2016-12-07 13:31:59 +00:00

986 lines
30 KiB
ObjectPascal

{*********************************************************}
{* FlashFiler: Hash table & calculation routines *}
{*********************************************************}
(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TurboPower FlashFiler
*
* The Initial Developer of the Original Code is
* TurboPower Software
*
* Portions created by the Initial Developer are Copyright (C) 1996-2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** *)
{$I ffdefine.inc}
{.$DEFINE CompileDebugCode}
unit ffhash;
interface
uses
SysUtils,
ffllbase;
type
{ forward declarations }
TffBaseHashTable = class;
TffHashIteratorFunction = procedure(aKey : longInt; aData : pointer;
const cookie1, cookie2, cookie3 : TffWord32) of object;
{ Used by TffHash.Iterate. Called for each item in the hash
table. }
TffHash64IteratorFunction = procedure(aKey : TffInt64; aData : pointer;
const cookie1, cookie2, cookie3 : TffWord32) of object;
{ Used by TffHash64.Iterate. Called for each item in the hash
table. }
{ This type defines the kind of procedure called when the data associated
with a hash table entry must be freed by the owning object. }
TffDisposeDataProc = procedure(Sender : TffBaseHashTable; AData : Pointer) of object;
{ This class is used to store key/value pairs within the hash table. }
{ Assumption: The TffHashNode.ExtraData property is not used for
any other purpose. }
TffHashNode = class(TffObject)
protected
fhKey : Pointer;
fhNext : TffHashNode; { The next node in this hash table slot. }
fhValue : Pointer;
public
ExtraData : pointer;
end;
{ This class is a simple hash table implementation. It assumes the
key values will be long integers and the associated data will be a
pointer. It assumes the owning object will properly destroy the
data associated with each hash table entry by assigning a disposal
function to the OnDispose property of this class.
This implementation is thread-safe.
}
TffBaseHashTable = class(TffObject)
protected {private}
FAtMin : boolean;
FCanShrink : boolean;
FCount : Integer;
FHashSizeIndex : integer;
FMinSizeIndex : Integer;
FOnDisposeData : TffDisposeDataProc;
FTable : TffPointerList;
protected
function fhAddPrim(aKey : Pointer;
aValue : Pointer) : Boolean;
{-Use this method to add an entry to the hash table. Returns True if
the key/value pair was added or False if the key is already in the
hash table. }
function fhCompareKey(const aKey1 : Pointer;
const aKey2 : Pointer) : Boolean; virtual;
function fhCreateNode: TffHashNode; virtual;
procedure fhDeletePrim(const AKey : Pointer;
const AInx : Integer);
{-This method is used to delete an entry in the hash table. aInx
must specify the exact slot within the table containing the entry.
This method will then run through the associated entry list and
locate the exact hash node using aKey. }
function fhFindPrim(const AKey : Pointer;
var AInx : Integer;
var ANode : TffHashNode) : Boolean;
{-This method is used to find an entry within the hash table.
It fills aInx with the index of the key within the hash table and
aNode with a pointer to the hash node storing the entry. }
procedure fhFreeKeyPrim(aKey : pointer); virtual; abstract;
{-Use this method to free a key created for a TffHashNode.
Called from fhDeletePrim. }
function fhGetIndex(const AKey : Pointer;
const ACount : Integer) : Integer; virtual; abstract;
{calculate the index, ie hash, of the key}
function fhMoveNodePrim(OldTable : TffPointerList;
OldNodeInx : integer;
Node : TffHashNode): Boolean;
{-Used by fhResizeTable to move a node from an old table to the new,
resized table. Assumption: Resized table has enough room to hold
the new node. }
procedure fhResizeTable(const increase : boolean); virtual;
{-Resize the table. If you want the table to increase to the next
level of capacity, set increase to True. If you want the table
to decrease to the next level of capacity, set increase to False. }
public
constructor Create(initialSizeIndex : integer); virtual;
{-This method creates and initializes the hash table. initialSizeIndex
specifies the index of array ffc_HashSizes that is to specify the
initial number of slots within the hash table. }
destructor Destroy; override;
procedure Clear;
{-Use this method to clear the hash table. The OnDisposeData event is
raised for each entry in case the caller needs to free the data
associated with the entry.}
property CanShrink : boolean read FCanShrink write FCanShrink;
{-Use this property to indicate whether or not the hash table may
be reduced in size when the number of items is less than 1/6 the
number of slots. }
property Count : Integer read FCount;
{-Use this property to determine the number of entries in the hash
table. }
property OnDisposeData : TffDisposeDataProc
read FOnDisposeData write FOnDisposeData;
{-This event is raised when data associated with an entry must be
destroyed by the calling object. }
end;
TffHash = class(TffBaseHashTable)
protected
procedure fhFreeKeyPrim(aKey : pointer); override;
function fhGetIndex(const AKey : Pointer;
const ACount : Integer) : Integer; override;
{calculate the index, ie hash, of the key}
public
function Add(aKey : Longint;
aValue : Pointer) : Boolean;
{-Use this method to add an entry to the hash table. Returns True if
the key/value pair was added or False if the key is already in the
hash table. }
function Get(const AKey : Longint) : Pointer;
{-Use this method to find an entry in the hash table. }
procedure Iterate(const CallBack : TffHashIteratorFunction;
const cookie1, cookie2, cookie3 : longInt);
{-Use this method to iterate through the entries in the hash table.
Callback will be called once for each entry. }
procedure IterateSafely(const CallBack : TffHashIteratorFunction;
const cookie1, cookie2, cookie3 : longInt);
{-Use this method to iterate through the entries in the hash table.
It is safe in the sense that it allows the Callback function to
free the item that is the current subject of the iteration.
Callback will be called once for each entry. }
function Remove(const AKey : Longint) : Boolean; {!!.02}
{-Use this method to remove an entry from the hash table. The
OnDisposeData event is raised in case the caller needs to free the
data associated with the entry. }
{$IFDEF CompileDebugCode}
procedure DebugPrint(const AFileName : string);
{-Use this method to dump the contents of the hash table during
testing stage. }
{$ENDIF}
end;
TffHash64 = class(TffBaseHashTable)
protected
function fhCompareKey(const aKey1 : Pointer;
const aKey2 : Pointer) : Boolean; override;
procedure fhFreeKeyPrim(aKey : pointer); override;
function fhGetIndex(const AKey : Pointer;
const ACount : Integer) : Integer; override;
{calculate the index, ie hash, of the key}
public
function Add(const AKey : TffInt64;
AValue : Pointer) : Boolean;
{-Use this method to add an entry to the hash table. Returns True if
the key/value pair was added or False if the key is already in the
hash table. }
function Get(const AKey : TffInt64) : Pointer;
{-Use this method to find an entry in the hash table. }
procedure Iterate(const CallBack : TffHash64IteratorFunction;
const cookie1, cookie2, cookie3 : longInt);
{-Use this method to iterate through the entries in the hash table.
Callback will be called once for each entry. }
procedure IterateSafely(const CallBack : TffHash64IteratorFunction;
const cookie1, cookie2, cookie3 : longInt);
{-Use this method to iterate through the entries in the hash table.
It is safe in the sense that it allows the Callback function to
free the item that is the current subject of the iteration.
Callback will be called once for each entry. }
procedure Remove(const AKey : TffInt64);
{-Use this method to remove an entry from the hash table. The
OnDisposeData event is raised in case the caller needs to free the
data associated with the entry. }
{$IFDEF CompileDebugCode}
procedure DebugPrint(const AFileName : string);
{-Use this method to dump the contents of the hash table during
testing stage. }
{$ENDIF}
end;
{ This class is a threadsafe version of TffHash. This class allows multiple
threads to have read access or one thread to have write access (i.e.,
multiple read, exclusive write). A thread is granted write access only if
there are no reading threads or writing threads.}
TffThreadHash = class(TffHash)
protected {private}
FPortal : TffReadWritePortal;
public
constructor Create(initialSizeIndex : Integer); override;
destructor Destroy; override;
function BeginRead : TffThreadHash;
{-A thread must call this method to gain read access to the list.
Returns the instance of TffThreadList as a convenience. }
function BeginWrite : TffThreadHash;
{-A thread must call this method to gain write access to the list.
Returns the instance of TffThreadList as a convenience.}
procedure EndRead;
{-A thread must call this method when it no longer needs read access
to the list. If it does not call this method, all writers will
be perpetually blocked. }
procedure EndWrite;
{-A thread must call this method when it no longer needs write access
to the list. If it does not call this method, all readers and writers
will be perpetualy blocked. }
end;
TffThreadHash64 = class(TffHash64)
protected {private}
FPortal : TffReadWritePortal;
public
constructor Create(initialSizeIndex : Integer); override;
destructor Destroy; override;
function BeginRead : TffThreadHash64;
{-A thread must call this method to gain read access to the list.
Returns the instance of TffThreadList as a convenience. }
function BeginWrite : TffThreadHash64;
{-A thread must call this method to gain write access to the list.
Returns the instance of TffThreadList as a convenience.}
procedure EndRead;
{-A thread must call this method when it no longer needs read access
to the list. If it does not call this method, all writers will
be perpetually blocked. }
procedure EndWrite;
{-A thread must call this method when it no longer needs write access
to the list. If it does not call this method, all readers and writers
will be perpetualy blocked. }
end;
{The following algorithm is the UNIX ELF format hash. The code was
converted and adapted from the one in C published in Dr Dobbs
Journal, April 1996, in the article "Hashing Rehashed" by
Andrew Binstock.}
function FFCalcELFHash(const Buffer; BufSize : Integer) : TffWord32;
function FFCalcShStrELFHash(const S : TffShStr) : TffWord32;
const
{ The following constants represent indexes into ffc_HashSizes array
declared in the implementation section of this unit. Use these constants
to specify the initial size index for hash tables. }
ffc_Size59 = 0;
ffc_Size127 = 1;
ffc_Size257 = 2;
ffc_Size521 = 3;
ffc_Size1049 = 4;
ffc_Size2099 = 5;
implementation
{ The following array contains the legal hash table sizes. Each is a prime
number which allows for better spread of inserts within a hash table. }
const
ffc_HashSizes : array[0..15] of integer =
( 59, 127, 257, 521, 1049, 2099, 4201, 8419,
16843, 33703, 67409, 134837, 269683, 539389, 1078787, 2157587);
const
ffc_HashLoadFactor = 4;
{ When storing integer-ish items in a hash table, the hash table can
quickly walk through a slot's chain of nodes in those cases where a slot
contains more than one item. As a result, we can load up the hash
table with more items than slots. This constant specifies how far the
table may be overloaded. The table won't be resized until this limit
is reached. The limit is defined as Number of Slots * Load Factor. }
{===TffBaseHashTable=================================================}
constructor TffBaseHashTable.Create(initialSizeIndex : integer);
begin
inherited Create;
FAtMin := False;
FCount := 0;
if initialSizeIndex > high(ffc_HashSizes) then
initialSizeIndex := high(ffc_HashSizes);
FHashSizeIndex := initialSizeIndex;
FMinSizeIndex := FHashSizeIndex;
FOnDisposeData := nil;
FTable := TffPointerList.Create;
FTable.Count := ffc_HashSizes[FHashSizeIndex];
end;
{--------}
function TffBaseHashTable.fhCreateNode: TffHashNode;
begin
Result := TffHashNode.Create;
end;
{--------}
procedure TffBaseHashTable.Clear;
var
i : integer;
Node : TffHashNode;
Temp : TffHashNode;
begin
for i := 0 to pred(FTable.Count) do begin
Node := TffHashNode(FTable[i]);
while assigned(Node) do begin
Temp := Node;
Node := Node.fhNext;
if assigned(FOnDisposeData) then
FOnDisposeData(Self,Temp.fhValue);
{Temp.fhValue := nil;}
fhFreeKeyPrim(Temp.fhKey); {!!.01}
Temp.Free;
end;
FTable[i] := nil;
end;
FCount := 0;
end;
{--------}
destructor TffBaseHashTable.Destroy;
begin
Clear;
FTable.Free;
inherited Destroy;
end;
{--------}
function TffBaseHashTable.fhAddPrim(aKey : Pointer;
aValue : Pointer): Boolean;
var
Inx : integer;
Node : TffHashNode;
begin
if fhFindPrim(aKey, Inx, Node) then
Result := false
else begin
Result := true;
Node := fhCreateNode;
Node.fhNext := TffHashNode(FTable[Inx]);
Node.fhKey := aKey;
Node.fhValue := aValue;
FTable.List[Inx] := Node;
inc(FCount);
{ Expand the table if we've reached our load limit. }
if (FCount > (FTable.Count * ffc_HashLoadFactor)) then
fhResizeTable(True);
end;
end;
{--------}
function TffBaseHashTable.fhCompareKey(const aKey1 : Pointer;
const aKey2 : Pointer) : Boolean;
begin
Result := aKey1 = aKey2;
end;
{--------}
procedure TffBaseHashTable.fhDeletePrim(const aKey : Pointer;
const aInx : Integer);
var
Node : TffHashNode;
NextNode : TffHashNode;
PrevNode : TffHashNode;
begin
Node := TffHashNode(FTable.List[aInx]);
PrevNode := nil;
while assigned(Node) and (not fhCompareKey(Node.fhKey, AKey)) do begin
PrevNode := Node;
Node := Node.fhNext;
end;
if assigned(Node) then begin
if assigned(FOnDisposeData) then
FOnDisposeData(Self, Node.fhValue);
NextNode := Node.fhNext;
{Node.fhValue := nil;}
fhFreeKeyPrim(Node.fhKey);
Node.Free;
if assigned(PrevNode) then
PrevNode.fhNext := NextNode
else if assigned(NextNode) then
FTable.List[aInx] := NextNode
else
FTable.List[aInx] := nil;
end;
dec(FCount);
end;
{--------}
function TffBaseHashTable.fhFindPrim(const AKey : Pointer;
var AInx : Integer;
var ANode : TffHashNode): Boolean;
var
Node : TffHashNode;
begin
{assume we won't find aKey}
Result := false;
aNode := nil;
{calculate the index, ie hash, of the key}
aInx := fhGetIndex(aKey, FTable.Count);
{traverse the linked list at this entry, looking for the key in each
node we encounter--a case-sensitive comparison}
Node := TffHashNode(FTable[aInx]);
while (Node <> nil) do begin
if fhCompareKey(AKey, Node.fhKey) then begin
Result := true;
aNode := Node;
Exit;
end;
Node := Node.fhNext;
end;
end;
{--------}
function TffBaseHashTable.fhMoveNodePrim(OldTable : TffPointerList;
OldNodeInx : integer;
Node : TffHashNode): Boolean;
var
Inx : integer;
NextNode : TffHashNode;
PrevNode : TffHashNode;
TmpNode : TffHashNode;
begin
{ Assumption: The node will not be found in the table because we are only
being called during a resize. }
{ Assumption: Table does not need to be expanded since this method is
called during table expansion. }
{ Remove the node from the old table. }
TmpNode := TffHashNode(OldTable[OldNodeInx]);
PrevNode := nil;
while assigned(TmpNode) and
(not fhCompareKey(TmpNode.fhKey, Node.fhKey)) do begin
PrevNode := TmpNode;
TmpNode := TmpNode.fhNext;
end;
if assigned(TmpNode) then begin
NextNode := TmpNode.fhNext;
if assigned(PrevNode) then
PrevNode.fhNext := NextNode
else if assigned(NextNode) then
OldTable.List[OldNodeInx] := NextNode
else
OldTable.List[OldNodeInx] := nil;
end;
{ Calculate the index, ie hash, of the key. }
Inx := fhGetIndex(Node.fhKey, FTable.Count);
{ Insert the node into the new table. }
Result := true;
Node.fhNext := TffHashNode(FTable[Inx]);
FTable.List[Inx] := Node;
end;
{--------}
procedure TffBaseHashTable.fhResizeTable(const increase : boolean);
var
OldTable : TffPointerList;
Count : Integer;
Node : TffHashNode;
NewSize : Integer;
begin
FAtMin := False;
{ Are we increasing or decreasing? }
if increase then begin
{ Increasing. Have we reached the limits of the ffc_HashSizes array? }
if FHashSizeIndex = high(ffc_HashSizes) then begin
{ Yes. Double the current size and add one. If divisible by 3 then
add 2. }
NewSize := (FTable.Count * 2) + 1;
if NewSize mod 3 = 0 then
inc(NewSize, 2);
end
else begin
{ No. Move to the next size. }
inc(FHashSizeIndex);
NewSize := ffc_HashSizes[FHashSizeIndex];
end;
end
else begin
{ Decreasing. Have we reached our lower limit? }
FAtMin := (FHashSizeIndex = FMinSizeIndex);
if FAtMin then
exit
else begin
dec(FHashSizeIndex);
NewSize := ffc_HashSizes[FHashSizeIndex];
end;
end;
{ Expand the table. }
OldTable := FTable;
FTable := TffPointerList.Create;
FTable.Count := NewSize;
for Count := 0 to Pred(OldTable.Count) do begin
Node := TffHashNode(OldTable.List[Count]);
repeat
if Assigned(Node) then
fhMoveNodePrim(OldTable, Count, Node);
Node := TffHashNode(OldTable.List[Count]);
until (not assigned(Node));
end;
OldTable.Free;
end;
{====================================================================}
{===TffHash==========================================================}
function TffHash.Add(aKey : LongInt;
aValue : Pointer): Boolean;
begin
Result := fhAddPrim(pointer(aKey), aValue);
end;
{--------}
{$IFDEF CompileDebugCode}
procedure TffHash.DebugPrint(const AFileName: string);
var
F : text;
i : integer;
Node : TffHashNode;
begin
System.Assign(F, aFileName);
System.Rewrite(F);
for i := 0 to pred(FTable.Count) do begin
writeln(F, '---', i, '---');
Node := TffHashNode(FTable[i]);
while assigned(Node) do begin
writeln(F, Longint(Node.fhKey):10, intToStr(longInt(Node.fhValue)):20);
Node := Node.fhNext;
end;
end;
writeln(F);
writeln(F, 'Count: ', Count, ' (mean: ', Count/FTable.Count:5:3, ')');
System.Close(F);
end;
{$ENDIF}
{--------}
procedure TffHash.fhFreeKeyPrim(aKey : pointer);
begin
{ Do nothing. }
end;
{--------}
function TffHash.fhGetIndex(const AKey : Pointer;
const ACount : Integer): Integer;
begin
Result := Longint(AKey) mod ACount;
end;
{--------}
function TffHash.Get(const AKey: Integer): Pointer;
var
Inx : integer;
Node : TffHashNode;
begin
Result := nil;
if fhFindPrim(Pointer(aKey), Inx, Node) then
Result := Node.fhValue
end;
{--------}
procedure TffHash.Iterate(const CallBack : TffHashIteratorFunction;
const cookie1, cookie2, cookie3 : longInt);
var
Count : Integer;
Node : TffHashNode;
begin
for Count := 0 to Pred(FTable.Count) do begin
Node := TffHashNode(FTable[Count]);
while assigned(Node) do begin
CallBack(longInt(Node.fhKey), Node.fhValue, cookie1, cookie2, cookie3);
Node := Node.fhNext;
end;
end;
end;
{--------}
procedure TffHash.IterateSafely(const CallBack : TffHashIteratorFunction;
const cookie1, cookie2, cookie3 : longInt);
var
Count : Integer;
FirstNode : TffHashNode;
NextNode : TffHashNode;
Node : TffHashNode;
PrevNode : TffHashNode;
begin
{ Assumption: The TffHashNode.ExtraData property is not used for
any other purpose. }
{ String the nodes together. }
FirstNode := nil;
PrevNode := nil;
for Count := 0 to Pred(FTable.Count) do begin
Node := TffHashNode(FTable[Count]);
while assigned(Node) do begin
if FirstNode = nil then
FirstNode := Node;
if Assigned(PrevNode) then
PrevNode.ExtraData := Node;
PrevNode := Node;
Node := Node.fhNext;
end;
end;
{ Iterate through the list of nodes. }
Node := FirstNode;
while assigned(Node) do begin
NextNode := Node.ExtraData;
Callback(longInt(Node.fhKey), Node.fhValue, cookie1, cookie2, cookie3);
Node := NextNode;
end;
end;
{--------}
function TffHash.Remove(const AKey: Longint) : Boolean; {!!.02}
var
Inx : integer;
Node : TffHashNode;
begin
if fhFindPrim(Pointer(aKey), Inx, Node) then begin
fhDeletePrim(Pointer(aKey), Inx);
{ Shrink the table if:
1. Shrinking is allowed.
2. We are not at the minimum size already.
3. We have some elements.
4. We have some elements and we're under 1/6 full
}
if FCanShrink and (not FAtMin) and
(FCount > 10) and ((FCount * 6) < FTable.Count) then
fhResizeTable(False);
Result := True; {!!.02}
end {!!.02}
else {!!.02}
Result := False; {!!.02}
end;
{====================================================================}
{===TffHash64========================================================}
function TffHash64.Add(const aKey : TffInt64;
aValue : Pointer): Boolean;
var
keyPtr : pointer;
begin
FFGetMem(keyPtr, sizeOf(TffInt64));
TffInt64(keyPtr^) := aKey;
Result := fhAddPrim(keyPtr, aValue);
if not Result then
FFFreeMem(keyPtr, SizeOf(TffInt64));
end;
{--------}
{$IFDEF CompileDebugCode}
procedure TffHash64.DebugPrint(const AFileName: string);
var
F : text;
i : integer;
Node : TffHashNode;
begin
System.Assign(F, aFileName);
System.Rewrite(F);
for i := 0 to pred(FTable.Count) do begin
writeln(F, '---', i, '---');
Node := TffHashNode(FTable[i]);
while assigned(Node) do begin
writeln(F, FFI64ToStr(PffInt64(Node.fhKey)^), intToStr(longInt(Node.fhValue)):20);
Node := Node.fhNext;
end;
end;
writeln(F);
writeln(F, 'Count: ', Count, ' (mean: ', Count/FTable.Count:5:3, ')');
System.Close(F);
end;
{$ENDIF}
{--------}
function TffHash64.fhCompareKey(const aKey1 : Pointer;
const aKey2 : Pointer) : Boolean;
begin
Result := FFCmpI64(PffInt64(aKey1)^, PffInt64(aKey2)^) = 0;
end;
{--------}
procedure TffHash64.fhFreeKeyPrim(aKey : pointer);
begin
FFFreeMem(aKey, sizeOf(TffInt64));
end;
{--------}
function TffHash64.fhGetIndex(const AKey : Pointer;
const ACount : Integer): Integer;
var
Int : Integer;
begin
Int := ffI64ModInt(PffInt64(AKey)^, ACount);
Result := Int;
end;
{--------}
function TffHash64.Get(const AKey : TffInt64) : Pointer;
var
Inx : integer;
Node : TffHashNode;
begin
Result := nil;
if fhFindPrim(@aKey, Inx, Node) then
Result := Node.fhValue
end;
{--------}
procedure TffHash64.Iterate(const CallBack : TffHash64IteratorFunction;
const cookie1, cookie2, cookie3 : longInt);
var
Count : Integer;
Node : TffHashNode;
begin
for Count := 0 to Pred(FTable.Count) do begin
Node := TffHashNode(FTable[Count]);
while assigned(Node) do begin
CallBack(TffInt64(Node.fhKey^), Node.fhValue, cookie1, cookie2, cookie3);
Node := Node.fhNext;
end;
end;
end;
{--------}
procedure Tffhash64.IterateSafely(const CallBack : TffHash64IteratorFunction;
const cookie1, cookie2, cookie3 : longInt);
var
Count : Integer;
FirstNode : TffHashNode;
NextNode : TffHashNode;
Node : TffHashNode;
PrevNode : TffHashNode;
begin
{ Assumption: The TffHashNode.ExtraData property is not used for
any other purpose. }
{ String the nodes together. }
FirstNode := nil;
PrevNode := nil;
for Count := 0 to Pred(FTable.Count) do begin
Node := TffHashNode(FTable[Count]);
while assigned(Node) do begin
if FirstNode = nil then
FirstNode := Node;
if Assigned(PrevNode) then
PrevNode.ExtraData := Node;
PrevNode := Node;
Node := Node.fhNext;
end;
end;
{ Iterate through the list of nodes. }
Node := FirstNode;
while assigned(Node) do begin
NextNode := Node.ExtraData;
Callback(TffInt64(Node.fhKey^), Node.fhValue, cookie1, cookie2, cookie3);
Node := NextNode;
end;
end;
{--------}
procedure TffHash64.Remove(const AKey : TffInt64);
var
Inx : integer;
Node : TffHashNode;
begin
if fhFindPrim(@aKey, Inx, Node) then begin
fhDeletePrim(@aKey, Inx);
{ Shrink the table if:
1. Shrinking is allowed.
2. We are not at the minimum size already.
3. We have some elements.
4. We have some elements and we're under 1/6 full
}
if FCanShrink and (not FAtMin) and
(FCount > 10) and ((FCount * 6) < FTable.Count) then
fhResizeTable(False);
end;
end;
{====================================================================}
{===TffThreadHash====================================================}
function TffThreadHash.BeginRead : TffThreadHash;
begin
if IsMultiThread then
FPortal.BeginRead;
Result := Self
end;
{--------}
function TffThreadHash.BeginWrite : TffThreadHash;
begin
if IsMultiThread then
FPortal.BeginWrite;
Result := Self
end;
{--------}
constructor TffThreadHash.Create(initialSizeIndex : Integer);
begin
inherited Create(initialSizeIndex);
FPortal := TffReadWritePortal.Create;
end;
{--------}
destructor TffThreadHash.Destroy;
begin
if Assigned(FPortal) then
FPortal.Free;
inherited Destroy;
end;
{--------}
procedure TffThreadHash.EndRead;
begin
if IsMultiThread then
FPortal.EndRead;
end;
{--------}
procedure TffThreadHash.EndWrite;
begin
if IsMultiThread then
FPortal.EndWrite;
end;
{====================================================================}
{===TffThreadHash64==================================================}
function TffThreadHash64.BeginRead : TffThreadHash64;
begin
FPortal.BeginRead;
Result := Self
end;
{--------}
function TffThreadHash64.BeginWrite : TffThreadHash64;
begin
FPortal.BeginWrite;
Result := Self
end;
{--------}
constructor TffThreadHash64.Create(initialSizeIndex : Integer);
begin
inherited Create(initialSizeIndex);
FPortal := TffReadWritePortal.Create;
end;
{--------}
destructor TffThreadHash64.Destroy;
begin
if Assigned(FPortal) then
FPortal.Free;
inherited Destroy;
end;
{--------}
procedure TffThreadHash64.EndRead;
begin
FPortal.EndRead;
end;
{--------}
procedure TffThreadHash64.EndWrite;
begin
FPortal.EndWrite;
end;
{====================================================================}
(****
Note: the original C routine looked like this:
unsigned long ElfHash ( const unsigned char *name )
{
unsigned long h = 0, g;
while ( *name )
{
h = ( h << 4 ) + *name++;
if ( g = h & 0xF0000000 )
h ^= g >> 24;
h &= ~g;
}
return h;
}
****)
{$Q-} {!!.05}
function FFCalcELFHash(const Buffer; BufSize : integer) : TffWord32;
var
BufAsBytes : TffByteArray absolute Buffer;
G : TffWord32;
i : integer;
begin
Result := 0;
for i := 0 to pred(BufSize) do begin
Result := (Result shl 4) + BufAsBytes[i];
G := Result and $F0000000;
if (G <> 0) then
Result := Result xor (G shr 24);
Result := Result and (not G);
end;
end;
{$Q+} {!!.05}
{--------}
function FFCalcShStrELFHash(const S : TffShStr) : TffWord32;
begin
Result := FFCalcELFHash(S[1], length(S));
end;
end.