unit CollHash;
(*****************************************************************************
* Copyright 2003 by Matthew Greet
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library 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. See the GNU Lesser General Public License for more
* details. (http://opensource.org/licenses/lgpl-license.php)
*
* See http://www.warmachine.u-net.com/delphi_collections for updates and downloads.
*
* $Version: v1.0.3 $
* $Revision: 1.1.1.2 $
* $Log: D:\QVCS Repositories\Delphi Collections\CollHash.qbt $
*
* Collection implementations based on hash tables.
*
* Revision 1.1.1.2 by: Matthew Greet Rev date: 12/06/04 20:04:30
* Capacity property.
*
* Revision 1.1.1.1 by: Matthew Greet Rev date: 24/10/03 16:48:16
* v1.0 branch.
*
* Revision 1.1 by: Matthew Greet Rev date: 06/04/03 10:40:16
* Added integer map and string map versions.
* THashSet uses its own implementation, not THashMap.
* DefaulMaxLoadFactor changed.
*
* Revision 1.0 by: Matthew Greet Rev date: 01/03/03 10:50:02
* Initial revision.
*
* FPC compatibility fixes by: UltraStar Deluxe Team
*
* $Endlog$
*****************************************************************************)
{$IFDEF FPC}
{$MODE Delphi}{$H+}
{$ENDIF}
interface
uses
Classes, Math,
Collections;
const
DefaultTableSize = 100;
MaxLoadFactorMin = 0.01; // Minimum allowed value for MaxLoadFactor property.
DefaultMaxLoadFactor = 5.0;
type
THashMap = class(TAbstractMap)
private
FArray: TListArray;
FCapacity: Integer;
FMaxLoadFactor: Double;
FSize: Integer;
FTableSize: Integer;
protected
function GetAssociationIterator: IMapIterator; override;
procedure SetMaxLoadFactor(Value: Double); virtual;
procedure SetTableSize(Value: Integer); virtual;
procedure ChangeCapacity(Value: TListArray); virtual;
procedure CheckLoadFactor(AlwaysChangeCapacity: Boolean); virtual;
function GetHash(const Key: ICollectable): Integer; virtual;
function GetKeyPosition(const Key: ICollectable): TCollectionPosition; override;
procedure Rehash;
procedure TrueClear; override;
function TrueGet(Position: TCollectionPosition): IAssociation; override;
function TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation; override;
function TrueRemove2(Position: TCollectionPosition): IAssociation; override;
public
constructor Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean); override;
destructor Destroy; override;
class function GetAlwaysNaturalKeys: Boolean; override;
function GetCapacity: Integer; override;
procedure SetCapacity(Value: Integer); override;
function GetNaturalKeyIID: TGUID; override;
function GetSize: Integer; override;
property MaxLoadFactor: Double read FMaxLoadFactor write SetMaxLoadFactor;
property TableSize: Integer read FTableSize write SetTableSize;
end;
THashSet = class(TAbstractSet)
private
FArray: TListArray;
FCapacity: Integer;
FMaxLoadFactor: Double;
FSize: Integer;
FTableSize: Integer;
protected
procedure SetMaxLoadFactor(Value: Double); virtual;
procedure SetTableSize(Value: Integer); virtual;
procedure ChangeCapacity(Value: TListArray); virtual;
procedure CheckLoadFactor(AlwaysChangeCapacity: Boolean); virtual;
function GetHash(const Item: ICollectable): Integer; virtual;
function GetPosition(const Item: ICollectable): TCollectionPosition; override;
procedure Rehash;
procedure TrueAdd2(Position: TCollectionPosition; const Item: ICollectable); override;
procedure TrueClear; override;
function TrueGet(Position: TCollectionPosition): ICollectable; override;
procedure TrueRemove2(Position: TCollectionPosition); override;
public
constructor Create(NaturalItemsOnly: Boolean); override;
destructor Destroy; override;
class function GetAlwaysNaturalItems: Boolean; override;
function GetCapacity: Integer; override;
procedure SetCapacity(Value: Integer); override;
function GetIterator: IIterator; override;
function GetNaturalItemIID: TGUID; override;
function GetSize: Integer; override;
property MaxLoadFactor: Double read FMaxLoadFactor write SetMaxLoadFactor;
property TableSize: Integer read FTableSize write SetTableSize;
end;
THashIntegerMap = class(TAbstractIntegerMap)
private
FArray: TListArray;
FCapacity: Integer;
FMaxLoadFactor: Double;
FSize: Integer;
FTableSize: Integer;
protected
function GetAssociationIterator: IIntegerMapIterator; override;
procedure SetMaxLoadFactor(Value: Double); virtual;
procedure SetTableSize(Value: Integer); virtual;
procedure ChangeCapacity(Value: TListArray); virtual;
procedure CheckLoadFactor(AlwaysChangeCapacity: Boolean); virtual;
function GetHash(const Key: Integer): Integer; virtual;
function GetKeyPosition(const Key: Integer): TCollectionPosition; override;
procedure Rehash;
procedure TrueClear; override;
function TrueGet(Position: TCollectionPosition): IIntegerAssociation; override;
function TruePut(Position: TCollectionPosition; const Association: IIntegerAssociation): IIntegerAssociation; override;
function TrueRemove2(Position: TCollectionPosition): IIntegerAssociation; override;
public
constructor Create; override;
constructor Create(NaturalItemsOnly: Boolean); override;
constructor Create(NaturalItemsOnly: Boolean; TableSize: Integer; MaxLoadFactor: Double = DefaultMaxLoadFactor); overload; virtual;
destructor Destroy; override;
function GetCapacity: Integer; override;
procedure SetCapacity(Value: Integer); override;
function GetSize: Integer; override;
property MaxLoadFactor: Double read FMaxLoadFactor write SetMaxLoadFactor;
property TableSize: Integer read FTableSize write SetTableSize;
end;
THashStringMap = class(TAbstractStringMap)
private
FArray: TListArray;
FCapacity: Integer;
FMaxLoadFactor: Double;
FSize: Integer;
FTableSize: Integer;
protected
function GetAssociationIterator: IStringMapIterator; override;
procedure SetMaxLoadFactor(Value: Double); virtual;
procedure SetTableSize(Value: Integer); virtual;
procedure ChangeCapacity(Value: TListArray); virtual;
procedure CheckLoadFactor(AlwaysChangeCapacity: Boolean); virtual;
function GetHash(const Key: String): Integer; virtual;
function GetKeyPosition(const Key: String): TCollectionPosition; override;
procedure Rehash;
procedure TrueClear; override;
function TrueGet(Position: TCollectionPosition): IStringAssociation; override;
function TruePut(Position: TCollectionPosition; const Association: IStringAssociation): IStringAssociation; override;
function TrueRemove2(Position: TCollectionPosition): IStringAssociation; override;
public
constructor Create; override;
constructor Create(NaturalItemsOnly: Boolean); override;
constructor Create(NaturalItemsOnly: Boolean; TableSize: Integer; MaxLoadFactor: Double = DefaultMaxLoadFactor); overload; virtual;
destructor Destroy; override;
function GetCapacity: Integer; override;
procedure SetCapacity(Value: Integer); override;
function GetSize: Integer; override;
property MaxLoadFactor: Double read FMaxLoadFactor write SetMaxLoadFactor;
property TableSize: Integer read FTableSize write SetTableSize;
end;
implementation
const
(* (sqrt(5) - 1)/2
See Introduction to Algorithms in Pascal, 1995, by Thomas W. Parsons,
published by John Wiley & Sons, Inc, ISBN 0-471-11600-9
*)
HashFactor = 0.618033988749894848204586834365638;
type
THashIterator = class(TAbstractIterator)
private
FHashSet: THashSet;
FHash: Integer;
FChainIndex: Integer;
protected
constructor Create(HashSet: THashSet);
function TrueFirst: ICollectable; override;
function TrueNext: ICollectable; override;
procedure TrueRemove; override;
end;
THashAssociationIterator = class(TAbstractAssociationIterator)
private
FHashMap: THashMap;
FHash: Integer;
FChainIndex: Integer;
protected
constructor Create(HashMap: THashMap);
function TrueFirst: IAssociation; override;
function TrueNext: IAssociation; override;
procedure TrueRemove; override;
end;
THashIntegerIterator = class(TAbstractIntegerAssociationIterator)
private
FHashIntegerMap: THashIntegerMap;
FHash: Integer;
FChainIndex: Integer;
protected
constructor Create(HashIntegerMap: THashIntegerMap);
function TrueFirst: IIntegerAssociation; override;
function TrueNext: IIntegerAssociation; override;
procedure TrueRemove; override;
end;
THashStringIterator = class(TAbstractStringAssociationIterator)
private
FHashStringMap: THashStringMap;
FHash: Integer;
FChainIndex: Integer;
protected
constructor Create(HashStringMap: THashStringMap);
function TrueFirst: IStringAssociation; override;
function TrueNext: IStringAssociation; override;
procedure TrueRemove; override;
end;
THashPosition = class(TCollectionPosition)
private
FChain: TList;
FIndex: Integer;
public
constructor Create(Found: Boolean; Chain: TList; Index: Integer);
property Chain: TList read FChain;
property Index: Integer read FIndex;
end;
{ THashMap }
constructor THashMap.Create(NaturalItemsOnly: Boolean; NaturalKeysOnly: Boolean);
var
I: Integer;
begin
// Force use of natural keys
inherited Create(NaturalItemsOnly, true);
FTableSize := DefaultTableSize;
FMaxLoadFactor := DefaultMaxLoadFactor;
SetLength(FArray, FTableSize);
for I := Low(FArray) to High(FArray) do
FArray[I] := TList.Create;
FCapacity := 0;
FSize := 0;
ChangeCapacity(FArray);
end;
destructor THashMap.Destroy;
var
I: Integer;
begin
for I := Low(FArray) to High(FArray) do
FArray[I].Free;
FArray := nil;
inherited Destroy;
end;
class function THashMap.GetAlwaysNaturalKeys: Boolean;
begin
Result := true;
end;
function THashMap.GetNaturalKeyIID: TGUID;
begin
Result := HashableIID;
end;
function THashMap.GetAssociationIterator: IMapIterator;
begin
Result := THashAssociationIterator.Create(Self);
end;
procedure THashMap.SetTableSize(Value: Integer);
begin
if (FTableSize <> Value) and (Value >= 1) then
begin
FTableSize := Value;
Rehash;
end;
end;
procedure THashMap.SetMaxLoadFactor(Value: Double);
begin
if (FMaxLoadFactor <> Value) and (Value >= MaxLoadFactorMin) then
begin
FMaxLoadFactor := Value;
CheckLoadFactor(false);
end;
end;
procedure THashMap.ChangeCapacity(Value: TListArray);
var
Chain: TList;
I, Total, ChainCapacity: Integer;
begin
if FCapacity mod FTableSize = 0 then
ChainCapacity := Trunc(FCapacity / FTableSize)
else
ChainCapacity := Trunc(FCapacity / FTableSize) + 1;
Total := 0;
for I := Low(Value) to High(Value) do
begin
Chain := Value[I];
Chain.Capacity := ChainCapacity;
Total := Total + Chain.Capacity;
end;
FCapacity := Total;
end;
procedure THashMap.CheckLoadFactor(AlwaysChangeCapacity: Boolean);
var
LoadFactor: Double;
begin
LoadFactor := Capacity / TableSize;
if LoadFactor > MaxLoadFactor then
TableSize := Trunc(Capacity / Max(MaxLoadFactor, MaxLoadFactorMin))
else if AlwaysChangeCapacity then
ChangeCapacity(FArray);
end;
function THashMap.GetHash(const Key: ICollectable): Integer;
var
Hashable: IHashable;
HashCode: Cardinal;
begin
Key.QueryInterface(IHashable, Hashable);
HashCode := Hashable.HashCode;
Result := Trunc(Frac(HashCode * HashFactor) * TableSize);
end;
function THashMap.GetKeyPosition(const Key: ICollectable): TCollectionPosition;
var
Chain: TList;
I: Integer;
Success: Boolean;
begin
Chain := FArray[GetHash(Key)];
Success := false;
for I := 0 to Chain.Count - 1 do
begin
Success := KeyComparator.Equals(Key, IAssociation(Chain[I]).GetKey);
if Success then
Break;
end;
Result := THashPosition.Create(Success, Chain, I);
end;
procedure THashMap.Rehash;
var
NewArray: TListArray;
OldChain, NewChain: TList;
Association: IAssociation;
Total: Integer;
I, J: Integer;
Hash: Integer;
begin
// Create new chains
SetLength(NewArray, TableSize);
for I := Low(NewArray) to High(NewArray) do
begin
NewChain := TList.Create;
NewArray[I] := NewChain;
end;
ChangeCapacity(NewArray);
// Transfer from old chains to new and drop old
for I := Low(FArray) to High(FArray) do
begin
OldChain := FArray[I];
for J := 0 to OldChain.Count - 1 do
begin
Association := IAssociation(OldChain[J]);
Hash := GetHash(Association.GetKey);
NewArray[Hash].Add(Pointer(Association));
end;
OldChain.Free;
end;
FArray := NewArray;
// Find actual, new capacity
Total := 0;
for I := Low(FArray) to High(FArray) do
begin
NewChain := FArray[I];
Total := Total + NewChain.Capacity;
end;
FCapacity := Total;
end;
procedure THashMap.TrueClear;
var
Association: IAssociation;
Chain: TList;
I, J: Integer;
begin
for I := Low(FArray) to High(FArray) do
begin
Chain := FArray[I];
for J := 0 to Chain.Count - 1 do
begin
Association := IAssociation(Chain[J]);
Chain[J] := nil;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
Chain.Clear;
end;
FSize := 0;
end;
function THashMap.TrueGet(Position: TCollectionPosition): IAssociation;
var
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
Result := IAssociation(HashPosition.Chain.Items[HashPosition.Index]);
end;
function THashMap.TruePut(Position: TCollectionPosition; const Association: IAssociation): IAssociation;
var
HashPosition: THashPosition;
OldAssociation: IAssociation;
begin
HashPosition := THashPosition(Position);
if HashPosition.Found then
begin
OldAssociation := IAssociation(HashPosition.Chain.Items[HashPosition.Index]);
HashPosition.Chain.Items[HashPosition.Index] := Pointer(Association);
Result := OldAssociation;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._AddRef;
OldAssociation._Release;
end
else
begin
HashPosition.Chain.Add(Pointer(Association));
Inc(FSize);
Result := nil;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._AddRef;
end;
end;
function THashMap.TrueRemove2(Position: TCollectionPosition): IAssociation;
var
Association: IAssociation;
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
Association := IAssociation(TrueGet(Position));
HashPosition.Chain.Delete(HashPosition.Index);
Dec(FSize);
Result := Association;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
function THashMap.GetCapacity;
begin
Result := FCapacity;
end;
procedure THashMap.SetCapacity(Value: Integer);
begin
FCapacity := Value;
CheckLoadFactor(true);
end;
function THashMap.GetSize: Integer;
begin
Result := FSize;
end;
{ THashSet }
constructor THashSet.Create(NaturalItemsOnly: Boolean);
var
I: Integer;
begin
// Force use of natural items
inherited Create(true);
FTableSize := DefaultTableSize;
FMaxLoadFactor := DefaultMaxLoadFactor;
SetLength(FArray, FTableSize);
for I := Low(FArray) to High(FArray) do
FArray[I] := TList.Create;
FSize := 0;
end;
destructor THashSet.Destroy;
var
I: Integer;
begin
for I := Low(FArray) to High(FArray) do
FArray[I].Free;
FArray := nil;
inherited Destroy;
end;
procedure THashSet.SetTableSize(Value: Integer);
begin
if (FTableSize <> Value) and (Value >= 1) then
begin
FTableSize := Value;
Rehash;
end;
end;
procedure THashSet.SetMaxLoadFactor(Value: Double);
begin
if (FMaxLoadFactor <> Value) and (Value >= MaxLoadFactorMin) then
begin
FMaxLoadFactor := Value;
CheckLoadFactor(false);
end;
end;
procedure THashSet.ChangeCapacity(Value: TListArray);
var
Chain: TList;
I, Total, ChainCapacity: Integer;
begin
if FCapacity mod FTableSize = 0 then
ChainCapacity := Trunc(FCapacity / FTableSize)
else
ChainCapacity := Trunc(FCapacity / FTableSize) + 1;
Total := 0;
for I := Low(Value) to High(Value) do
begin
Chain := Value[I];
Chain.Capacity := ChainCapacity;
Total := Total + Chain.Capacity;
end;
FCapacity := Total;
end;
procedure THashSet.CheckLoadFactor(AlwaysChangeCapacity: Boolean);
var
LoadFactor: Double;
begin
LoadFactor := Capacity / TableSize;
if LoadFactor > MaxLoadFactor then
TableSize := Trunc(Capacity / Max(MaxLoadFactor, MaxLoadFactorMin))
else if AlwaysChangeCapacity then
ChangeCapacity(FArray);
end;
function THashSet.GetHash(const Item: ICollectable): Integer;
var
Hashable: IHashable;
HashCode: Cardinal;
begin
Item.QueryInterface(IHashable, Hashable);
HashCode := Hashable.HashCode;
Result := Trunc(Frac(HashCode * HashFactor) * TableSize);
end;
function THashSet.GetPosition(const Item: ICollectable): TCollectionPosition;
var
Chain: TList;
I: Integer;
Success: Boolean;
begin
Chain := FArray[GetHash(Item)];
Success := false;
for I := 0 to Chain.Count - 1 do
begin
Success := Comparator.Equals(Item, ICollectable(Chain[I]));
if Success then
Break;
end;
Result := THashPosition.Create(Success, Chain, I);
end;
procedure THashSet.Rehash;
var
NewArray: TListArray;
OldChain, NewChain: TList;
Item: ICollectable;
Total: Integer;
I, J: Integer;
Hash: Integer;
begin
// Create new chains
SetLength(NewArray, TableSize);
for I := Low(NewArray) to High(NewArray) do
begin
NewChain := TList.Create;
NewArray[I] := NewChain;
end;
ChangeCapacity(NewArray);
// Transfer from old chains to new and drop old
for I := Low(FArray) to High(FArray) do
begin
OldChain := FArray[I];
for J := 0 to OldChain.Count - 1 do
begin
Item := ICollectable(OldChain[J]);
Hash := GetHash(Item);
NewArray[Hash].Add(Pointer(Item));
end;
OldChain.Free;
end;
FArray := NewArray;
// Find actual, new capacity
Total := 0;
for I := Low(FArray) to High(FArray) do
begin
NewChain := FArray[I];
Total := Total + NewChain.Capacity;
end;
FCapacity := Total;
end;
procedure THashSet.TrueAdd2(Position: TCollectionPosition; const Item: ICollectable);
var
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
HashPosition.Chain.Add(Pointer(Item));
Inc(FSize);
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Item._AddRef;
end;
procedure THashSet.TrueClear;
var
Item: ICollectable;
Chain: TList;
I, J: Integer;
begin
for I := Low(FArray) to High(FArray) do
begin
Chain := FArray[I];
for J := 0 to Chain.Count - 1 do
begin
Item := ICollectable(Chain[J]);
Chain[J] := nil;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Item._Release;
end;
Chain.Clear;
end;
FSize := 0;
end;
function THashSet.TrueGet(Position: TCollectionPosition): ICollectable;
var
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
Result := ICollectable(HashPosition.Chain.Items[HashPosition.Index]);
end;
procedure THashSet.TrueRemove2(Position: TCollectionPosition);
var
Item: ICollectable;
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
Item := TrueGet(Position);
HashPosition.Chain.Delete(HashPosition.Index);
Dec(FSize);
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Item._Release;
end;
class function THashSet.GetAlwaysNaturalItems: Boolean;
begin
Result := true;
end;
function THashSet.GetIterator: IIterator;
begin
Result := THashIterator.Create(Self);
end;
function THashSet.GetNaturalItemIID: TGUID;
begin
Result := HashableIID;
end;
function THashSet.GetCapacity;
begin
Result := FCapacity;
end;
procedure THashSet.SetCapacity(Value: Integer);
begin
FCapacity := Value;
CheckLoadFactor(true);
end;
function THashSet.GetSize: Integer;
begin
Result := FSize;
end;
{ THashIntegerMap }
constructor THashIntegerMap.Create;
begin
Create(false, DefaultTableSize, DefaultMaxLoadFactor);
end;
constructor THashIntegerMap.Create(NaturalItemsOnly: Boolean);
begin
Create(NaturalItemsOnly, DefaultTableSize, DefaultMaxLoadFactor);
end;
constructor THashIntegerMap.Create(NaturalItemsOnly: Boolean; TableSize: Integer; MaxLoadFactor: Double = DefaultMaxLoadFactor);
var
I: Integer;
begin
inherited Create(NaturalItemsOnly);
SetLength(FArray, TableSize);
for I := Low(FArray) to High(FArray) do
FArray[I] := TList.Create;
FTableSize := TableSize;
FMaxLoadFactor := MaxLoadFactor;
FSize := 0;
end;
destructor THashIntegerMap.Destroy;
var
I: Integer;
begin
for I := Low(FArray) to High(FArray) do
FArray[I].Free;
FArray := nil;
inherited Destroy;
end;
function THashIntegerMap.GetAssociationIterator: IIntegerMapIterator;
begin
Result := THashIntegerIterator.Create(Self);
end;
procedure THashIntegerMap.SetTableSize(Value: Integer);
begin
if (FTableSize <> Value) and (Value >= 1) then
begin
FTableSize := Value;
Rehash;
end;
end;
procedure THashIntegerMap.SetMaxLoadFactor(Value: Double);
begin
if (FMaxLoadFactor <> Value) and (Value >= MaxLoadFactorMin) then
begin
FMaxLoadFactor := Value;
CheckLoadFactor(false);
end;
end;
procedure THashIntegerMap.ChangeCapacity;
var
Chain: TList;
I, Total, ChainCapacity: Integer;
begin
if FCapacity mod FTableSize = 0 then
ChainCapacity := Trunc(FCapacity / FTableSize)
else
ChainCapacity := Trunc(FCapacity / FTableSize) + 1;
Total := 0;
for I := Low(Value) to High(Value) do
begin
Chain := Value[I];
Chain.Capacity := ChainCapacity;
Total := Total + Chain.Capacity;
end;
FCapacity := Total;
end;
procedure THashIntegerMap.CheckLoadFactor(AlwaysChangeCapacity: Boolean);
var
LoadFactor: Double;
begin
LoadFactor := Capacity / TableSize;
if LoadFactor > MaxLoadFactor then
TableSize := Trunc(Capacity / Max(MaxLoadFactor, MaxLoadFactorMin))
else if AlwaysChangeCapacity then
ChangeCapacity(FArray);
end;
function THashIntegerMap.GetHash(const Key: Integer): Integer;
begin
Result := Trunc(Frac(Cardinal(Key) * HashFactor) * TableSize);
end;
function THashIntegerMap.GetKeyPosition(const Key: Integer): TCollectionPosition;
var
Chain: TList;
I: Integer;
Success: Boolean;
begin
Chain := FArray[GetHash(Key)];
Success := false;
for I := 0 to Chain.Count - 1 do
begin
Success := (Key = IIntegerAssociation(Chain[I]).GetKey);
if Success then
Break;
end;
Result := THashPosition.Create(Success, Chain, I);
end;
procedure THashIntegerMap.Rehash;
var
NewArray: TListArray;
OldChain, NewChain: TList;
Association: IIntegerAssociation;
Total: Integer;
I, J: Integer;
Hash: Integer;
begin
// Create new chains
SetLength(NewArray, TableSize);
for I := Low(NewArray) to High(NewArray) do
begin
NewChain := TList.Create;
NewArray[I] := NewChain;
end;
ChangeCapacity(NewArray);
// Transfer from old chains to new and drop old
for I := Low(FArray) to High(FArray) do
begin
OldChain := FArray[I];
for J := 0 to OldChain.Count - 1 do
begin
Association := IIntegerAssociation(OldChain[J]);
Hash := GetHash(Association.GetKey);
NewArray[Hash].Add(Pointer(Association));
end;
OldChain.Free;
end;
FArray := NewArray;
// Find actual, new capacity
Total := 0;
for I := Low(FArray) to High(FArray) do
begin
NewChain := FArray[I];
Total := Total + NewChain.Capacity;
end;
FCapacity := Total;
end;
procedure THashIntegerMap.TrueClear;
var
Association: IIntegerAssociation;
Chain: TList;
I, J: Integer;
begin
for I := Low(FArray) to High(FArray) do
begin
Chain := FArray[I];
for J := 0 to Chain.Count - 1 do
begin
Association := IIntegerAssociation(Chain[J]);
Chain[J] := nil;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
Chain.Clear;
end;
FSize := 0;
end;
function THashIntegerMap.TrueGet(Position: TCollectionPosition): IIntegerAssociation;
var
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
Result := IIntegerAssociation(HashPosition.Chain.Items[HashPosition.Index]);
end;
function THashIntegerMap.TruePut(Position: TCollectionPosition; const Association: IIntegerAssociation): IIntegerAssociation;
var
HashPosition: THashPosition;
OldAssociation: IIntegerAssociation;
begin
HashPosition := THashPosition(Position);
if HashPosition.Found then
begin
OldAssociation := IIntegerAssociation(HashPosition.Chain.Items[HashPosition.Index]);
HashPosition.Chain.Items[HashPosition.Index] := Pointer(Association);
Result := OldAssociation;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._AddRef;
OldAssociation._Release;
end
else
begin
HashPosition.Chain.Add(Pointer(Association));
Inc(FSize);
Result := nil;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._AddRef;
end;
end;
function THashIntegerMap.TrueRemove2(Position: TCollectionPosition): IIntegerAssociation;
var
Association: IIntegerAssociation;
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
Association := IIntegerAssociation(TrueGet(Position));
HashPosition.Chain.Delete(HashPosition.Index);
Dec(FSize);
Result := Association;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
function THashIntegerMap.GetCapacity;
begin
Result := FCapacity;
end;
procedure THashIntegerMap.SetCapacity(Value: Integer);
begin
FCapacity := Value;
CheckLoadFactor(true);
end;
function THashIntegerMap.GetSize: Integer;
begin
Result := FSize;
end;
{ THashStringMap }
constructor THashStringMap.Create;
begin
Create(false, DefaultTableSize, DefaultMaxLoadFactor);
end;
constructor THashStringMap.Create(NaturalItemsOnly: Boolean);
begin
Create(NaturalItemsOnly, DefaultTableSize, DefaultMaxLoadFactor);
end;
constructor THashStringMap.Create(NaturalItemsOnly: Boolean; TableSize: Integer; MaxLoadFactor: Double = DefaultMaxLoadFactor);
var
I: Integer;
begin
inherited Create(NaturalItemsOnly);
SetLength(FArray, TableSize);
for I := Low(FArray) to High(FArray) do
FArray[I] := TList.Create;
FTableSize := TableSize;
FMaxLoadFactor := MaxLoadFactor;
FSize := 0;
end;
destructor THashStringMap.Destroy;
var
I: Integer;
begin
for I := Low(FArray) to High(FArray) do
FArray[I].Free;
FArray := nil;
inherited Destroy;
end;
function THashStringMap.GetAssociationIterator: IStringMapIterator;
begin
Result := THashStringIterator.Create(Self);
end;
procedure THashStringMap.SetTableSize(Value: Integer);
begin
if (FTableSize <> Value) and (Value >= 1) then
begin
FTableSize := Value;
Rehash;
end;
end;
procedure THashStringMap.SetMaxLoadFactor(Value: Double);
begin
if (FMaxLoadFactor <> Value) and (Value >= MaxLoadFactorMin) then
begin
FMaxLoadFactor := Value;
CheckLoadFactor(false);
end;
end;
procedure THashStringMap.ChangeCapacity;
var
Chain: TList;
I, Total, ChainCapacity: Integer;
begin
if FCapacity mod FTableSize = 0 then
ChainCapacity := Trunc(FCapacity / FTableSize)
else
ChainCapacity := Trunc(FCapacity / FTableSize) + 1;
Total := 0;
for I := Low(Value) to High(Value) do
begin
Chain := Value[I];
Chain.Capacity := ChainCapacity;
Total := Total + Chain.Capacity;
end;
FCapacity := Total;
end;
procedure THashStringMap.CheckLoadFactor(AlwaysChangeCapacity: Boolean);
var
LoadFactor: Double;
begin
LoadFactor := Capacity / TableSize;
if LoadFactor > MaxLoadFactor then
TableSize := Trunc(Capacity / Max(MaxLoadFactor, MaxLoadFactorMin))
else if AlwaysChangeCapacity then
ChangeCapacity(FArray);
end;
function THashStringMap.GetHash(const Key: String): Integer;
var
HashCode: Cardinal;
I: Integer;
begin
HashCode := 0;
for I := 1 to Length(Key) do
HashCode := (HashCode shl 1) xor Ord(Key[I]);
Result := Trunc(Frac(HashCode * HashFactor) * TableSize);
end;
function THashStringMap.GetKeyPosition(const Key: String): TCollectionPosition;
var
Chain: TList;
I: Integer;
Success: Boolean;
begin
Chain := FArray[GetHash(Key)];
Success := false;
for I := 0 to Chain.Count - 1 do
begin
Success := (Key = IStringAssociation(Chain[I]).GetKey);
if Success then
Break;
end;
Result := THashPosition.Create(Success, Chain, I);
end;
procedure THashStringMap.Rehash;
var
NewArray: TListArray;
OldChain, NewChain: TList;
Association: IStringAssociation;
Total: Integer;
I, J: Integer;
Hash: Integer;
begin
// Create new chains
SetLength(NewArray, TableSize);
for I := Low(NewArray) to High(NewArray) do
begin
NewChain := TList.Create;
NewArray[I] := NewChain;
end;
ChangeCapacity(NewArray);
// Transfer from old chains to new and drop old
for I := Low(FArray) to High(FArray) do
begin
OldChain := FArray[I];
for J := 0 to OldChain.Count - 1 do
begin
Association := IStringAssociation(OldChain[J]);
Hash := GetHash(Association.GetKey);
NewArray[Hash].Add(Pointer(Association));
end;
OldChain.Free;
end;
FArray := NewArray;
// Find actual, new capacity
Total := 0;
for I := Low(FArray) to High(FArray) do
begin
NewChain := FArray[I];
Total := Total + NewChain.Capacity;
end;
FCapacity := Total;
end;
procedure THashStringMap.TrueClear;
var
Association: IStringAssociation;
Chain: TList;
I, J: Integer;
begin
for I := Low(FArray) to High(FArray) do
begin
Chain := FArray[I];
for J := 0 to Chain.Count - 1 do
begin
Association := IStringAssociation(Chain[J]);
Chain[J] := nil;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
Chain.Clear;
end;
FSize := 0;
end;
function THashStringMap.TrueGet(Position: TCollectionPosition): IStringAssociation;
var
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
Result := IStringAssociation(HashPosition.Chain.Items[HashPosition.Index]);
end;
function THashStringMap.TruePut(Position: TCollectionPosition; const Association: IStringAssociation): IStringAssociation;
var
HashPosition: THashPosition;
OldAssociation: IStringAssociation;
begin
HashPosition := THashPosition(Position);
if HashPosition.Found then
begin
OldAssociation := IStringAssociation(HashPosition.Chain.Items[HashPosition.Index]);
HashPosition.Chain.Items[HashPosition.Index] := Pointer(Association);
Result := OldAssociation;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._AddRef;
OldAssociation._Release;
end
else
begin
HashPosition.Chain.Add(Pointer(Association));
Inc(FSize);
Result := nil;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._AddRef;
end;
end;
function THashStringMap.TrueRemove2(Position: TCollectionPosition): IStringAssociation;
var
Association: IStringAssociation;
HashPosition: THashPosition;
begin
HashPosition := THashPosition(Position);
Association := IStringAssociation(TrueGet(Position));
HashPosition.Chain.Delete(HashPosition.Index);
Dec(FSize);
Result := Association;
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
function THashStringMap.GetCapacity;
begin
Result := FCapacity;
end;
procedure THashStringMap.SetCapacity(Value: Integer);
begin
FCapacity := Value;
CheckLoadFactor(true);
end;
function THashStringMap.GetSize: Integer;
begin
Result := FSize;
end;
{ THashPosition }
constructor THashPosition.Create(Found: Boolean; Chain: TList; Index: Integer);
begin
inherited Create(Found);
FChain := Chain;
FIndex := Index;
end;
{ THashIterator }
constructor THashIterator.Create(HashSet: THashSet);
begin
inherited Create(true);
FHashSet := HashSet;
First;
end;
function THashIterator.TrueFirst: ICollectable;
var
Chain: TList;
Success: Boolean;
begin
FHash := 0;
FChainIndex := 0;
Success := false;
while FHash < FHashSet.TableSize do
begin
Chain := FHashSet.FArray[FHash];
Success := Chain.Count > 0;
if Success then
Break;
Inc(FHash);
end;
if Success then
Result := ICollectable(FHashSet.FArray[FHash].Items[FChainIndex])
else
Result := nil;
end;
function THashIterator.TrueNext: ICollectable;
var
Chain: TList;
Success: Boolean;
begin
Success := false;
Chain := FHashSet.FArray[FHash];
repeat
Inc(FChainIndex);
if FChainIndex >= Chain.Count then
begin
Inc(FHash);
FChainIndex := -1;
if FHash < FHashSet.TableSize then
Chain := FHashSet.FArray[FHash];
end
else
Success := true;
until Success or (FHash >= FHashSet.TableSize);
if Success then
Result := ICollectable(FHashSet.FArray[FHash].Items[FChainIndex])
else
Result := nil;
end;
procedure THashIterator.TrueRemove;
var
Item: ICollectable;
begin
Item := ICollectable(FHashSet.FArray[FHash].Items[FChainIndex]);
FHashSet.FArray[FHash].Delete(FChainIndex);
Dec(FChainIndex);
Dec(FHashSet.FSize);
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Item._Release;
end;
{ THashAssociationIterator }
constructor THashAssociationIterator.Create(HashMap: THashMap);
begin
inherited Create(true);
FHashMap := HashMap;
First;
end;
function THashAssociationIterator.TrueFirst: IAssociation;
var
Chain: TList;
Success: Boolean;
begin
FHash := 0;
FChainIndex := 0;
Success := false;
while FHash < FHashMap.TableSize do
begin
Chain := FHashMap.FArray[FHash];
Success := Chain.Count > 0;
if Success then
Break;
Inc(FHash);
end;
if Success then
Result := IAssociation(FHashMap.FArray[FHash].Items[FChainIndex])
else
Result := nil;
end;
function THashAssociationIterator.TrueNext: IAssociation;
var
Chain: TList;
Success: Boolean;
begin
Success := false;
Chain := FHashMap.FArray[FHash];
repeat
Inc(FChainIndex);
if FChainIndex >= Chain.Count then
begin
Inc(FHash);
FChainIndex := -1;
if FHash < FHashMap.TableSize then
Chain := FHashMap.FArray[FHash];
end
else
Success := true;
until Success or (FHash >= FHashMap.TableSize);
if Success then
Result := IAssociation(FHashMap.FArray[FHash].Items[FChainIndex])
else
Result := nil;
end;
procedure THashAssociationIterator.TrueRemove;
var
Association: IAssociation;
begin
Association := IAssociation(FHashMap.FArray[FHash].Items[FChainIndex]);
FHashMap.FArray[FHash].Delete(FChainIndex);
Dec(FChainIndex);
Dec(FHashMap.FSize);
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
{ THashIntegerIterator }
constructor THashIntegerIterator.Create(HashIntegerMap: THashIntegerMap);
begin
inherited Create(true);
FHashIntegerMap := HashIntegerMap;
First;
end;
function THashIntegerIterator.TrueFirst: IIntegerAssociation;
var
Chain: TList;
Success: Boolean;
begin
FHash := 0;
FChainIndex := 0;
Success := false;
while FHash < FHashIntegerMap.TableSize do
begin
Chain := FHashIntegerMap.FArray[FHash];
Success := Chain.Count > 0;
if Success then
Break;
Inc(FHash);
end;
if Success then
Result := IIntegerAssociation(FHashIntegerMap.FArray[FHash].Items[FChainIndex])
else
Result := nil;
end;
function THashIntegerIterator.TrueNext: IIntegerAssociation;
var
Chain: TList;
Success: Boolean;
begin
Success := false;
Chain := FHashIntegerMap.FArray[FHash];
repeat
Inc(FChainIndex);
if FChainIndex >= Chain.Count then
begin
Inc(FHash);
FChainIndex := -1;
if FHash < FHashIntegerMap.TableSize then
Chain := FHashIntegerMap.FArray[FHash];
end
else
Success := true;
until Success or (FHash >= FHashIntegerMap.TableSize);
if Success then
Result := IIntegerAssociation(FHashIntegerMap.FArray[FHash].Items[FChainIndex])
else
Result := nil;
end;
procedure THashIntegerIterator.TrueRemove;
var
Association: IIntegerAssociation;
begin
Association := IIntegerAssociation(FHashIntegerMap.FArray[FHash].Items[FChainIndex]);
FHashIntegerMap.FArray[FHash].Delete(FChainIndex);
Dec(FChainIndex);
Dec(FHashIntegerMap.FSize);
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
{ THashStringIterator }
constructor THashStringIterator.Create(HashStringMap: THashStringMap);
begin
inherited Create(true);
FHashStringMap := HashStringMap;
First;
end;
function THashStringIterator.TrueFirst: IStringAssociation;
var
Chain: TList;
Success: Boolean;
begin
FHash := 0;
FChainIndex := 0;
Success := false;
while FHash < FHashStringMap.TableSize do
begin
Chain := FHashStringMap.FArray[FHash];
Success := Chain.Count > 0;
if Success then
Break;
Inc(FHash);
end;
if Success then
Result := IStringAssociation(FHashStringMap.FArray[FHash].Items[FChainIndex])
else
Result := nil;
end;
function THashStringIterator.TrueNext: IStringAssociation;
var
Chain: TList;
Success: Boolean;
begin
Success := false;
Chain := FHashStringMap.FArray[FHash];
repeat
Inc(FChainIndex);
if FChainIndex >= Chain.Count then
begin
Inc(FHash);
FChainIndex := -1;
if FHash < FHashStringMap.TableSize then
Chain := FHashStringMap.FArray[FHash];
end
else
Success := true;
until Success or (FHash >= FHashStringMap.TableSize);
if Success then
Result := IStringAssociation(FHashStringMap.FArray[FHash].Items[FChainIndex])
else
Result := nil;
end;
procedure THashStringIterator.TrueRemove;
var
Association: IStringAssociation;
begin
Association := IStringAssociation(FHashStringMap.FArray[FHash].Items[FChainIndex]);
FHashStringMap.FArray[FHash].Delete(FChainIndex);
Dec(FChainIndex);
Dec(FHashStringMap.FSize);
// Storing interface reference as a pointer does not update reference
// count automatically, so this must be done manually
Association._Release;
end;
end.