Componentes.Terceros.jcl/official/2.1.1/source/common/JclHashMaps.pas
2010-01-18 16:51:36 +00:00

28445 lines
742 KiB
ObjectPascal

{**************************************************************************************************}
{ WARNING: JEDI preprocessor generated unit. Do not edit. }
{**************************************************************************************************}
{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ 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 HashMap.pas. }
{ }
{ The Initial Developer of the Original Code is Jean-Philippe BEMPEL aka RDM. Portions created by }
{ Jean-Philippe BEMPEL are Copyright (C) Jean-Philippe BEMPEL (rdm_30 att yahoo dott com) }
{ All rights reserved. }
{ }
{ Contributors: }
{ Florent Ouchet (outchy) }
{ }
{**************************************************************************************************}
{ }
{ The Delphi Container Library }
{ }
{**************************************************************************************************}
{ }
{ Last modified: $Date:: 2009-09-12 14:21:23 +0200 (sam., 12 sept. 2009) $ }
{ Revision: $Rev:: 2997 $ }
{ Author: $Author:: outchy $ }
{ }
{**************************************************************************************************}
unit JclHashMaps;
{$I jcl.inc}
interface
uses
Classes,
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
{$IFDEF SUPPORTS_GENERICS}
JclAlgorithms,
{$ENDIF SUPPORTS_GENERICS}
JclBase, JclSynch,
JclContainerIntf, JclAbstractContainers, JclArrayLists, JclArraySets;
type
// Hash Function
// Result must be in 0..Range-1
TJclHashFunction = function(Key, Range: Integer): Integer;
TJclIntfIntfHashEntry = record
Key: IInterface;
Value: IInterface;
end;
TJclIntfIntfBucket = class
public
Size: Integer;
Entries: array of TJclIntfIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfIntfHashMap = class(TJclIntfAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclIntfIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclIntfIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfIntfMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(const Key: IInterface): IInterface;
function GetValue(const Key: IInterface): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfIntfMap): Boolean;
procedure PutAll(const AMap: IJclIntfIntfMap);
procedure PutValue(const Key: IInterface; const Value: IInterface);
function Remove(const Key: IInterface): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclAnsiStrIntfHashEntry = record
Key: AnsiString;
Value: IInterface;
end;
TJclAnsiStrIntfBucket = class
public
Size: Integer;
Entries: array of TJclAnsiStrIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclAnsiStrIntfHashMap = class(TJclAnsiStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclAnsiStrContainer,
IJclAnsiStrIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: AnsiString): AnsiString;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(const A, B: AnsiString): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclAnsiStrIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclAnsiStrIntfMap }
procedure Clear;
function ContainsKey(const Key: AnsiString): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(const Key: AnsiString): IInterface;
function GetValue(const Key: AnsiString): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): AnsiString;
function KeySet: IJclAnsiStrSet;
function MapEquals(const AMap: IJclAnsiStrIntfMap): Boolean;
procedure PutAll(const AMap: IJclAnsiStrIntfMap);
procedure PutValue(const Key: AnsiString; const Value: IInterface);
function Remove(const Key: AnsiString): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfAnsiStrHashEntry = record
Key: IInterface;
Value: AnsiString;
end;
TJclIntfAnsiStrBucket = class
public
Size: Integer;
Entries: array of TJclIntfAnsiStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfAnsiStrHashMap = class(TJclAnsiStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclAnsiStrContainer,
IJclIntfAnsiStrMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: AnsiString): AnsiString;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(const A, B: AnsiString): Boolean;
private
FBuckets: array of TJclIntfAnsiStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfAnsiStrMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(const Value: AnsiString): Boolean;
function Extract(const Key: IInterface): AnsiString;
function GetValue(const Key: IInterface): AnsiString;
function IsEmpty: Boolean;
function KeyOfValue(const Value: AnsiString): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfAnsiStrMap): Boolean;
procedure PutAll(const AMap: IJclIntfAnsiStrMap);
procedure PutValue(const Key: IInterface; const Value: AnsiString);
function Remove(const Key: IInterface): AnsiString;
function Size: Integer;
function Values: IJclAnsiStrCollection;
end;
TJclAnsiStrAnsiStrHashEntry = record
Key: AnsiString;
Value: AnsiString;
end;
TJclAnsiStrAnsiStrBucket = class
public
Size: Integer;
Entries: array of TJclAnsiStrAnsiStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclAnsiStrAnsiStrHashMap = class(TJclAnsiStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclAnsiStrContainer,
IJclAnsiStrAnsiStrMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: AnsiString): AnsiString;
function FreeValue(var Value: AnsiString): AnsiString;
function KeysEqual(const A, B: AnsiString): Boolean;
function ValuesEqual(const A, B: AnsiString): Boolean;
private
FBuckets: array of TJclAnsiStrAnsiStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclAnsiStrAnsiStrMap }
procedure Clear;
function ContainsKey(const Key: AnsiString): Boolean;
function ContainsValue(const Value: AnsiString): Boolean;
function Extract(const Key: AnsiString): AnsiString;
function GetValue(const Key: AnsiString): AnsiString;
function IsEmpty: Boolean;
function KeyOfValue(const Value: AnsiString): AnsiString;
function KeySet: IJclAnsiStrSet;
function MapEquals(const AMap: IJclAnsiStrAnsiStrMap): Boolean;
procedure PutAll(const AMap: IJclAnsiStrAnsiStrMap);
procedure PutValue(const Key: AnsiString; const Value: AnsiString);
function Remove(const Key: AnsiString): AnsiString;
function Size: Integer;
function Values: IJclAnsiStrCollection;
end;
TJclWideStrIntfHashEntry = record
Key: WideString;
Value: IInterface;
end;
TJclWideStrIntfBucket = class
public
Size: Integer;
Entries: array of TJclWideStrIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclWideStrIntfHashMap = class(TJclWideStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclWideStrContainer,
IJclWideStrIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: WideString): WideString;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(const A, B: WideString): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclWideStrIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclWideStrIntfMap }
procedure Clear;
function ContainsKey(const Key: WideString): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(const Key: WideString): IInterface;
function GetValue(const Key: WideString): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): WideString;
function KeySet: IJclWideStrSet;
function MapEquals(const AMap: IJclWideStrIntfMap): Boolean;
procedure PutAll(const AMap: IJclWideStrIntfMap);
procedure PutValue(const Key: WideString; const Value: IInterface);
function Remove(const Key: WideString): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfWideStrHashEntry = record
Key: IInterface;
Value: WideString;
end;
TJclIntfWideStrBucket = class
public
Size: Integer;
Entries: array of TJclIntfWideStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfWideStrHashMap = class(TJclWideStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclWideStrContainer,
IJclIntfWideStrMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: WideString): WideString;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(const A, B: WideString): Boolean;
private
FBuckets: array of TJclIntfWideStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfWideStrMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(const Value: WideString): Boolean;
function Extract(const Key: IInterface): WideString;
function GetValue(const Key: IInterface): WideString;
function IsEmpty: Boolean;
function KeyOfValue(const Value: WideString): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfWideStrMap): Boolean;
procedure PutAll(const AMap: IJclIntfWideStrMap);
procedure PutValue(const Key: IInterface; const Value: WideString);
function Remove(const Key: IInterface): WideString;
function Size: Integer;
function Values: IJclWideStrCollection;
end;
TJclWideStrWideStrHashEntry = record
Key: WideString;
Value: WideString;
end;
TJclWideStrWideStrBucket = class
public
Size: Integer;
Entries: array of TJclWideStrWideStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclWideStrWideStrHashMap = class(TJclWideStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclWideStrContainer,
IJclWideStrWideStrMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: WideString): WideString;
function FreeValue(var Value: WideString): WideString;
function KeysEqual(const A, B: WideString): Boolean;
function ValuesEqual(const A, B: WideString): Boolean;
private
FBuckets: array of TJclWideStrWideStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclWideStrWideStrMap }
procedure Clear;
function ContainsKey(const Key: WideString): Boolean;
function ContainsValue(const Value: WideString): Boolean;
function Extract(const Key: WideString): WideString;
function GetValue(const Key: WideString): WideString;
function IsEmpty: Boolean;
function KeyOfValue(const Value: WideString): WideString;
function KeySet: IJclWideStrSet;
function MapEquals(const AMap: IJclWideStrWideStrMap): Boolean;
procedure PutAll(const AMap: IJclWideStrWideStrMap);
procedure PutValue(const Key: WideString; const Value: WideString);
function Remove(const Key: WideString): WideString;
function Size: Integer;
function Values: IJclWideStrCollection;
end;
{$IFDEF SUPPORTS_UNICODE_STRING}
TJclUnicodeStrIntfHashEntry = record
Key: UnicodeString;
Value: IInterface;
end;
TJclUnicodeStrIntfBucket = class
public
Size: Integer;
Entries: array of TJclUnicodeStrIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclUnicodeStrIntfHashMap = class(TJclUnicodeStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclUnicodeStrContainer,
IJclUnicodeStrIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: UnicodeString): UnicodeString;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(const A, B: UnicodeString): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclUnicodeStrIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclUnicodeStrIntfMap }
procedure Clear;
function ContainsKey(const Key: UnicodeString): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(const Key: UnicodeString): IInterface;
function GetValue(const Key: UnicodeString): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): UnicodeString;
function KeySet: IJclUnicodeStrSet;
function MapEquals(const AMap: IJclUnicodeStrIntfMap): Boolean;
procedure PutAll(const AMap: IJclUnicodeStrIntfMap);
procedure PutValue(const Key: UnicodeString; const Value: IInterface);
function Remove(const Key: UnicodeString): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfUnicodeStrHashEntry = record
Key: IInterface;
Value: UnicodeString;
end;
TJclIntfUnicodeStrBucket = class
public
Size: Integer;
Entries: array of TJclIntfUnicodeStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfUnicodeStrHashMap = class(TJclUnicodeStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclUnicodeStrContainer,
IJclIntfUnicodeStrMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: UnicodeString): UnicodeString;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(const A, B: UnicodeString): Boolean;
private
FBuckets: array of TJclIntfUnicodeStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfUnicodeStrMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(const Value: UnicodeString): Boolean;
function Extract(const Key: IInterface): UnicodeString;
function GetValue(const Key: IInterface): UnicodeString;
function IsEmpty: Boolean;
function KeyOfValue(const Value: UnicodeString): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfUnicodeStrMap): Boolean;
procedure PutAll(const AMap: IJclIntfUnicodeStrMap);
procedure PutValue(const Key: IInterface; const Value: UnicodeString);
function Remove(const Key: IInterface): UnicodeString;
function Size: Integer;
function Values: IJclUnicodeStrCollection;
end;
TJclUnicodeStrUnicodeStrHashEntry = record
Key: UnicodeString;
Value: UnicodeString;
end;
TJclUnicodeStrUnicodeStrBucket = class
public
Size: Integer;
Entries: array of TJclUnicodeStrUnicodeStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclUnicodeStrUnicodeStrHashMap = class(TJclUnicodeStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclUnicodeStrContainer,
IJclUnicodeStrUnicodeStrMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: UnicodeString): UnicodeString;
function FreeValue(var Value: UnicodeString): UnicodeString;
function KeysEqual(const A, B: UnicodeString): Boolean;
function ValuesEqual(const A, B: UnicodeString): Boolean;
private
FBuckets: array of TJclUnicodeStrUnicodeStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclUnicodeStrUnicodeStrMap }
procedure Clear;
function ContainsKey(const Key: UnicodeString): Boolean;
function ContainsValue(const Value: UnicodeString): Boolean;
function Extract(const Key: UnicodeString): UnicodeString;
function GetValue(const Key: UnicodeString): UnicodeString;
function IsEmpty: Boolean;
function KeyOfValue(const Value: UnicodeString): UnicodeString;
function KeySet: IJclUnicodeStrSet;
function MapEquals(const AMap: IJclUnicodeStrUnicodeStrMap): Boolean;
procedure PutAll(const AMap: IJclUnicodeStrUnicodeStrMap);
procedure PutValue(const Key: UnicodeString; const Value: UnicodeString);
function Remove(const Key: UnicodeString): UnicodeString;
function Size: Integer;
function Values: IJclUnicodeStrCollection;
end;
{$ENDIF SUPPORTS_UNICODE_STRING}
{$IFDEF CONTAINER_ANSISTR}
TJclStrIntfHashMap = TJclAnsiStrIntfHashMap;
TJclIntfStrHashMap = TJclIntfAnsiStrHashMap;
TJclStrStrHashMap = TJclAnsiStrAnsiStrHashMap;
{$ENDIF CONTAINER_ANSISTR}
{$IFDEF CONTAINER_WIDESTR}
TJclStrIntfHashMap = TJclWideStrIntfHashMap;
TJclIntfStrHashMap = TJclIntfWideStrHashMap;
TJclStrStrHashMap = TJclWideStrWideStrHashMap;
{$ENDIF CONTAINER_WIDESTR}
{$IFDEF CONTAINER_UNICODESTR}
TJclStrIntfHashMap = TJclUnicodeStrIntfHashMap;
TJclIntfStrHashMap = TJclIntfUnicodeStrHashMap;
TJclStrStrHashMap = TJclUnicodeStrUnicodeStrHashMap;
{$ENDIF CONTAINER_UNICODESTR}
TJclSingleIntfHashEntry = record
Key: Single;
Value: IInterface;
end;
TJclSingleIntfBucket = class
public
Size: Integer;
Entries: array of TJclSingleIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclSingleIntfHashMap = class(TJclSingleAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclSingleContainer,
IJclSingleIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Single): Single;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(const A, B: Single): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclSingleIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclSingleIntfMap }
procedure Clear;
function ContainsKey(const Key: Single): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(const Key: Single): IInterface;
function GetValue(const Key: Single): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): Single;
function KeySet: IJclSingleSet;
function MapEquals(const AMap: IJclSingleIntfMap): Boolean;
procedure PutAll(const AMap: IJclSingleIntfMap);
procedure PutValue(const Key: Single; const Value: IInterface);
function Remove(const Key: Single): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfSingleHashEntry = record
Key: IInterface;
Value: Single;
end;
TJclIntfSingleBucket = class
public
Size: Integer;
Entries: array of TJclIntfSingleHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfSingleHashMap = class(TJclSingleAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclSingleContainer,
IJclIntfSingleMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: Single): Single;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(const A, B: Single): Boolean;
private
FBuckets: array of TJclIntfSingleBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfSingleMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(const Value: Single): Boolean;
function Extract(const Key: IInterface): Single;
function GetValue(const Key: IInterface): Single;
function IsEmpty: Boolean;
function KeyOfValue(const Value: Single): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfSingleMap): Boolean;
procedure PutAll(const AMap: IJclIntfSingleMap);
procedure PutValue(const Key: IInterface; const Value: Single);
function Remove(const Key: IInterface): Single;
function Size: Integer;
function Values: IJclSingleCollection;
end;
TJclSingleSingleHashEntry = record
Key: Single;
Value: Single;
end;
TJclSingleSingleBucket = class
public
Size: Integer;
Entries: array of TJclSingleSingleHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclSingleSingleHashMap = class(TJclSingleAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclSingleContainer,
IJclSingleSingleMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Single): Single;
function FreeValue(var Value: Single): Single;
function KeysEqual(const A, B: Single): Boolean;
function ValuesEqual(const A, B: Single): Boolean;
private
FBuckets: array of TJclSingleSingleBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclSingleSingleMap }
procedure Clear;
function ContainsKey(const Key: Single): Boolean;
function ContainsValue(const Value: Single): Boolean;
function Extract(const Key: Single): Single;
function GetValue(const Key: Single): Single;
function IsEmpty: Boolean;
function KeyOfValue(const Value: Single): Single;
function KeySet: IJclSingleSet;
function MapEquals(const AMap: IJclSingleSingleMap): Boolean;
procedure PutAll(const AMap: IJclSingleSingleMap);
procedure PutValue(const Key: Single; const Value: Single);
function Remove(const Key: Single): Single;
function Size: Integer;
function Values: IJclSingleCollection;
end;
TJclDoubleIntfHashEntry = record
Key: Double;
Value: IInterface;
end;
TJclDoubleIntfBucket = class
public
Size: Integer;
Entries: array of TJclDoubleIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclDoubleIntfHashMap = class(TJclDoubleAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclDoubleContainer,
IJclDoubleIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Double): Double;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(const A, B: Double): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclDoubleIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclDoubleIntfMap }
procedure Clear;
function ContainsKey(const Key: Double): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(const Key: Double): IInterface;
function GetValue(const Key: Double): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): Double;
function KeySet: IJclDoubleSet;
function MapEquals(const AMap: IJclDoubleIntfMap): Boolean;
procedure PutAll(const AMap: IJclDoubleIntfMap);
procedure PutValue(const Key: Double; const Value: IInterface);
function Remove(const Key: Double): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfDoubleHashEntry = record
Key: IInterface;
Value: Double;
end;
TJclIntfDoubleBucket = class
public
Size: Integer;
Entries: array of TJclIntfDoubleHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfDoubleHashMap = class(TJclDoubleAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclDoubleContainer,
IJclIntfDoubleMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: Double): Double;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(const A, B: Double): Boolean;
private
FBuckets: array of TJclIntfDoubleBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfDoubleMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(const Value: Double): Boolean;
function Extract(const Key: IInterface): Double;
function GetValue(const Key: IInterface): Double;
function IsEmpty: Boolean;
function KeyOfValue(const Value: Double): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfDoubleMap): Boolean;
procedure PutAll(const AMap: IJclIntfDoubleMap);
procedure PutValue(const Key: IInterface; const Value: Double);
function Remove(const Key: IInterface): Double;
function Size: Integer;
function Values: IJclDoubleCollection;
end;
TJclDoubleDoubleHashEntry = record
Key: Double;
Value: Double;
end;
TJclDoubleDoubleBucket = class
public
Size: Integer;
Entries: array of TJclDoubleDoubleHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclDoubleDoubleHashMap = class(TJclDoubleAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclDoubleContainer,
IJclDoubleDoubleMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Double): Double;
function FreeValue(var Value: Double): Double;
function KeysEqual(const A, B: Double): Boolean;
function ValuesEqual(const A, B: Double): Boolean;
private
FBuckets: array of TJclDoubleDoubleBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclDoubleDoubleMap }
procedure Clear;
function ContainsKey(const Key: Double): Boolean;
function ContainsValue(const Value: Double): Boolean;
function Extract(const Key: Double): Double;
function GetValue(const Key: Double): Double;
function IsEmpty: Boolean;
function KeyOfValue(const Value: Double): Double;
function KeySet: IJclDoubleSet;
function MapEquals(const AMap: IJclDoubleDoubleMap): Boolean;
procedure PutAll(const AMap: IJclDoubleDoubleMap);
procedure PutValue(const Key: Double; const Value: Double);
function Remove(const Key: Double): Double;
function Size: Integer;
function Values: IJclDoubleCollection;
end;
TJclExtendedIntfHashEntry = record
Key: Extended;
Value: IInterface;
end;
TJclExtendedIntfBucket = class
public
Size: Integer;
Entries: array of TJclExtendedIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclExtendedIntfHashMap = class(TJclExtendedAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclExtendedContainer,
IJclExtendedIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Extended): Extended;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(const A, B: Extended): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclExtendedIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclExtendedIntfMap }
procedure Clear;
function ContainsKey(const Key: Extended): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(const Key: Extended): IInterface;
function GetValue(const Key: Extended): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): Extended;
function KeySet: IJclExtendedSet;
function MapEquals(const AMap: IJclExtendedIntfMap): Boolean;
procedure PutAll(const AMap: IJclExtendedIntfMap);
procedure PutValue(const Key: Extended; const Value: IInterface);
function Remove(const Key: Extended): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfExtendedHashEntry = record
Key: IInterface;
Value: Extended;
end;
TJclIntfExtendedBucket = class
public
Size: Integer;
Entries: array of TJclIntfExtendedHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfExtendedHashMap = class(TJclExtendedAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclExtendedContainer,
IJclIntfExtendedMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: Extended): Extended;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(const A, B: Extended): Boolean;
private
FBuckets: array of TJclIntfExtendedBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfExtendedMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(const Value: Extended): Boolean;
function Extract(const Key: IInterface): Extended;
function GetValue(const Key: IInterface): Extended;
function IsEmpty: Boolean;
function KeyOfValue(const Value: Extended): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfExtendedMap): Boolean;
procedure PutAll(const AMap: IJclIntfExtendedMap);
procedure PutValue(const Key: IInterface; const Value: Extended);
function Remove(const Key: IInterface): Extended;
function Size: Integer;
function Values: IJclExtendedCollection;
end;
TJclExtendedExtendedHashEntry = record
Key: Extended;
Value: Extended;
end;
TJclExtendedExtendedBucket = class
public
Size: Integer;
Entries: array of TJclExtendedExtendedHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclExtendedExtendedHashMap = class(TJclExtendedAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclExtendedContainer,
IJclExtendedExtendedMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Extended): Extended;
function FreeValue(var Value: Extended): Extended;
function KeysEqual(const A, B: Extended): Boolean;
function ValuesEqual(const A, B: Extended): Boolean;
private
FBuckets: array of TJclExtendedExtendedBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclExtendedExtendedMap }
procedure Clear;
function ContainsKey(const Key: Extended): Boolean;
function ContainsValue(const Value: Extended): Boolean;
function Extract(const Key: Extended): Extended;
function GetValue(const Key: Extended): Extended;
function IsEmpty: Boolean;
function KeyOfValue(const Value: Extended): Extended;
function KeySet: IJclExtendedSet;
function MapEquals(const AMap: IJclExtendedExtendedMap): Boolean;
procedure PutAll(const AMap: IJclExtendedExtendedMap);
procedure PutValue(const Key: Extended; const Value: Extended);
function Remove(const Key: Extended): Extended;
function Size: Integer;
function Values: IJclExtendedCollection;
end;
{$IFDEF MATH_EXTENDED_PRECISION}
TJclFloatIntfHashMap = TJclExtendedIntfHashMap;
TJclIntfFloatHashMap = TJclIntfExtendedHashMap;
TJclFloatFloatHashMap = TJclExtendedExtendedHashMap;
{$ENDIF MATH_EXTENDED_PRECISION}
{$IFDEF MATH_DOUBLE_PRECISION}
TJclFloatIntfHashMap = TJclDoubleIntfHashMap;
TJclIntfFloatHashMap = TJclIntfDoubleHashMap;
TJclFloatFloatHashMap = TJclDoubleDoubleHashMap;
{$ENDIF MATH_DOUBLE_PRECISION}
{$IFDEF MATH_SINGLE_PRECISION}
TJclFloatIntfHashMap = TJclSingleIntfHashMap;
TJclIntfFloatHashMap = TJclIntfSingleHashMap;
TJclFloatFloatHashMap = TJclSingleSingleHashMap;
{$ENDIF MATH_SINGLE_PRECISION}
TJclIntegerIntfHashEntry = record
Key: Integer;
Value: IInterface;
end;
TJclIntegerIntfBucket = class
public
Size: Integer;
Entries: array of TJclIntegerIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntegerIntfHashMap = class(TJclIntegerAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclIntegerIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Integer): Integer;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(A, B: Integer): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclIntegerIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntegerIntfMap }
procedure Clear;
function ContainsKey(Key: Integer): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(Key: Integer): IInterface;
function GetValue(Key: Integer): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): Integer;
function KeySet: IJclIntegerSet;
function MapEquals(const AMap: IJclIntegerIntfMap): Boolean;
procedure PutAll(const AMap: IJclIntegerIntfMap);
procedure PutValue(Key: Integer; const Value: IInterface);
function Remove(Key: Integer): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfIntegerHashEntry = record
Key: IInterface;
Value: Integer;
end;
TJclIntfIntegerBucket = class
public
Size: Integer;
Entries: array of TJclIntfIntegerHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfIntegerHashMap = class(TJclIntegerAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclIntfIntegerMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: Integer): Integer;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(A, B: Integer): Boolean;
private
FBuckets: array of TJclIntfIntegerBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfIntegerMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(Value: Integer): Boolean;
function Extract(const Key: IInterface): Integer;
function GetValue(const Key: IInterface): Integer;
function IsEmpty: Boolean;
function KeyOfValue(Value: Integer): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfIntegerMap): Boolean;
procedure PutAll(const AMap: IJclIntfIntegerMap);
procedure PutValue(const Key: IInterface; Value: Integer);
function Remove(const Key: IInterface): Integer;
function Size: Integer;
function Values: IJclIntegerCollection;
end;
TJclIntegerIntegerHashEntry = record
Key: Integer;
Value: Integer;
end;
TJclIntegerIntegerBucket = class
public
Size: Integer;
Entries: array of TJclIntegerIntegerHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntegerIntegerHashMap = class(TJclIntegerAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclIntegerIntegerMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Integer): Integer;
function FreeValue(var Value: Integer): Integer;
function KeysEqual(A, B: Integer): Boolean;
function ValuesEqual(A, B: Integer): Boolean;
private
FBuckets: array of TJclIntegerIntegerBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntegerIntegerMap }
procedure Clear;
function ContainsKey(Key: Integer): Boolean;
function ContainsValue(Value: Integer): Boolean;
function Extract(Key: Integer): Integer;
function GetValue(Key: Integer): Integer;
function IsEmpty: Boolean;
function KeyOfValue(Value: Integer): Integer;
function KeySet: IJclIntegerSet;
function MapEquals(const AMap: IJclIntegerIntegerMap): Boolean;
procedure PutAll(const AMap: IJclIntegerIntegerMap);
procedure PutValue(Key: Integer; Value: Integer);
function Remove(Key: Integer): Integer;
function Size: Integer;
function Values: IJclIntegerCollection;
end;
TJclCardinalIntfHashEntry = record
Key: Cardinal;
Value: IInterface;
end;
TJclCardinalIntfBucket = class
public
Size: Integer;
Entries: array of TJclCardinalIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclCardinalIntfHashMap = class(TJclCardinalAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclCardinalIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Cardinal): Cardinal;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(A, B: Cardinal): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclCardinalIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclCardinalIntfMap }
procedure Clear;
function ContainsKey(Key: Cardinal): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(Key: Cardinal): IInterface;
function GetValue(Key: Cardinal): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): Cardinal;
function KeySet: IJclCardinalSet;
function MapEquals(const AMap: IJclCardinalIntfMap): Boolean;
procedure PutAll(const AMap: IJclCardinalIntfMap);
procedure PutValue(Key: Cardinal; const Value: IInterface);
function Remove(Key: Cardinal): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfCardinalHashEntry = record
Key: IInterface;
Value: Cardinal;
end;
TJclIntfCardinalBucket = class
public
Size: Integer;
Entries: array of TJclIntfCardinalHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfCardinalHashMap = class(TJclCardinalAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclIntfCardinalMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: Cardinal): Cardinal;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(A, B: Cardinal): Boolean;
private
FBuckets: array of TJclIntfCardinalBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfCardinalMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(Value: Cardinal): Boolean;
function Extract(const Key: IInterface): Cardinal;
function GetValue(const Key: IInterface): Cardinal;
function IsEmpty: Boolean;
function KeyOfValue(Value: Cardinal): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfCardinalMap): Boolean;
procedure PutAll(const AMap: IJclIntfCardinalMap);
procedure PutValue(const Key: IInterface; Value: Cardinal);
function Remove(const Key: IInterface): Cardinal;
function Size: Integer;
function Values: IJclCardinalCollection;
end;
TJclCardinalCardinalHashEntry = record
Key: Cardinal;
Value: Cardinal;
end;
TJclCardinalCardinalBucket = class
public
Size: Integer;
Entries: array of TJclCardinalCardinalHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclCardinalCardinalHashMap = class(TJclCardinalAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclCardinalCardinalMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Cardinal): Cardinal;
function FreeValue(var Value: Cardinal): Cardinal;
function KeysEqual(A, B: Cardinal): Boolean;
function ValuesEqual(A, B: Cardinal): Boolean;
private
FBuckets: array of TJclCardinalCardinalBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclCardinalCardinalMap }
procedure Clear;
function ContainsKey(Key: Cardinal): Boolean;
function ContainsValue(Value: Cardinal): Boolean;
function Extract(Key: Cardinal): Cardinal;
function GetValue(Key: Cardinal): Cardinal;
function IsEmpty: Boolean;
function KeyOfValue(Value: Cardinal): Cardinal;
function KeySet: IJclCardinalSet;
function MapEquals(const AMap: IJclCardinalCardinalMap): Boolean;
procedure PutAll(const AMap: IJclCardinalCardinalMap);
procedure PutValue(Key: Cardinal; Value: Cardinal);
function Remove(Key: Cardinal): Cardinal;
function Size: Integer;
function Values: IJclCardinalCollection;
end;
TJclInt64IntfHashEntry = record
Key: Int64;
Value: IInterface;
end;
TJclInt64IntfBucket = class
public
Size: Integer;
Entries: array of TJclInt64IntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclInt64IntfHashMap = class(TJclInt64AbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclInt64IntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Int64): Int64;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(const A, B: Int64): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclInt64IntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclInt64IntfMap }
procedure Clear;
function ContainsKey(const Key: Int64): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(const Key: Int64): IInterface;
function GetValue(const Key: Int64): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): Int64;
function KeySet: IJclInt64Set;
function MapEquals(const AMap: IJclInt64IntfMap): Boolean;
procedure PutAll(const AMap: IJclInt64IntfMap);
procedure PutValue(const Key: Int64; const Value: IInterface);
function Remove(const Key: Int64): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfInt64HashEntry = record
Key: IInterface;
Value: Int64;
end;
TJclIntfInt64Bucket = class
public
Size: Integer;
Entries: array of TJclIntfInt64HashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfInt64HashMap = class(TJclInt64AbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclIntfInt64Map)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: Int64): Int64;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(const A, B: Int64): Boolean;
private
FBuckets: array of TJclIntfInt64Bucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfInt64Map }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(const Value: Int64): Boolean;
function Extract(const Key: IInterface): Int64;
function GetValue(const Key: IInterface): Int64;
function IsEmpty: Boolean;
function KeyOfValue(const Value: Int64): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfInt64Map): Boolean;
procedure PutAll(const AMap: IJclIntfInt64Map);
procedure PutValue(const Key: IInterface; const Value: Int64);
function Remove(const Key: IInterface): Int64;
function Size: Integer;
function Values: IJclInt64Collection;
end;
TJclInt64Int64HashEntry = record
Key: Int64;
Value: Int64;
end;
TJclInt64Int64Bucket = class
public
Size: Integer;
Entries: array of TJclInt64Int64HashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclInt64Int64HashMap = class(TJclInt64AbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclInt64Int64Map)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Int64): Int64;
function FreeValue(var Value: Int64): Int64;
function KeysEqual(const A, B: Int64): Boolean;
function ValuesEqual(const A, B: Int64): Boolean;
private
FBuckets: array of TJclInt64Int64Bucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclInt64Int64Map }
procedure Clear;
function ContainsKey(const Key: Int64): Boolean;
function ContainsValue(const Value: Int64): Boolean;
function Extract(const Key: Int64): Int64;
function GetValue(const Key: Int64): Int64;
function IsEmpty: Boolean;
function KeyOfValue(const Value: Int64): Int64;
function KeySet: IJclInt64Set;
function MapEquals(const AMap: IJclInt64Int64Map): Boolean;
procedure PutAll(const AMap: IJclInt64Int64Map);
procedure PutValue(const Key: Int64; const Value: Int64);
function Remove(const Key: Int64): Int64;
function Size: Integer;
function Values: IJclInt64Collection;
end;
TJclPtrIntfHashEntry = record
Key: Pointer;
Value: IInterface;
end;
TJclPtrIntfBucket = class
public
Size: Integer;
Entries: array of TJclPtrIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclPtrIntfHashMap = class(TJclPtrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclPtrIntfMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Pointer): Pointer;
function FreeValue(var Value: IInterface): IInterface;
function KeysEqual(A, B: Pointer): Boolean;
function ValuesEqual(const A, B: IInterface): Boolean;
private
FBuckets: array of TJclPtrIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclPtrIntfMap }
procedure Clear;
function ContainsKey(Key: Pointer): Boolean;
function ContainsValue(const Value: IInterface): Boolean;
function Extract(Key: Pointer): IInterface;
function GetValue(Key: Pointer): IInterface;
function IsEmpty: Boolean;
function KeyOfValue(const Value: IInterface): Pointer;
function KeySet: IJclPtrSet;
function MapEquals(const AMap: IJclPtrIntfMap): Boolean;
procedure PutAll(const AMap: IJclPtrIntfMap);
procedure PutValue(Key: Pointer; const Value: IInterface);
function Remove(Key: Pointer): IInterface;
function Size: Integer;
function Values: IJclIntfCollection;
end;
TJclIntfPtrHashEntry = record
Key: IInterface;
Value: Pointer;
end;
TJclIntfPtrBucket = class
public
Size: Integer;
Entries: array of TJclIntfPtrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfPtrHashMap = class(TJclPtrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclIntfPtrMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function FreeValue(var Value: Pointer): Pointer;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(A, B: Pointer): Boolean;
private
FBuckets: array of TJclIntfPtrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfPtrMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(Value: Pointer): Boolean;
function Extract(const Key: IInterface): Pointer;
function GetValue(const Key: IInterface): Pointer;
function IsEmpty: Boolean;
function KeyOfValue(Value: Pointer): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfPtrMap): Boolean;
procedure PutAll(const AMap: IJclIntfPtrMap);
procedure PutValue(const Key: IInterface; Value: Pointer);
function Remove(const Key: IInterface): Pointer;
function Size: Integer;
function Values: IJclPtrCollection;
end;
TJclPtrPtrHashEntry = record
Key: Pointer;
Value: Pointer;
end;
TJclPtrPtrBucket = class
public
Size: Integer;
Entries: array of TJclPtrPtrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclPtrPtrHashMap = class(TJclPtrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer,
IJclPtrPtrMap)
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Pointer): Pointer;
function FreeValue(var Value: Pointer): Pointer;
function KeysEqual(A, B: Pointer): Boolean;
function ValuesEqual(A, B: Pointer): Boolean;
private
FBuckets: array of TJclPtrPtrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclPtrPtrMap }
procedure Clear;
function ContainsKey(Key: Pointer): Boolean;
function ContainsValue(Value: Pointer): Boolean;
function Extract(Key: Pointer): Pointer;
function GetValue(Key: Pointer): Pointer;
function IsEmpty: Boolean;
function KeyOfValue(Value: Pointer): Pointer;
function KeySet: IJclPtrSet;
function MapEquals(const AMap: IJclPtrPtrMap): Boolean;
procedure PutAll(const AMap: IJclPtrPtrMap);
procedure PutValue(Key: Pointer; Value: Pointer);
function Remove(Key: Pointer): Pointer;
function Size: Integer;
function Values: IJclPtrCollection;
end;
TJclIntfHashEntry = record
Key: IInterface;
Value: TObject;
end;
TJclIntfBucket = class
public
Size: Integer;
Entries: array of TJclIntfHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntfHashMap = class(TJclAbstractContainerBase, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclValueOwner,
IJclIntfMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: IInterface): IInterface;
function Hash(const AInterface: IInterface): Integer; reintroduce;
function KeysEqual(const A, B: IInterface): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclIntfBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntfMap }
procedure Clear;
function ContainsKey(const Key: IInterface): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(const Key: IInterface): TObject;
function GetValue(const Key: IInterface): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): IInterface;
function KeySet: IJclIntfSet;
function MapEquals(const AMap: IJclIntfMap): Boolean;
procedure PutAll(const AMap: IJclIntfMap);
procedure PutValue(const Key: IInterface; Value: TObject);
function Remove(const Key: IInterface): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
TJclAnsiStrHashEntry = record
Key: AnsiString;
Value: TObject;
end;
TJclAnsiStrBucket = class
public
Size: Integer;
Entries: array of TJclAnsiStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclAnsiStrHashMap = class(TJclAnsiStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclAnsiStrContainer, IJclValueOwner,
IJclAnsiStrMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: AnsiString): AnsiString;
function KeysEqual(const A, B: AnsiString): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclAnsiStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclAnsiStrMap }
procedure Clear;
function ContainsKey(const Key: AnsiString): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(const Key: AnsiString): TObject;
function GetValue(const Key: AnsiString): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): AnsiString;
function KeySet: IJclAnsiStrSet;
function MapEquals(const AMap: IJclAnsiStrMap): Boolean;
procedure PutAll(const AMap: IJclAnsiStrMap);
procedure PutValue(const Key: AnsiString; Value: TObject);
function Remove(const Key: AnsiString): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
TJclWideStrHashEntry = record
Key: WideString;
Value: TObject;
end;
TJclWideStrBucket = class
public
Size: Integer;
Entries: array of TJclWideStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclWideStrHashMap = class(TJclwideStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclWideStrContainer, IJclValueOwner,
IJclWideStrMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: WideString): WideString;
function KeysEqual(const A, B: WideString): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclWideStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclWideStrMap }
procedure Clear;
function ContainsKey(const Key: WideString): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(const Key: WideString): TObject;
function GetValue(const Key: WideString): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): WideString;
function KeySet: IJclWideStrSet;
function MapEquals(const AMap: IJclWideStrMap): Boolean;
procedure PutAll(const AMap: IJclWideStrMap);
procedure PutValue(const Key: WideString; Value: TObject);
function Remove(const Key: WideString): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
{$IFDEF SUPPORTS_UNICODE_STRING}
TJclUnicodeStrHashEntry = record
Key: UnicodeString;
Value: TObject;
end;
TJclUnicodeStrBucket = class
public
Size: Integer;
Entries: array of TJclUnicodeStrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclUnicodeStrHashMap = class(TJclUnicodeStrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclStrContainer, IJclUnicodeStrContainer, IJclValueOwner,
IJclUnicodeStrMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: UnicodeString): UnicodeString;
function KeysEqual(const A, B: UnicodeString): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclUnicodeStrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclUnicodeStrMap }
procedure Clear;
function ContainsKey(const Key: UnicodeString): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(const Key: UnicodeString): TObject;
function GetValue(const Key: UnicodeString): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): UnicodeString;
function KeySet: IJclUnicodeStrSet;
function MapEquals(const AMap: IJclUnicodeStrMap): Boolean;
procedure PutAll(const AMap: IJclUnicodeStrMap);
procedure PutValue(const Key: UnicodeString; Value: TObject);
function Remove(const Key: UnicodeString): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
{$ENDIF SUPPORTS_UNICODE_STRING}
{$IFDEF CONTAINER_ANSISTR}
TJclStrHashMap = TJclAnsiStrHashMap;
{$ENDIF CONTAINER_ANSISTR}
{$IFDEF CONTAINER_WIDESTR}
TJclStrHashMap = TJclWideStrHashMap;
{$ENDIF CONTAINER_WIDESTR}
{$IFDEF CONTAINER_UNICODESTR}
TJclStrHashMap = TJclUnicodeStrHashMap;
{$ENDIF CONTAINER_UNICODESTR}
TJclSingleHashEntry = record
Key: Single;
Value: TObject;
end;
TJclSingleBucket = class
public
Size: Integer;
Entries: array of TJclSingleHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclSingleHashMap = class(TJclSingleAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclSingleContainer, IJclValueOwner,
IJclSingleMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Single): Single;
function KeysEqual(const A, B: Single): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclSingleBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclSingleMap }
procedure Clear;
function ContainsKey(const Key: Single): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(const Key: Single): TObject;
function GetValue(const Key: Single): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): Single;
function KeySet: IJclSingleSet;
function MapEquals(const AMap: IJclSingleMap): Boolean;
procedure PutAll(const AMap: IJclSingleMap);
procedure PutValue(const Key: Single; Value: TObject);
function Remove(const Key: Single): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
TJclDoubleHashEntry = record
Key: Double;
Value: TObject;
end;
TJclDoubleBucket = class
public
Size: Integer;
Entries: array of TJclDoubleHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclDoubleHashMap = class(TJclDoubleAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclDoubleContainer, IJclValueOwner,
IJclDoubleMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Double): Double;
function KeysEqual(const A, B: Double): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclDoubleBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclDoubleMap }
procedure Clear;
function ContainsKey(const Key: Double): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(const Key: Double): TObject;
function GetValue(const Key: Double): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): Double;
function KeySet: IJclDoubleSet;
function MapEquals(const AMap: IJclDoubleMap): Boolean;
procedure PutAll(const AMap: IJclDoubleMap);
procedure PutValue(const Key: Double; Value: TObject);
function Remove(const Key: Double): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
TJclExtendedHashEntry = record
Key: Extended;
Value: TObject;
end;
TJclExtendedBucket = class
public
Size: Integer;
Entries: array of TJclExtendedHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclExtendedHashMap = class(TJclExtendedAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclExtendedContainer, IJclValueOwner,
IJclExtendedMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Extended): Extended;
function KeysEqual(const A, B: Extended): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclExtendedBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclExtendedMap }
procedure Clear;
function ContainsKey(const Key: Extended): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(const Key: Extended): TObject;
function GetValue(const Key: Extended): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): Extended;
function KeySet: IJclExtendedSet;
function MapEquals(const AMap: IJclExtendedMap): Boolean;
procedure PutAll(const AMap: IJclExtendedMap);
procedure PutValue(const Key: Extended; Value: TObject);
function Remove(const Key: Extended): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
{$IFDEF MATH_EXTENDED_PRECISION}
TJclFloatHashMap = TJclExtendedHashMap;
{$ENDIF MATH_EXTENDED_PRECISION}
{$IFDEF MATH_DOUBLE_PRECISION}
TJclFloatHashMap = TJclDoubleHashMap;
{$ENDIF MATH_DOUBLE_PRECISION}
{$IFDEF MATH_SINGLE_PRECISION}
TJclFloatHashMap = TJclSingleHashMap;
{$ENDIF MATH_SINGLE_PRECISION}
TJclIntegerHashEntry = record
Key: Integer;
Value: TObject;
end;
TJclIntegerBucket = class
public
Size: Integer;
Entries: array of TJclIntegerHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclIntegerHashMap = class(TJclIntegerAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclValueOwner,
IJclIntegerMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Integer): Integer;
function KeysEqual(A, B: Integer): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclIntegerBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclIntegerMap }
procedure Clear;
function ContainsKey(Key: Integer): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(Key: Integer): TObject;
function GetValue(Key: Integer): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): Integer;
function KeySet: IJclIntegerSet;
function MapEquals(const AMap: IJclIntegerMap): Boolean;
procedure PutAll(const AMap: IJclIntegerMap);
procedure PutValue(Key: Integer; Value: TObject);
function Remove(Key: Integer): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
TJclCardinalHashEntry = record
Key: Cardinal;
Value: TObject;
end;
TJclCardinalBucket = class
public
Size: Integer;
Entries: array of TJclCardinalHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclCardinalHashMap = class(TJclCardinalAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclValueOwner,
IJclCardinalMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Cardinal): Cardinal;
function KeysEqual(A, B: Cardinal): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclCardinalBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclCardinalMap }
procedure Clear;
function ContainsKey(Key: Cardinal): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(Key: Cardinal): TObject;
function GetValue(Key: Cardinal): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): Cardinal;
function KeySet: IJclCardinalSet;
function MapEquals(const AMap: IJclCardinalMap): Boolean;
procedure PutAll(const AMap: IJclCardinalMap);
procedure PutValue(Key: Cardinal; Value: TObject);
function Remove(Key: Cardinal): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
TJclInt64HashEntry = record
Key: Int64;
Value: TObject;
end;
TJclInt64Bucket = class
public
Size: Integer;
Entries: array of TJclInt64HashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclInt64HashMap = class(TJclInt64AbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclValueOwner,
IJclInt64Map)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Int64): Int64;
function KeysEqual(const A, B: Int64): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclInt64Bucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclInt64Map }
procedure Clear;
function ContainsKey(const Key: Int64): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(const Key: Int64): TObject;
function GetValue(const Key: Int64): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): Int64;
function KeySet: IJclInt64Set;
function MapEquals(const AMap: IJclInt64Map): Boolean;
procedure PutAll(const AMap: IJclInt64Map);
procedure PutValue(const Key: Int64; Value: TObject);
function Remove(const Key: Int64): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
TJclPtrHashEntry = record
Key: Pointer;
Value: TObject;
end;
TJclPtrBucket = class
public
Size: Integer;
Entries: array of TJclPtrHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclPtrHashMap = class(TJclPtrAbstractContainer, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclValueOwner,
IJclPtrMap)
private
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function FreeKey(var Key: Pointer): Pointer;
function KeysEqual(A, B: Pointer): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclPtrBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclPtrMap }
procedure Clear;
function ContainsKey(Key: Pointer): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(Key: Pointer): TObject;
function GetValue(Key: Pointer): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): Pointer;
function KeySet: IJclPtrSet;
function MapEquals(const AMap: IJclPtrMap): Boolean;
procedure PutAll(const AMap: IJclPtrMap);
procedure PutValue(Key: Pointer; Value: TObject);
function Remove(Key: Pointer): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
TJclHashEntry = record
Key: TObject;
Value: TObject;
end;
TJclBucket = class
public
Size: Integer;
Entries: array of TJclHashEntry;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclHashMap = class(TJclAbstractContainerBase, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclKeyOwner, IJclValueOwner,
IJclMap)
private
FOwnsKeys: Boolean;
FOwnsValues: Boolean;
protected
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function Hash(AObject: TObject): Integer;
function KeysEqual(A, B: TObject): Boolean;
function ValuesEqual(A, B: TObject): Boolean;
public
{ IJclKeyOwner }
function FreeKey(var Key: TObject): TObject;
function GetOwnsKeys: Boolean;
property OwnsKeys: Boolean read FOwnsKeys;
{ IJclValueOwner }
function FreeValue(var Value: TObject): TObject;
function GetOwnsValues: Boolean;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TJclBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean; AOwnsKeys: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclMap }
procedure Clear;
function ContainsKey(Key: TObject): Boolean;
function ContainsValue(Value: TObject): Boolean;
function Extract(Key: TObject): TObject;
function GetValue(Key: TObject): TObject;
function IsEmpty: Boolean;
function KeyOfValue(Value: TObject): TObject;
function KeySet: IJclSet;
function MapEquals(const AMap: IJclMap): Boolean;
procedure PutAll(const AMap: IJclMap);
procedure PutValue(Key: TObject; Value: TObject);
function Remove(Key: TObject): TObject;
function Size: Integer;
function Values: IJclCollection;
end;
{$IFDEF SUPPORTS_GENERICS}
TJclHashEntry<TKey,TValue> = record
Key: TKey;
Value: TValue;
end;
TJclBucket<TKey,TValue> = class
public
Size: Integer;
Entries: array of TJclHashEntry<TKey,TValue>;
procedure MoveArray(FromIndex, ToIndex, Count: Integer);
end;
TJclHashMap<TKey,TValue> = class(TJclAbstractContainerBase, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclGrowable, IJclPackable, IJclContainer, IJclPairOwner<TKey, TValue>,
IJclMap<TKey,TValue>)
protected
type
TBucket = TJclBucket<TKey,TValue>;
private
FOwnsKeys: Boolean;
FOwnsValues: Boolean;
protected
function Hash(const AKey: TKey): Integer; virtual; abstract;
function KeysEqual(const A, B: TKey): Boolean; virtual; abstract;
function ValuesEqual(const A, B: TValue): Boolean; virtual; abstract;
function CreateEmptyArrayList(ACapacity: Integer; AOwnsObjects: Boolean): IJclCollection<TValue>; virtual; abstract;
function CreateEmptyArraySet(ACapacity: Integer; AOwnsObjects: Boolean): IJclSet<TKey>; virtual; abstract;
public
{ IJclPairOwner }
function FreeKey(var Key: TKey): TKey;
function FreeValue(var Value: TValue): TValue;
function GetOwnsKeys: Boolean;
function GetOwnsValues: Boolean;
property OwnsKeys: Boolean read FOwnsKeys;
property OwnsValues: Boolean read FOwnsValues;
private
FBuckets: array of TBucket;
FHashFunction: TJclHashFunction;
protected
procedure AssignDataTo(Dest: TJclAbstractContainerBase); override;
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
public
constructor Create(ACapacity: Integer; AOwnsValues: Boolean; AOwnsKeys: Boolean);
destructor Destroy; override;
property HashFunction: TJclHashFunction read FHashFunction write FHashFunction;
{ IJclPackable }
procedure Pack; override;
procedure SetCapacity(Value: Integer); override;
{ IJclMap<TKey,TValue> }
procedure Clear;
function ContainsKey(const Key: TKey): Boolean;
function ContainsValue(const Value: TValue): Boolean;
function Extract(const Key: TKey): TValue;
function GetValue(const Key: TKey): TValue;
function IsEmpty: Boolean;
function KeyOfValue(const Value: TValue): TKey;
function KeySet: IJclSet<TKey>;
function MapEquals(const AMap: IJclMap<TKey,TValue>): Boolean;
procedure PutAll(const AMap: IJclMap<TKey,TValue>);
procedure PutValue(const Key: TKey; const Value: TValue);
function Remove(const Key: TKey): TValue;
function Size: Integer;
function Values: IJclCollection<TValue>;
end;
// E = external helper to compare and hash items
// KeyComparer is used only when getting KeySet
// GetHashCode and Equals methods of KeyEqualityComparer are used
// GetHashCode of ValueEqualityComparer is not used
TJclHashMapE<TKey, TValue> = class(TJclHashMap<TKey, TValue>, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclPackable, IJclContainer, IJclMap<TKey,TValue>, IJclPairOwner<TKey, TValue>)
protected
type
TArrayList = TJclArrayListE<TValue>;
TArraySet = TJclArraySetE<TKey>;
private
FKeyEqualityComparer: IJclEqualityComparer<TKey>;
FKeyHashConverter: IJclHashConverter<TKey>;
FKeyComparer: IJclComparer<TKey>;
FValueEqualityComparer: IJclEqualityComparer<TValue>;
protected
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
function Hash(const AKey: TKey): Integer; override;
function KeysEqual(const A, B: TKey): Boolean; override;
function ValuesEqual(const A, B: TValue): Boolean; override;
function CreateEmptyArrayList(ACapacity: Integer; AOwnsObjects: Boolean): IJclCollection<TValue>; override;
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function CreateEmptyArraySet(ACapacity: Integer; AOwnsObjects: Boolean): IJclSet<TKey>; override;
public
constructor Create(const AKeyEqualityComparer: IJclEqualityComparer<TKey>;
const AKeyHashConverter: IJclHashConverter<TKey>; const AValueEqualityComparer: IJclEqualityComparer<TValue>;
const AKeyComparer: IJclComparer<TKey>; ACapacity: Integer; AOwnsValues: Boolean; AOwnsKeys: Boolean);
property KeyEqualityComparer: IJclEqualityComparer<TKey> read FKeyEqualityComparer write FKeyEqualityComparer;
property KeyHashConverter: IJclHashConverter<TKey> read FKeyHashConverter write FKeyHashConverter;
property KeyComparer: IJclComparer<TKey> read FKeyComparer write FKeyComparer;
property ValueEqualityComparer: IJclEqualityComparer<TValue> read FValueEqualityComparer write FValueEqualityComparer;
end;
// F = Functions to compare and hash items
// KeyComparer is used only when getting KeySet
TJclHashMapF<TKey, TValue> = class(TJclHashMap<TKey, TValue>, {$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE}
IJclIntfCloneable, IJclCloneable, IJclPackable, IJclContainer, IJclMap<TKey,TValue>, IJclPairOwner<TKey, TValue>)
protected
type
TArrayList = TJclArrayListF<TValue>;
TArraySet = TJclArraySetF<TKey>;
private
FKeyEqualityCompare: TEqualityCompare<TKey>;
FKeyHash: THashConvert<TKey>;
FKeyCompare: TCompare<TKey>;
FValueEqualityCompare: TEqualityCompare<TValue>;
protected
procedure AssignPropertiesTo(Dest: TJclAbstractContainerBase); override;
function Hash(const AKey: TKey): Integer; override;
function KeysEqual(const A, B: TKey): Boolean; override;
function ValuesEqual(const A, B: TValue): Boolean; override;
function CreateEmptyArrayList(ACapacity: Integer; AOwnsObjects: Boolean): IJclCollection<TValue>; override;
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function CreateEmptyArraySet(ACapacity: Integer; AOwnsObjects: Boolean): IJclSet<TKey>; override;
public
constructor Create(AKeyEqualityCompare: TEqualityCompare<TKey>; AKeyHash: THashConvert<TKey>;
AValueEqualityCompare: TEqualityCompare<TValue>; AKeyCompare: TCompare<TKey>;
ACapacity: Integer; AOwnsValues: Boolean; AOwnsKeys: Boolean);
property KeyEqualityCompare: TEqualityCompare<TKey> read FKeyEqualityCompare write FKeyEqualityCompare;
property KeyCompare: TCompare<TKey> read FKeyCompare write FKeyCompare;
property KeyHash: THashConvert<TKey> read FKeyHash write FKeyHash;
property ValueEqualityCompare: TEqualityCompare<TValue> read FValueEqualityCompare write FValueEqualityCompare;
end;
// I = items can compare themselves to an other, items can create hash value from themselves
TJclHashMapI<TKey: IComparable<TKey>, IEquatable<TKey>, IHashable; TValue: IEquatable<TValue>> = class(TJclHashMap<TKey, TValue>,
{$IFDEF THREADSAFE} IJclLockable, {$ENDIF THREADSAFE} IJclIntfCloneable, IJclCloneable, IJclPackable, IJclContainer,
IJclMap<TKey,TValue>, IJclPairOwner<TKey, TValue>)
protected
type
TArrayList = TJclArrayListI<TValue>;
TArraySet = TJclArraySetI<TKey>;
protected
function Hash(const AKey: TKey): Integer; override;
function KeysEqual(const A, B: TKey): Boolean; override;
function ValuesEqual(const A, B: TValue): Boolean; override;
function CreateEmptyArrayList(ACapacity: Integer; AOwnsObjects: Boolean): IJclCollection<TValue>; override;
function CreateEmptyContainer: TJclAbstractContainerBase; override;
function CreateEmptyArraySet(ACapacity: Integer; AOwnsObjects: Boolean): IJclSet<TKey>; override;
end;
{$ENDIF SUPPORTS_GENERICS}
function HashMul(Key, Range: Integer): Integer;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jcl.svn.sourceforge.net/svnroot/jcl/tags/JCL-2.1-Build3536/jcl/source/common/JclHashMaps.pas $';
Revision: '$Revision: 2997 $';
Date: '$Date: 2009-09-12 14:21:23 +0200 (sam., 12 sept. 2009) $';
LogPath: 'JCL\source\common';
Extra: '';
Data: nil
);
{$ENDIF UNITVERSIONING}
implementation
uses
SysUtils,
JclResources;
function HashMul(Key, Range: Integer): Integer;
// return a value between 0 and (Range-1) based on integer-hash Key
const
A = 0.6180339887; // (sqrt(5) - 1) / 2
begin
Result := Trunc(Range * (Frac(Abs(Key * A))));
end;
//=== { TJclIntfIntfBucket } ==========================================
procedure TJclIntfIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfIntfHashMap } ==========================================
constructor TJclIntfIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfIntfBucket;
ADest: TJclIntfIntfHashMap;
AMap: IJclIntfIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfIntfHashMap then
begin
ADest := TJclIntfIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfIntfHashMap then
TJclIntfIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclIntfIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.Extract(const Key: IInterface): IInterface;
var
Bucket: TJclIntfIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.GetValue(const Key: IInterface): IInterface;
var
I: Integer;
Bucket: TJclIntfIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfIntfHashMap.KeyOfValue(const Value: IInterface): IInterface;
var
I, J: Integer;
Bucket: TJclIntfIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.MapEquals(const AMap: IJclIntfIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntfHashMap.PutAll(const AMap: IJclIntfIntfMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntfHashMap.PutValue(const Key: IInterface; const Value: IInterface);
var
Index: Integer;
Bucket: TJclIntfIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.Remove(const Key: IInterface): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclIntfIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfIntfHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclIntfIntfHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclIntfIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclAnsiStrIntfBucket } ==========================================
procedure TJclAnsiStrIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclAnsiStrIntfHashMap } ==========================================
constructor TJclAnsiStrIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclAnsiStrIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclAnsiStrIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclAnsiStrIntfBucket;
ADest: TJclAnsiStrIntfHashMap;
AMap: IJclAnsiStrIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclAnsiStrIntfHashMap then
begin
ADest := TJclAnsiStrIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclAnsiStrIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclAnsiStrIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclAnsiStrIntfHashMap then
TJclAnsiStrIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclAnsiStrIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclAnsiStrIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.ContainsKey(const Key: AnsiString): Boolean;
var
I: Integer;
Bucket: TJclAnsiStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclAnsiStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.Extract(const Key: AnsiString): IInterface;
var
Bucket: TJclAnsiStrIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.GetValue(const Key: AnsiString): IInterface;
var
I: Integer;
Bucket: TJclAnsiStrIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclAnsiStrIntfHashMap.KeyOfValue(const Value: IInterface): AnsiString;
var
I, J: Integer;
Bucket: TJclAnsiStrIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.KeySet: IJclAnsiStrSet;
var
I, J: Integer;
Bucket: TJclAnsiStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclAnsiStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.MapEquals(const AMap: IJclAnsiStrIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclAnsiStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclAnsiStrIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrIntfHashMap.PutAll(const AMap: IJclAnsiStrIntfMap);
var
It: IJclAnsiStrIterator;
Key: AnsiString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrIntfHashMap.PutValue(const Key: AnsiString; const Value: IInterface);
var
Index: Integer;
Bucket: TJclAnsiStrIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclAnsiStrIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.Remove(const Key: AnsiString): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclAnsiStrIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclAnsiStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclAnsiStrIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclAnsiStrIntfHashMap.FreeKey(var Key: AnsiString): AnsiString;
begin
Result := Key;
Key := '';
end;
function TJclAnsiStrIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclAnsiStrIntfHashMap.KeysEqual(const A, B: AnsiString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclAnsiStrIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfAnsiStrBucket } ==========================================
procedure TJclIntfAnsiStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfAnsiStrHashMap } ==========================================
constructor TJclIntfAnsiStrHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfAnsiStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfAnsiStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfAnsiStrBucket;
ADest: TJclIntfAnsiStrHashMap;
AMap: IJclIntfAnsiStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfAnsiStrHashMap then
begin
ADest := TJclIntfAnsiStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfAnsiStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfAnsiStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfAnsiStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfAnsiStrHashMap then
TJclIntfAnsiStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfAnsiStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfAnsiStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.ContainsValue(const Value: AnsiString): Boolean;
var
I, J: Integer;
Bucket: TJclIntfAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.Extract(const Key: IInterface): AnsiString;
var
Bucket: TJclIntfAnsiStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := '';
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.GetValue(const Key: IInterface): AnsiString;
var
I: Integer;
Bucket: TJclIntfAnsiStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfAnsiStrHashMap.KeyOfValue(const Value: AnsiString): IInterface;
var
I, J: Integer;
Bucket: TJclIntfAnsiStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.MapEquals(const AMap: IJclIntfAnsiStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfAnsiStrHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfAnsiStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfAnsiStrHashMap.PutAll(const AMap: IJclIntfAnsiStrMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfAnsiStrHashMap.PutValue(const Key: IInterface; const Value: AnsiString);
var
Index: Integer;
Bucket: TJclIntfAnsiStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, '')) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfAnsiStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.Remove(const Key: IInterface): AnsiString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfAnsiStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfAnsiStrHashMap.Values: IJclAnsiStrCollection;
var
I, J: Integer;
Bucket: TJclIntfAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclAnsiStrArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfAnsiStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfAnsiStrHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfAnsiStrHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfAnsiStrHashMap.FreeValue(var Value: AnsiString): AnsiString;
begin
Result := Value;
Value := '';
end;
function TJclIntfAnsiStrHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfAnsiStrHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfAnsiStrHashMap.ValuesEqual(const A, B: AnsiString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclAnsiStrAnsiStrBucket } ==========================================
procedure TJclAnsiStrAnsiStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclAnsiStrAnsiStrHashMap } ==========================================
constructor TJclAnsiStrAnsiStrHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclAnsiStrAnsiStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclAnsiStrAnsiStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclAnsiStrAnsiStrBucket;
ADest: TJclAnsiStrAnsiStrHashMap;
AMap: IJclAnsiStrAnsiStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclAnsiStrAnsiStrHashMap then
begin
ADest := TJclAnsiStrAnsiStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclAnsiStrAnsiStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclAnsiStrAnsiStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrAnsiStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclAnsiStrAnsiStrHashMap then
TJclAnsiStrAnsiStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclAnsiStrAnsiStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.ContainsKey(const Key: AnsiString): Boolean;
var
I: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.ContainsValue(const Value: AnsiString): Boolean;
var
I, J: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.Extract(const Key: AnsiString): AnsiString;
var
Bucket: TJclAnsiStrAnsiStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := '';
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.GetValue(const Key: AnsiString): AnsiString;
var
I: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclAnsiStrAnsiStrHashMap.KeyOfValue(const Value: AnsiString): AnsiString;
var
I, J: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.KeySet: IJclAnsiStrSet;
var
I, J: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclAnsiStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.MapEquals(const AMap: IJclAnsiStrAnsiStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrAnsiStrHashMap.Pack;
var
I: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrAnsiStrHashMap.PutAll(const AMap: IJclAnsiStrAnsiStrMap);
var
It: IJclAnsiStrIterator;
Key: AnsiString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrAnsiStrHashMap.PutValue(const Key: AnsiString; const Value: AnsiString);
var
Index: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, '')) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclAnsiStrAnsiStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.Remove(const Key: AnsiString): AnsiString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrAnsiStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclAnsiStrAnsiStrHashMap.Values: IJclAnsiStrCollection;
var
I, J: Integer;
Bucket: TJclAnsiStrAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclAnsiStrArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrAnsiStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclAnsiStrAnsiStrHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclAnsiStrAnsiStrHashMap.FreeKey(var Key: AnsiString): AnsiString;
begin
Result := Key;
Key := '';
end;
function TJclAnsiStrAnsiStrHashMap.FreeValue(var Value: AnsiString): AnsiString;
begin
Result := Value;
Value := '';
end;
function TJclAnsiStrAnsiStrHashMap.KeysEqual(const A, B: AnsiString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclAnsiStrAnsiStrHashMap.ValuesEqual(const A, B: AnsiString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclWideStrIntfBucket } ==========================================
procedure TJclWideStrIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclWideStrIntfHashMap } ==========================================
constructor TJclWideStrIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclWideStrIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclWideStrIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclWideStrIntfBucket;
ADest: TJclWideStrIntfHashMap;
AMap: IJclWideStrIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclWideStrIntfHashMap then
begin
ADest := TJclWideStrIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclWideStrIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclWideStrIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclWideStrIntfHashMap then
TJclWideStrIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclWideStrIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclWideStrIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.ContainsKey(const Key: WideString): Boolean;
var
I: Integer;
Bucket: TJclWideStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclWideStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.Extract(const Key: WideString): IInterface;
var
Bucket: TJclWideStrIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.GetValue(const Key: WideString): IInterface;
var
I: Integer;
Bucket: TJclWideStrIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclWideStrIntfHashMap.KeyOfValue(const Value: IInterface): WideString;
var
I, J: Integer;
Bucket: TJclWideStrIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.KeySet: IJclWideStrSet;
var
I, J: Integer;
Bucket: TJclWideStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclWideStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.MapEquals(const AMap: IJclWideStrIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclWideStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclWideStrIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrIntfHashMap.PutAll(const AMap: IJclWideStrIntfMap);
var
It: IJclWideStrIterator;
Key: WideString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrIntfHashMap.PutValue(const Key: WideString; const Value: IInterface);
var
Index: Integer;
Bucket: TJclWideStrIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclWideStrIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.Remove(const Key: WideString): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclWideStrIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclWideStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclWideStrIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclWideStrIntfHashMap.FreeKey(var Key: WideString): WideString;
begin
Result := Key;
Key := '';
end;
function TJclWideStrIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclWideStrIntfHashMap.KeysEqual(const A, B: WideString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclWideStrIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfWideStrBucket } ==========================================
procedure TJclIntfWideStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfWideStrHashMap } ==========================================
constructor TJclIntfWideStrHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfWideStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfWideStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfWideStrBucket;
ADest: TJclIntfWideStrHashMap;
AMap: IJclIntfWideStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfWideStrHashMap then
begin
ADest := TJclIntfWideStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfWideStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfWideStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfWideStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfWideStrHashMap then
TJclIntfWideStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfWideStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfWideStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.ContainsValue(const Value: WideString): Boolean;
var
I, J: Integer;
Bucket: TJclIntfWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.Extract(const Key: IInterface): WideString;
var
Bucket: TJclIntfWideStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := '';
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.GetValue(const Key: IInterface): WideString;
var
I: Integer;
Bucket: TJclIntfWideStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfWideStrHashMap.KeyOfValue(const Value: WideString): IInterface;
var
I, J: Integer;
Bucket: TJclIntfWideStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.MapEquals(const AMap: IJclIntfWideStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfWideStrHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfWideStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfWideStrHashMap.PutAll(const AMap: IJclIntfWideStrMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfWideStrHashMap.PutValue(const Key: IInterface; const Value: WideString);
var
Index: Integer;
Bucket: TJclIntfWideStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, '')) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfWideStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.Remove(const Key: IInterface): WideString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfWideStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfWideStrHashMap.Values: IJclWideStrCollection;
var
I, J: Integer;
Bucket: TJclIntfWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclWideStrArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfWideStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfWideStrHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfWideStrHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfWideStrHashMap.FreeValue(var Value: WideString): WideString;
begin
Result := Value;
Value := '';
end;
function TJclIntfWideStrHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfWideStrHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfWideStrHashMap.ValuesEqual(const A, B: WideString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclWideStrWideStrBucket } ==========================================
procedure TJclWideStrWideStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclWideStrWideStrHashMap } ==========================================
constructor TJclWideStrWideStrHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclWideStrWideStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclWideStrWideStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclWideStrWideStrBucket;
ADest: TJclWideStrWideStrHashMap;
AMap: IJclWideStrWideStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclWideStrWideStrHashMap then
begin
ADest := TJclWideStrWideStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclWideStrWideStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclWideStrWideStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrWideStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclWideStrWideStrHashMap then
TJclWideStrWideStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclWideStrWideStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclWideStrWideStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.ContainsKey(const Key: WideString): Boolean;
var
I: Integer;
Bucket: TJclWideStrWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.ContainsValue(const Value: WideString): Boolean;
var
I, J: Integer;
Bucket: TJclWideStrWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.Extract(const Key: WideString): WideString;
var
Bucket: TJclWideStrWideStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := '';
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.GetValue(const Key: WideString): WideString;
var
I: Integer;
Bucket: TJclWideStrWideStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclWideStrWideStrHashMap.KeyOfValue(const Value: WideString): WideString;
var
I, J: Integer;
Bucket: TJclWideStrWideStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.KeySet: IJclWideStrSet;
var
I, J: Integer;
Bucket: TJclWideStrWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclWideStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.MapEquals(const AMap: IJclWideStrWideStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclWideStrWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrWideStrHashMap.Pack;
var
I: Integer;
Bucket: TJclWideStrWideStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrWideStrHashMap.PutAll(const AMap: IJclWideStrWideStrMap);
var
It: IJclWideStrIterator;
Key: WideString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrWideStrHashMap.PutValue(const Key: WideString; const Value: WideString);
var
Index: Integer;
Bucket: TJclWideStrWideStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, '')) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclWideStrWideStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.Remove(const Key: WideString): WideString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrWideStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclWideStrWideStrHashMap.Values: IJclWideStrCollection;
var
I, J: Integer;
Bucket: TJclWideStrWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclWideStrArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrWideStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclWideStrWideStrHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclWideStrWideStrHashMap.FreeKey(var Key: WideString): WideString;
begin
Result := Key;
Key := '';
end;
function TJclWideStrWideStrHashMap.FreeValue(var Value: WideString): WideString;
begin
Result := Value;
Value := '';
end;
function TJclWideStrWideStrHashMap.KeysEqual(const A, B: WideString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclWideStrWideStrHashMap.ValuesEqual(const A, B: Widestring): Boolean;
begin
Result := ItemsEqual(A, B);
end;
{$IFDEF SUPPORTS_UNICODE_STRING}
//=== { TJclUnicodeStrIntfBucket } ==========================================
procedure TJclUnicodeStrIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclUnicodeStrIntfHashMap } ==========================================
constructor TJclUnicodeStrIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclUnicodeStrIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclUnicodeStrIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclUnicodeStrIntfBucket;
ADest: TJclUnicodeStrIntfHashMap;
AMap: IJclUnicodeStrIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclUnicodeStrIntfHashMap then
begin
ADest := TJclUnicodeStrIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclUnicodeStrIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclUnicodeStrIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclUnicodeStrIntfHashMap then
TJclUnicodeStrIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclUnicodeStrIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclUnicodeStrIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.ContainsKey(const Key: UnicodeString): Boolean;
var
I: Integer;
Bucket: TJclUnicodeStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclUnicodeStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.Extract(const Key: UnicodeString): IInterface;
var
Bucket: TJclUnicodeStrIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.GetValue(const Key: UnicodeString): IInterface;
var
I: Integer;
Bucket: TJclUnicodeStrIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclUnicodeStrIntfHashMap.KeyOfValue(const Value: IInterface): UnicodeString;
var
I, J: Integer;
Bucket: TJclUnicodeStrIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.KeySet: IJclUnicodeStrSet;
var
I, J: Integer;
Bucket: TJclUnicodeStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclUnicodeStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.MapEquals(const AMap: IJclUnicodeStrIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclUnicodeStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclUnicodeStrIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrIntfHashMap.PutAll(const AMap: IJclUnicodeStrIntfMap);
var
It: IJclUnicodeStrIterator;
Key: UnicodeString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrIntfHashMap.PutValue(const Key: UnicodeString; const Value: IInterface);
var
Index: Integer;
Bucket: TJclUnicodeStrIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclUnicodeStrIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.Remove(const Key: UnicodeString): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclUnicodeStrIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclUnicodeStrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclUnicodeStrIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclUnicodeStrIntfHashMap.FreeKey(var Key: UnicodeString): UnicodeString;
begin
Result := Key;
Key := '';
end;
function TJclUnicodeStrIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclUnicodeStrIntfHashMap.KeysEqual(const A, B: UnicodeString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclUnicodeStrIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfUnicodeStrBucket } ==========================================
procedure TJclIntfUnicodeStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfUnicodeStrHashMap } ==========================================
constructor TJclIntfUnicodeStrHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfUnicodeStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfUnicodeStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfUnicodeStrBucket;
ADest: TJclIntfUnicodeStrHashMap;
AMap: IJclIntfUnicodeStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfUnicodeStrHashMap then
begin
ADest := TJclIntfUnicodeStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfUnicodeStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfUnicodeStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfUnicodeStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfUnicodeStrHashMap then
TJclIntfUnicodeStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfUnicodeStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfUnicodeStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.ContainsValue(const Value: UnicodeString): Boolean;
var
I, J: Integer;
Bucket: TJclIntfUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.Extract(const Key: IInterface): UnicodeString;
var
Bucket: TJclIntfUnicodeStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := '';
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.GetValue(const Key: IInterface): UnicodeString;
var
I: Integer;
Bucket: TJclIntfUnicodeStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfUnicodeStrHashMap.KeyOfValue(const Value: UnicodeString): IInterface;
var
I, J: Integer;
Bucket: TJclIntfUnicodeStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.MapEquals(const AMap: IJclIntfUnicodeStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfUnicodeStrHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfUnicodeStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfUnicodeStrHashMap.PutAll(const AMap: IJclIntfUnicodeStrMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfUnicodeStrHashMap.PutValue(const Key: IInterface; const Value: UnicodeString);
var
Index: Integer;
Bucket: TJclIntfUnicodeStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, '')) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfUnicodeStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.Remove(const Key: IInterface): UnicodeString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfUnicodeStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfUnicodeStrHashMap.Values: IJclUnicodeStrCollection;
var
I, J: Integer;
Bucket: TJclIntfUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclUnicodeStrArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfUnicodeStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfUnicodeStrHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfUnicodeStrHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfUnicodeStrHashMap.FreeValue(var Value: UnicodeString): UnicodeString;
begin
Result := Value;
Value := '';
end;
function TJclIntfUnicodeStrHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfUnicodeStrHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfUnicodeStrHashMap.ValuesEqual(const A, B: UnicodeString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclUnicodeStrUnicodeStrBucket } ==========================================
procedure TJclUnicodeStrUnicodeStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclUnicodeStrUnicodeStrHashMap } ==========================================
constructor TJclUnicodeStrUnicodeStrHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclUnicodeStrUnicodeStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclUnicodeStrUnicodeStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclUnicodeStrUnicodeStrBucket;
ADest: TJclUnicodeStrUnicodeStrHashMap;
AMap: IJclUnicodeStrUnicodeStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclUnicodeStrUnicodeStrHashMap then
begin
ADest := TJclUnicodeStrUnicodeStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclUnicodeStrUnicodeStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclUnicodeStrUnicodeStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrUnicodeStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclUnicodeStrUnicodeStrHashMap then
TJclUnicodeStrUnicodeStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclUnicodeStrUnicodeStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.ContainsKey(const Key: UnicodeString): Boolean;
var
I: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.ContainsValue(const Value: UnicodeString): Boolean;
var
I, J: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.Extract(const Key: UnicodeString): UnicodeString;
var
Bucket: TJclUnicodeStrUnicodeStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := '';
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.GetValue(const Key: UnicodeString): UnicodeString;
var
I: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclUnicodeStrUnicodeStrHashMap.KeyOfValue(const Value: UnicodeString): UnicodeString;
var
I, J: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.KeySet: IJclUnicodeStrSet;
var
I, J: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclUnicodeStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.MapEquals(const AMap: IJclUnicodeStrUnicodeStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrUnicodeStrHashMap.Pack;
var
I: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrUnicodeStrHashMap.PutAll(const AMap: IJclUnicodeStrUnicodeStrMap);
var
It: IJclUnicodeStrIterator;
Key: UnicodeString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrUnicodeStrHashMap.PutValue(const Key: UnicodeString; const Value: UnicodeString);
var
Index: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, '')) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclUnicodeStrUnicodeStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.Remove(const Key: UnicodeString): UnicodeString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrUnicodeStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclUnicodeStrUnicodeStrHashMap.Values: IJclUnicodeStrCollection;
var
I, J: Integer;
Bucket: TJclUnicodeStrUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclUnicodeStrArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrUnicodeStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclUnicodeStrUnicodeStrHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclUnicodeStrUnicodeStrHashMap.FreeKey(var Key: UnicodeString): UnicodeString;
begin
Result := Key;
Key := '';
end;
function TJclUnicodeStrUnicodeStrHashMap.FreeValue(var Value: UnicodeString): UnicodeString;
begin
Result := Value;
Value := '';
end;
function TJclUnicodeStrUnicodeStrHashMap.KeysEqual(const A, B: UnicodeString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclUnicodeStrUnicodeStrHashMap.ValuesEqual(const A, B: Unicodestring): Boolean;
begin
Result := ItemsEqual(A, B);
end;
{$ENDIF SUPPORTS_UNICODE_STRING}
//=== { TJclSingleIntfBucket } ==========================================
procedure TJclSingleIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclSingleIntfHashMap } ==========================================
constructor TJclSingleIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclSingleIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclSingleIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclSingleIntfBucket;
ADest: TJclSingleIntfHashMap;
AMap: IJclSingleIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclSingleIntfHashMap then
begin
ADest := TJclSingleIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclSingleIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclSingleIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclSingleIntfHashMap then
TJclSingleIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclSingleIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclSingleIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.ContainsKey(const Key: Single): Boolean;
var
I: Integer;
Bucket: TJclSingleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclSingleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.Extract(const Key: Single): IInterface;
var
Bucket: TJclSingleIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.GetValue(const Key: Single): IInterface;
var
I: Integer;
Bucket: TJclSingleIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclSingleIntfHashMap.KeyOfValue(const Value: IInterface): Single;
var
I, J: Integer;
Bucket: TJclSingleIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.KeySet: IJclSingleSet;
var
I, J: Integer;
Bucket: TJclSingleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclSingleArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.MapEquals(const AMap: IJclSingleIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclSingleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclSingleIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleIntfHashMap.PutAll(const AMap: IJclSingleIntfMap);
var
It: IJclSingleIterator;
Key: Single;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleIntfHashMap.PutValue(const Key: Single; const Value: IInterface);
var
Index: Integer;
Bucket: TJclSingleIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclSingleIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.Remove(const Key: Single): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclSingleIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclSingleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclSingleIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclSingleIntfHashMap.FreeKey(var Key: Single): Single;
begin
Result := Key;
Key := 0.0;
end;
function TJclSingleIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclSingleIntfHashMap.KeysEqual(const A, B: Single): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclSingleIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfSingleBucket } ==========================================
procedure TJclIntfSingleBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfSingleHashMap } ==========================================
constructor TJclIntfSingleHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfSingleHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfSingleHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfSingleBucket;
ADest: TJclIntfSingleHashMap;
AMap: IJclIntfSingleMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfSingleHashMap then
begin
ADest := TJclIntfSingleHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfSingleBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfSingleMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfSingleHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfSingleHashMap then
TJclIntfSingleHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfSingleHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfSingleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.ContainsValue(const Value: Single): Boolean;
var
I, J: Integer;
Bucket: TJclIntfSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.Extract(const Key: IInterface): Single;
var
Bucket: TJclIntfSingleBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0.0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.GetValue(const Key: IInterface): Single;
var
I: Integer;
Bucket: TJclIntfSingleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfSingleHashMap.KeyOfValue(const Value: Single): IInterface;
var
I, J: Integer;
Bucket: TJclIntfSingleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.MapEquals(const AMap: IJclIntfSingleMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfSingleHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfSingleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfSingleHashMap.PutAll(const AMap: IJclIntfSingleMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfSingleHashMap.PutValue(const Key: IInterface; const Value: Single);
var
Index: Integer;
Bucket: TJclIntfSingleBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, 0.0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfSingleBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.Remove(const Key: IInterface): Single;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfSingleHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfSingleHashMap.Values: IJclSingleCollection;
var
I, J: Integer;
Bucket: TJclIntfSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclSingleArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfSingleHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfSingleHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfSingleHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfSingleHashMap.FreeValue(var Value: Single): Single;
begin
Result := Value;
Value := 0.0;
end;
function TJclIntfSingleHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfSingleHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfSingleHashMap.ValuesEqual(const A, B: Single): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclSingleSingleBucket } ==========================================
procedure TJclSingleSingleBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclSingleSingleHashMap } ==========================================
constructor TJclSingleSingleHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclSingleSingleHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclSingleSingleHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclSingleSingleBucket;
ADest: TJclSingleSingleHashMap;
AMap: IJclSingleSingleMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclSingleSingleHashMap then
begin
ADest := TJclSingleSingleHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclSingleSingleBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclSingleSingleMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleSingleHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclSingleSingleHashMap then
TJclSingleSingleHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclSingleSingleHashMap.Clear;
var
I, J: Integer;
Bucket: TJclSingleSingleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.ContainsKey(const Key: Single): Boolean;
var
I: Integer;
Bucket: TJclSingleSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.ContainsValue(const Value: Single): Boolean;
var
I, J: Integer;
Bucket: TJclSingleSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.Extract(const Key: Single): Single;
var
Bucket: TJclSingleSingleBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0.0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.GetValue(const Key: Single): Single;
var
I: Integer;
Bucket: TJclSingleSingleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclSingleSingleHashMap.KeyOfValue(const Value: Single): Single;
var
I, J: Integer;
Bucket: TJclSingleSingleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.KeySet: IJclSingleSet;
var
I, J: Integer;
Bucket: TJclSingleSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclSingleArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.MapEquals(const AMap: IJclSingleSingleMap): Boolean;
var
I, J: Integer;
Bucket: TJclSingleSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleSingleHashMap.Pack;
var
I: Integer;
Bucket: TJclSingleSingleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleSingleHashMap.PutAll(const AMap: IJclSingleSingleMap);
var
It: IJclSingleIterator;
Key: Single;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleSingleHashMap.PutValue(const Key: Single; const Value: Single);
var
Index: Integer;
Bucket: TJclSingleSingleBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, 0.0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclSingleSingleBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.Remove(const Key: Single): Single;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleSingleHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclSingleSingleHashMap.Values: IJclSingleCollection;
var
I, J: Integer;
Bucket: TJclSingleSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclSingleArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleSingleHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclSingleSingleHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclSingleSingleHashMap.FreeKey(var Key: Single): Single;
begin
Result := Key;
Key := 0.0;
end;
function TJclSingleSingleHashMap.FreeValue(var Value: Single): Single;
begin
Result := Value;
Value := 0.0;
end;
function TJclSingleSingleHashMap.KeysEqual(const A, B: Single): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclSingleSingleHashMap.ValuesEqual(const A, B: Single): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclDoubleIntfBucket } ==========================================
procedure TJclDoubleIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclDoubleIntfHashMap } ==========================================
constructor TJclDoubleIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclDoubleIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclDoubleIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclDoubleIntfBucket;
ADest: TJclDoubleIntfHashMap;
AMap: IJclDoubleIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclDoubleIntfHashMap then
begin
ADest := TJclDoubleIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclDoubleIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclDoubleIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclDoubleIntfHashMap then
TJclDoubleIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclDoubleIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclDoubleIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.ContainsKey(const Key: Double): Boolean;
var
I: Integer;
Bucket: TJclDoubleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclDoubleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.Extract(const Key: Double): IInterface;
var
Bucket: TJclDoubleIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.GetValue(const Key: Double): IInterface;
var
I: Integer;
Bucket: TJclDoubleIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclDoubleIntfHashMap.KeyOfValue(const Value: IInterface): Double;
var
I, J: Integer;
Bucket: TJclDoubleIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.KeySet: IJclDoubleSet;
var
I, J: Integer;
Bucket: TJclDoubleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclDoubleArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.MapEquals(const AMap: IJclDoubleIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclDoubleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclDoubleIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleIntfHashMap.PutAll(const AMap: IJclDoubleIntfMap);
var
It: IJclDoubleIterator;
Key: Double;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleIntfHashMap.PutValue(const Key: Double; const Value: IInterface);
var
Index: Integer;
Bucket: TJclDoubleIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclDoubleIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.Remove(const Key: Double): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclDoubleIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclDoubleIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclDoubleIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclDoubleIntfHashMap.FreeKey(var Key: Double): Double;
begin
Result := Key;
Key := 0.0;
end;
function TJclDoubleIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclDoubleIntfHashMap.KeysEqual(const A, B: Double): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclDoubleIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfDoubleBucket } ==========================================
procedure TJclIntfDoubleBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfDoubleHashMap } ==========================================
constructor TJclIntfDoubleHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfDoubleHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfDoubleHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfDoubleBucket;
ADest: TJclIntfDoubleHashMap;
AMap: IJclIntfDoubleMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfDoubleHashMap then
begin
ADest := TJclIntfDoubleHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfDoubleBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfDoubleMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfDoubleHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfDoubleHashMap then
TJclIntfDoubleHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfDoubleHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfDoubleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.ContainsValue(const Value: Double): Boolean;
var
I, J: Integer;
Bucket: TJclIntfDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.Extract(const Key: IInterface): Double;
var
Bucket: TJclIntfDoubleBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0.0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.GetValue(const Key: IInterface): Double;
var
I: Integer;
Bucket: TJclIntfDoubleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfDoubleHashMap.KeyOfValue(const Value: Double): IInterface;
var
I, J: Integer;
Bucket: TJclIntfDoubleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.MapEquals(const AMap: IJclIntfDoubleMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfDoubleHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfDoubleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfDoubleHashMap.PutAll(const AMap: IJclIntfDoubleMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfDoubleHashMap.PutValue(const Key: IInterface; const Value: Double);
var
Index: Integer;
Bucket: TJclIntfDoubleBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, 0.0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfDoubleBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.Remove(const Key: IInterface): Double;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfDoubleHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfDoubleHashMap.Values: IJclDoubleCollection;
var
I, J: Integer;
Bucket: TJclIntfDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclDoubleArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfDoubleHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfDoubleHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfDoubleHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfDoubleHashMap.FreeValue(var Value: Double): Double;
begin
Result := Value;
Value := 0.0;
end;
function TJclIntfDoubleHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfDoubleHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfDoubleHashMap.ValuesEqual(const A, B: Double): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclDoubleDoubleBucket } ==========================================
procedure TJclDoubleDoubleBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclDoubleDoubleHashMap } ==========================================
constructor TJclDoubleDoubleHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclDoubleDoubleHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclDoubleDoubleHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclDoubleDoubleBucket;
ADest: TJclDoubleDoubleHashMap;
AMap: IJclDoubleDoubleMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclDoubleDoubleHashMap then
begin
ADest := TJclDoubleDoubleHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclDoubleDoubleBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclDoubleDoubleMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleDoubleHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclDoubleDoubleHashMap then
TJclDoubleDoubleHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclDoubleDoubleHashMap.Clear;
var
I, J: Integer;
Bucket: TJclDoubleDoubleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.ContainsKey(const Key: Double): Boolean;
var
I: Integer;
Bucket: TJclDoubleDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.ContainsValue(const Value: Double): Boolean;
var
I, J: Integer;
Bucket: TJclDoubleDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.Extract(const Key: Double): Double;
var
Bucket: TJclDoubleDoubleBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0.0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.GetValue(const Key: Double): Double;
var
I: Integer;
Bucket: TJclDoubleDoubleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclDoubleDoubleHashMap.KeyOfValue(const Value: Double): Double;
var
I, J: Integer;
Bucket: TJclDoubleDoubleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.KeySet: IJclDoubleSet;
var
I, J: Integer;
Bucket: TJclDoubleDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclDoubleArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.MapEquals(const AMap: IJclDoubleDoubleMap): Boolean;
var
I, J: Integer;
Bucket: TJclDoubleDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleDoubleHashMap.Pack;
var
I: Integer;
Bucket: TJclDoubleDoubleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleDoubleHashMap.PutAll(const AMap: IJclDoubleDoubleMap);
var
It: IJclDoubleIterator;
Key: Double;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleDoubleHashMap.PutValue(const Key: Double; const Value: Double);
var
Index: Integer;
Bucket: TJclDoubleDoubleBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, 0.0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclDoubleDoubleBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.Remove(const Key: Double): Double;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleDoubleHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclDoubleDoubleHashMap.Values: IJclDoubleCollection;
var
I, J: Integer;
Bucket: TJclDoubleDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclDoubleArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleDoubleHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclDoubleDoubleHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclDoubleDoubleHashMap.FreeKey(var Key: Double): Double;
begin
Result := Key;
Key := 0.0;
end;
function TJclDoubleDoubleHashMap.FreeValue(var Value: Double): Double;
begin
Result := Value;
Value := 0.0;
end;
function TJclDoubleDoubleHashMap.KeysEqual(const A, B: Double): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclDoubleDoubleHashMap.ValuesEqual(const A, B: Double): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclExtendedIntfBucket } ==========================================
procedure TJclExtendedIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclExtendedIntfHashMap } ==========================================
constructor TJclExtendedIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclExtendedIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclExtendedIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclExtendedIntfBucket;
ADest: TJclExtendedIntfHashMap;
AMap: IJclExtendedIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclExtendedIntfHashMap then
begin
ADest := TJclExtendedIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclExtendedIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclExtendedIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclExtendedIntfHashMap then
TJclExtendedIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclExtendedIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclExtendedIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.ContainsKey(const Key: Extended): Boolean;
var
I: Integer;
Bucket: TJclExtendedIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclExtendedIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.Extract(const Key: Extended): IInterface;
var
Bucket: TJclExtendedIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.GetValue(const Key: Extended): IInterface;
var
I: Integer;
Bucket: TJclExtendedIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclExtendedIntfHashMap.KeyOfValue(const Value: IInterface): Extended;
var
I, J: Integer;
Bucket: TJclExtendedIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.KeySet: IJclExtendedSet;
var
I, J: Integer;
Bucket: TJclExtendedIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclExtendedArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.MapEquals(const AMap: IJclExtendedIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclExtendedIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclExtendedIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedIntfHashMap.PutAll(const AMap: IJclExtendedIntfMap);
var
It: IJclExtendedIterator;
Key: Extended;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedIntfHashMap.PutValue(const Key: Extended; const Value: IInterface);
var
Index: Integer;
Bucket: TJclExtendedIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclExtendedIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.Remove(const Key: Extended): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclExtendedIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclExtendedIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclExtendedIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclExtendedIntfHashMap.FreeKey(var Key: Extended): Extended;
begin
Result := Key;
Key := 0.0;
end;
function TJclExtendedIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclExtendedIntfHashMap.KeysEqual(const A, B: Extended): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclExtendedIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfExtendedBucket } ==========================================
procedure TJclIntfExtendedBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfExtendedHashMap } ==========================================
constructor TJclIntfExtendedHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfExtendedHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfExtendedHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfExtendedBucket;
ADest: TJclIntfExtendedHashMap;
AMap: IJclIntfExtendedMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfExtendedHashMap then
begin
ADest := TJclIntfExtendedHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfExtendedBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfExtendedMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfExtendedHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfExtendedHashMap then
TJclIntfExtendedHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfExtendedHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfExtendedBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.ContainsValue(const Value: Extended): Boolean;
var
I, J: Integer;
Bucket: TJclIntfExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.Extract(const Key: IInterface): Extended;
var
Bucket: TJclIntfExtendedBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0.0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.GetValue(const Key: IInterface): Extended;
var
I: Integer;
Bucket: TJclIntfExtendedBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfExtendedHashMap.KeyOfValue(const Value: Extended): IInterface;
var
I, J: Integer;
Bucket: TJclIntfExtendedBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.MapEquals(const AMap: IJclIntfExtendedMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfExtendedHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfExtendedBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfExtendedHashMap.PutAll(const AMap: IJclIntfExtendedMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfExtendedHashMap.PutValue(const Key: IInterface; const Value: Extended);
var
Index: Integer;
Bucket: TJclIntfExtendedBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, 0.0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfExtendedBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.Remove(const Key: IInterface): Extended;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfExtendedHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfExtendedHashMap.Values: IJclExtendedCollection;
var
I, J: Integer;
Bucket: TJclIntfExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclExtendedArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfExtendedHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfExtendedHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfExtendedHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfExtendedHashMap.FreeValue(var Value: Extended): Extended;
begin
Result := Value;
Value := 0.0;
end;
function TJclIntfExtendedHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfExtendedHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfExtendedHashMap.ValuesEqual(const A, B: Extended): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclExtendedExtendedBucket } ==========================================
procedure TJclExtendedExtendedBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclExtendedExtendedHashMap } ==========================================
constructor TJclExtendedExtendedHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclExtendedExtendedHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclExtendedExtendedHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclExtendedExtendedBucket;
ADest: TJclExtendedExtendedHashMap;
AMap: IJclExtendedExtendedMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclExtendedExtendedHashMap then
begin
ADest := TJclExtendedExtendedHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclExtendedExtendedBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclExtendedExtendedMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedExtendedHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclExtendedExtendedHashMap then
TJclExtendedExtendedHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclExtendedExtendedHashMap.Clear;
var
I, J: Integer;
Bucket: TJclExtendedExtendedBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.ContainsKey(const Key: Extended): Boolean;
var
I: Integer;
Bucket: TJclExtendedExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.ContainsValue(const Value: Extended): Boolean;
var
I, J: Integer;
Bucket: TJclExtendedExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.Extract(const Key: Extended): Extended;
var
Bucket: TJclExtendedExtendedBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0.0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.GetValue(const Key: Extended): Extended;
var
I: Integer;
Bucket: TJclExtendedExtendedBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclExtendedExtendedHashMap.KeyOfValue(const Value: Extended): Extended;
var
I, J: Integer;
Bucket: TJclExtendedExtendedBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.KeySet: IJclExtendedSet;
var
I, J: Integer;
Bucket: TJclExtendedExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclExtendedArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.MapEquals(const AMap: IJclExtendedExtendedMap): Boolean;
var
I, J: Integer;
Bucket: TJclExtendedExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedExtendedHashMap.Pack;
var
I: Integer;
Bucket: TJclExtendedExtendedBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedExtendedHashMap.PutAll(const AMap: IJclExtendedExtendedMap);
var
It: IJclExtendedIterator;
Key: Extended;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedExtendedHashMap.PutValue(const Key: Extended; const Value: Extended);
var
Index: Integer;
Bucket: TJclExtendedExtendedBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, 0.0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclExtendedExtendedBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.Remove(const Key: Extended): Extended;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedExtendedHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclExtendedExtendedHashMap.Values: IJclExtendedCollection;
var
I, J: Integer;
Bucket: TJclExtendedExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclExtendedArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedExtendedHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclExtendedExtendedHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclExtendedExtendedHashMap.FreeKey(var Key: Extended): Extended;
begin
Result := Key;
Key := 0.0;
end;
function TJclExtendedExtendedHashMap.FreeValue(var Value: Extended): Extended;
begin
Result := Value;
Value := 0.0;
end;
function TJclExtendedExtendedHashMap.KeysEqual(const A, B: Extended): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclExtendedExtendedHashMap.ValuesEqual(const A, B: Extended): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclIntegerIntfBucket } ==========================================
procedure TJclIntegerIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntegerIntfHashMap } ==========================================
constructor TJclIntegerIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntegerIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntegerIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntegerIntfBucket;
ADest: TJclIntegerIntfHashMap;
AMap: IJclIntegerIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntegerIntfHashMap then
begin
ADest := TJclIntegerIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntegerIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntegerIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntegerIntfHashMap then
TJclIntegerIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntegerIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntegerIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.ContainsKey(Key: Integer): Boolean;
var
I: Integer;
Bucket: TJclIntegerIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclIntegerIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.Extract(Key: Integer): IInterface;
var
Bucket: TJclIntegerIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.GetValue(Key: Integer): IInterface;
var
I: Integer;
Bucket: TJclIntegerIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntegerIntfHashMap.KeyOfValue(const Value: IInterface): Integer;
var
I, J: Integer;
Bucket: TJclIntegerIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.KeySet: IJclIntegerSet;
var
I, J: Integer;
Bucket: TJclIntegerIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntegerArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.MapEquals(const AMap: IJclIntegerIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntegerIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclIntegerIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntfHashMap.PutAll(const AMap: IJclIntegerIntfMap);
var
It: IJclIntegerIterator;
Key: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntfHashMap.PutValue(Key: Integer; const Value: IInterface);
var
Index: Integer;
Bucket: TJclIntegerIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntegerIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.Remove(Key: Integer): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntegerIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclIntegerIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntegerIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntegerIntfHashMap.FreeKey(var Key: Integer): Integer;
begin
Result := Key;
Key := 0;
end;
function TJclIntegerIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclIntegerIntfHashMap.KeysEqual(A, B: Integer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclIntegerIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfIntegerBucket } ==========================================
procedure TJclIntfIntegerBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfIntegerHashMap } ==========================================
constructor TJclIntfIntegerHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfIntegerHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfIntegerHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfIntegerBucket;
ADest: TJclIntfIntegerHashMap;
AMap: IJclIntfIntegerMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfIntegerHashMap then
begin
ADest := TJclIntfIntegerHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfIntegerBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfIntegerMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntegerHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfIntegerHashMap then
TJclIntfIntegerHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfIntegerHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfIntegerBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.ContainsValue(Value: Integer): Boolean;
var
I, J: Integer;
Bucket: TJclIntfIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.Extract(const Key: IInterface): Integer;
var
Bucket: TJclIntfIntegerBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.GetValue(const Key: IInterface): Integer;
var
I: Integer;
Bucket: TJclIntfIntegerBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfIntegerHashMap.KeyOfValue(Value: Integer): IInterface;
var
I, J: Integer;
Bucket: TJclIntfIntegerBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.MapEquals(const AMap: IJclIntfIntegerMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntegerHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfIntegerBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntegerHashMap.PutAll(const AMap: IJclIntfIntegerMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntegerHashMap.PutValue(const Key: IInterface; Value: Integer);
var
Index: Integer;
Bucket: TJclIntfIntegerBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, 0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfIntegerBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.Remove(const Key: IInterface): Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfIntegerHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfIntegerHashMap.Values: IJclIntegerCollection;
var
I, J: Integer;
Bucket: TJclIntfIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntegerArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfIntegerHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfIntegerHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfIntegerHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfIntegerHashMap.FreeValue(var Value: Integer): Integer;
begin
Result := Value;
Value := 0;
end;
function TJclIntfIntegerHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfIntegerHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfIntegerHashMap.ValuesEqual(A, B: Integer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclIntegerIntegerBucket } ==========================================
procedure TJclIntegerIntegerBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntegerIntegerHashMap } ==========================================
constructor TJclIntegerIntegerHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntegerIntegerHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntegerIntegerHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntegerIntegerBucket;
ADest: TJclIntegerIntegerHashMap;
AMap: IJclIntegerIntegerMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntegerIntegerHashMap then
begin
ADest := TJclIntegerIntegerHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntegerIntegerBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntegerIntegerMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntegerHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntegerIntegerHashMap then
TJclIntegerIntegerHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntegerIntegerHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntegerIntegerBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.ContainsKey(Key: Integer): Boolean;
var
I: Integer;
Bucket: TJclIntegerIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.ContainsValue(Value: Integer): Boolean;
var
I, J: Integer;
Bucket: TJclIntegerIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.Extract(Key: Integer): Integer;
var
Bucket: TJclIntegerIntegerBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.GetValue(Key: Integer): Integer;
var
I: Integer;
Bucket: TJclIntegerIntegerBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntegerIntegerHashMap.KeyOfValue(Value: Integer): Integer;
var
I, J: Integer;
Bucket: TJclIntegerIntegerBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.KeySet: IJclIntegerSet;
var
I, J: Integer;
Bucket: TJclIntegerIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntegerArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.MapEquals(const AMap: IJclIntegerIntegerMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntegerIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntegerHashMap.Pack;
var
I: Integer;
Bucket: TJclIntegerIntegerBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntegerHashMap.PutAll(const AMap: IJclIntegerIntegerMap);
var
It: IJclIntegerIterator;
Key: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntegerHashMap.PutValue(Key: Integer; Value: Integer);
var
Index: Integer;
Bucket: TJclIntegerIntegerBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, 0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntegerIntegerBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.Remove(Key: Integer): Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerIntegerHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntegerIntegerHashMap.Values: IJclIntegerCollection;
var
I, J: Integer;
Bucket: TJclIntegerIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntegerArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerIntegerHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntegerIntegerHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntegerIntegerHashMap.FreeKey(var Key: Integer): Integer;
begin
Result := Key;
Key := 0;
end;
function TJclIntegerIntegerHashMap.FreeValue(var Value: Integer): Integer;
begin
Result := Value;
Value := 0;
end;
function TJclIntegerIntegerHashMap.KeysEqual(A, B: Integer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclIntegerIntegerHashMap.ValuesEqual(A, B: Integer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclCardinalIntfBucket } ==========================================
procedure TJclCardinalIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclCardinalIntfHashMap } ==========================================
constructor TJclCardinalIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclCardinalIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclCardinalIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclCardinalIntfBucket;
ADest: TJclCardinalIntfHashMap;
AMap: IJclCardinalIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclCardinalIntfHashMap then
begin
ADest := TJclCardinalIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclCardinalIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclCardinalIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclCardinalIntfHashMap then
TJclCardinalIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclCardinalIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclCardinalIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.ContainsKey(Key: Cardinal): Boolean;
var
I: Integer;
Bucket: TJclCardinalIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclCardinalIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.Extract(Key: Cardinal): IInterface;
var
Bucket: TJclCardinalIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.GetValue(Key: Cardinal): IInterface;
var
I: Integer;
Bucket: TJclCardinalIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclCardinalIntfHashMap.KeyOfValue(const Value: IInterface): Cardinal;
var
I, J: Integer;
Bucket: TJclCardinalIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.KeySet: IJclCardinalSet;
var
I, J: Integer;
Bucket: TJclCardinalIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclCardinalArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.MapEquals(const AMap: IJclCardinalIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclCardinalIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclCardinalIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalIntfHashMap.PutAll(const AMap: IJclCardinalIntfMap);
var
It: IJclCardinalIterator;
Key: Cardinal;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalIntfHashMap.PutValue(Key: Cardinal; const Value: IInterface);
var
Index: Integer;
Bucket: TJclCardinalIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclCardinalIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.Remove(Key: Cardinal): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclCardinalIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclCardinalIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclCardinalIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclCardinalIntfHashMap.FreeKey(var Key: Cardinal): Cardinal;
begin
Result := Key;
Key := 0;
end;
function TJclCardinalIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclCardinalIntfHashMap.KeysEqual(A, B: Cardinal): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclCardinalIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfCardinalBucket } ==========================================
procedure TJclIntfCardinalBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfCardinalHashMap } ==========================================
constructor TJclIntfCardinalHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfCardinalHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfCardinalHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfCardinalBucket;
ADest: TJclIntfCardinalHashMap;
AMap: IJclIntfCardinalMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfCardinalHashMap then
begin
ADest := TJclIntfCardinalHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfCardinalBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfCardinalMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfCardinalHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfCardinalHashMap then
TJclIntfCardinalHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfCardinalHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfCardinalBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.ContainsValue(Value: Cardinal): Boolean;
var
I, J: Integer;
Bucket: TJclIntfCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.Extract(const Key: IInterface): Cardinal;
var
Bucket: TJclIntfCardinalBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.GetValue(const Key: IInterface): Cardinal;
var
I: Integer;
Bucket: TJclIntfCardinalBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfCardinalHashMap.KeyOfValue(Value: Cardinal): IInterface;
var
I, J: Integer;
Bucket: TJclIntfCardinalBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.MapEquals(const AMap: IJclIntfCardinalMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfCardinalHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfCardinalBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfCardinalHashMap.PutAll(const AMap: IJclIntfCardinalMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfCardinalHashMap.PutValue(const Key: IInterface; Value: Cardinal);
var
Index: Integer;
Bucket: TJclIntfCardinalBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, 0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfCardinalBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.Remove(const Key: IInterface): Cardinal;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfCardinalHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfCardinalHashMap.Values: IJclCardinalCollection;
var
I, J: Integer;
Bucket: TJclIntfCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclCardinalArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfCardinalHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfCardinalHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfCardinalHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfCardinalHashMap.FreeValue(var Value: Cardinal): Cardinal;
begin
Result := Value;
Value := 0;
end;
function TJclIntfCardinalHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfCardinalHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfCardinalHashMap.ValuesEqual(A, B: Cardinal): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclCardinalCardinalBucket } ==========================================
procedure TJclCardinalCardinalBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclCardinalCardinalHashMap } ==========================================
constructor TJclCardinalCardinalHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclCardinalCardinalHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclCardinalCardinalHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclCardinalCardinalBucket;
ADest: TJclCardinalCardinalHashMap;
AMap: IJclCardinalCardinalMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclCardinalCardinalHashMap then
begin
ADest := TJclCardinalCardinalHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclCardinalCardinalBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclCardinalCardinalMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalCardinalHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclCardinalCardinalHashMap then
TJclCardinalCardinalHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclCardinalCardinalHashMap.Clear;
var
I, J: Integer;
Bucket: TJclCardinalCardinalBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.ContainsKey(Key: Cardinal): Boolean;
var
I: Integer;
Bucket: TJclCardinalCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.ContainsValue(Value: Cardinal): Boolean;
var
I, J: Integer;
Bucket: TJclCardinalCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.Extract(Key: Cardinal): Cardinal;
var
Bucket: TJclCardinalCardinalBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.GetValue(Key: Cardinal): Cardinal;
var
I: Integer;
Bucket: TJclCardinalCardinalBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclCardinalCardinalHashMap.KeyOfValue(Value: Cardinal): Cardinal;
var
I, J: Integer;
Bucket: TJclCardinalCardinalBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.KeySet: IJclCardinalSet;
var
I, J: Integer;
Bucket: TJclCardinalCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclCardinalArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.MapEquals(const AMap: IJclCardinalCardinalMap): Boolean;
var
I, J: Integer;
Bucket: TJclCardinalCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalCardinalHashMap.Pack;
var
I: Integer;
Bucket: TJclCardinalCardinalBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalCardinalHashMap.PutAll(const AMap: IJclCardinalCardinalMap);
var
It: IJclCardinalIterator;
Key: Cardinal;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalCardinalHashMap.PutValue(Key: Cardinal; Value: Cardinal);
var
Index: Integer;
Bucket: TJclCardinalCardinalBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, 0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclCardinalCardinalBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.Remove(Key: Cardinal): Cardinal;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalCardinalHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclCardinalCardinalHashMap.Values: IJclCardinalCollection;
var
I, J: Integer;
Bucket: TJclCardinalCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclCardinalArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalCardinalHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclCardinalCardinalHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclCardinalCardinalHashMap.FreeKey(var Key: Cardinal): Cardinal;
begin
Result := Key;
Key := 0;
end;
function TJclCardinalCardinalHashMap.FreeValue(var Value: Cardinal): Cardinal;
begin
Result := Value;
Value := 0;
end;
function TJclCardinalCardinalHashMap.KeysEqual(A, B: Cardinal): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclCardinalCardinalHashMap.ValuesEqual(A, B: Cardinal): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclInt64IntfBucket } ==========================================
procedure TJclInt64IntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclInt64IntfHashMap } ==========================================
constructor TJclInt64IntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclInt64IntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclInt64IntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclInt64IntfBucket;
ADest: TJclInt64IntfHashMap;
AMap: IJclInt64IntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclInt64IntfHashMap then
begin
ADest := TJclInt64IntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclInt64IntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclInt64IntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64IntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclInt64IntfHashMap then
TJclInt64IntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclInt64IntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclInt64IntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.ContainsKey(const Key: Int64): Boolean;
var
I: Integer;
Bucket: TJclInt64IntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclInt64IntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.Extract(const Key: Int64): IInterface;
var
Bucket: TJclInt64IntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.GetValue(const Key: Int64): IInterface;
var
I: Integer;
Bucket: TJclInt64IntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclInt64IntfHashMap.KeyOfValue(const Value: IInterface): Int64;
var
I, J: Integer;
Bucket: TJclInt64IntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.KeySet: IJclInt64Set;
var
I, J: Integer;
Bucket: TJclInt64IntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclInt64ArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.MapEquals(const AMap: IJclInt64IntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclInt64IntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64IntfHashMap.Pack;
var
I: Integer;
Bucket: TJclInt64IntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64IntfHashMap.PutAll(const AMap: IJclInt64IntfMap);
var
It: IJclInt64Iterator;
Key: Int64;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64IntfHashMap.PutValue(const Key: Int64; const Value: IInterface);
var
Index: Integer;
Bucket: TJclInt64IntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclInt64IntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.Remove(const Key: Int64): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64IntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclInt64IntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclInt64IntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64IntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclInt64IntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclInt64IntfHashMap.FreeKey(var Key: Int64): Int64;
begin
Result := Key;
Key := 0;
end;
function TJclInt64IntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclInt64IntfHashMap.KeysEqual(const A, B: Int64): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclInt64IntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfInt64Bucket } ==========================================
procedure TJclIntfInt64Bucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfInt64HashMap } ==========================================
constructor TJclIntfInt64HashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfInt64HashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfInt64HashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfInt64Bucket;
ADest: TJclIntfInt64HashMap;
AMap: IJclIntfInt64Map;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfInt64HashMap then
begin
ADest := TJclIntfInt64HashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfInt64Bucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfInt64Map, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfInt64HashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfInt64HashMap then
TJclIntfInt64HashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfInt64HashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfInt64Bucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.ContainsValue(const Value: Int64): Boolean;
var
I, J: Integer;
Bucket: TJclIntfInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.Extract(const Key: IInterface): Int64;
var
Bucket: TJclIntfInt64Bucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.GetValue(const Key: IInterface): Int64;
var
I: Integer;
Bucket: TJclIntfInt64Bucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfInt64HashMap.KeyOfValue(const Value: Int64): IInterface;
var
I, J: Integer;
Bucket: TJclIntfInt64Bucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.MapEquals(const AMap: IJclIntfInt64Map): Boolean;
var
I, J: Integer;
Bucket: TJclIntfInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfInt64HashMap.Pack;
var
I: Integer;
Bucket: TJclIntfInt64Bucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfInt64HashMap.PutAll(const AMap: IJclIntfInt64Map);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfInt64HashMap.PutValue(const Key: IInterface; const Value: Int64);
var
Index: Integer;
Bucket: TJclIntfInt64Bucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, 0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfInt64Bucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.Remove(const Key: IInterface): Int64;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfInt64HashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfInt64HashMap.Values: IJclInt64Collection;
var
I, J: Integer;
Bucket: TJclIntfInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclInt64ArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfInt64HashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfInt64HashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfInt64HashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfInt64HashMap.FreeValue(var Value: Int64): Int64;
begin
Result := Value;
Value := 0;
end;
function TJclIntfInt64HashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfInt64HashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfInt64HashMap.ValuesEqual(const A, B: Int64): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclInt64Int64Bucket } ==========================================
procedure TJclInt64Int64Bucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclInt64Int64HashMap } ==========================================
constructor TJclInt64Int64HashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclInt64Int64HashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclInt64Int64HashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclInt64Int64Bucket;
ADest: TJclInt64Int64HashMap;
AMap: IJclInt64Int64Map;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclInt64Int64HashMap then
begin
ADest := TJclInt64Int64HashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclInt64Int64Bucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclInt64Int64Map, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64Int64HashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclInt64Int64HashMap then
TJclInt64Int64HashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclInt64Int64HashMap.Clear;
var
I, J: Integer;
Bucket: TJclInt64Int64Bucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.ContainsKey(const Key: Int64): Boolean;
var
I: Integer;
Bucket: TJclInt64Int64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.ContainsValue(const Value: Int64): Boolean;
var
I, J: Integer;
Bucket: TJclInt64Int64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.Extract(const Key: Int64): Int64;
var
Bucket: TJclInt64Int64Bucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := 0;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.GetValue(const Key: Int64): Int64;
var
I: Integer;
Bucket: TJclInt64Int64Bucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclInt64Int64HashMap.KeyOfValue(const Value: Int64): Int64;
var
I, J: Integer;
Bucket: TJclInt64Int64Bucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.KeySet: IJclInt64Set;
var
I, J: Integer;
Bucket: TJclInt64Int64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclInt64ArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.MapEquals(const AMap: IJclInt64Int64Map): Boolean;
var
I, J: Integer;
Bucket: TJclInt64Int64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64Int64HashMap.Pack;
var
I: Integer;
Bucket: TJclInt64Int64Bucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64Int64HashMap.PutAll(const AMap: IJclInt64Int64Map);
var
It: IJclInt64Iterator;
Key: Int64;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64Int64HashMap.PutValue(const Key: Int64; const Value: Int64);
var
Index: Integer;
Bucket: TJclInt64Int64Bucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, 0)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclInt64Int64Bucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.Remove(const Key: Int64): Int64;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64Int64HashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclInt64Int64HashMap.Values: IJclInt64Collection;
var
I, J: Integer;
Bucket: TJclInt64Int64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclInt64ArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64Int64HashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclInt64Int64HashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclInt64Int64HashMap.FreeKey(var Key: Int64): Int64;
begin
Result := Key;
Key := 0;
end;
function TJclInt64Int64HashMap.FreeValue(var Value: Int64): Int64;
begin
Result := Value;
Value := 0;
end;
function TJclInt64Int64HashMap.KeysEqual(const A, B: Int64): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclInt64Int64HashMap.ValuesEqual(const A, B: Int64): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclPtrIntfBucket } ==========================================
procedure TJclPtrIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclPtrIntfHashMap } ==========================================
constructor TJclPtrIntfHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclPtrIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclPtrIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclPtrIntfBucket;
ADest: TJclPtrIntfHashMap;
AMap: IJclPtrIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclPtrIntfHashMap then
begin
ADest := TJclPtrIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclPtrIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclPtrIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclPtrIntfHashMap then
TJclPtrIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclPtrIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclPtrIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.ContainsKey(Key: Pointer): Boolean;
var
I: Integer;
Bucket: TJclPtrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.ContainsValue(const Value: IInterface): Boolean;
var
I, J: Integer;
Bucket: TJclPtrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.Extract(Key: Pointer): IInterface;
var
Bucket: TJclPtrIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.GetValue(Key: Pointer): IInterface;
var
I: Integer;
Bucket: TJclPtrIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclPtrIntfHashMap.KeyOfValue(const Value: IInterface): Pointer;
var
I, J: Integer;
Bucket: TJclPtrIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.KeySet: IJclPtrSet;
var
I, J: Integer;
Bucket: TJclPtrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclPtrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.MapEquals(const AMap: IJclPtrIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclPtrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclPtrIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrIntfHashMap.PutAll(const AMap: IJclPtrIntfMap);
var
It: IJclPtrIterator;
Key: Pointer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrIntfHashMap.PutValue(Key: Pointer; const Value: IInterface);
var
Index: Integer;
Bucket: TJclPtrIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclPtrIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.Remove(Key: Pointer): IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclPtrIntfHashMap.Values: IJclIntfCollection;
var
I, J: Integer;
Bucket: TJclPtrIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclPtrIntfHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclPtrIntfHashMap.FreeKey(var Key: Pointer): Pointer;
begin
Result := Key;
Key := nil;
end;
function TJclPtrIntfHashMap.FreeValue(var Value: IInterface): IInterface;
begin
Result := Value;
Value := nil;
end;
function TJclPtrIntfHashMap.KeysEqual(A, B: Pointer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclPtrIntfHashMap.ValuesEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntfPtrBucket } ==========================================
procedure TJclIntfPtrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfPtrHashMap } ==========================================
constructor TJclIntfPtrHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfPtrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfPtrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfPtrBucket;
ADest: TJclIntfPtrHashMap;
AMap: IJclIntfPtrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfPtrHashMap then
begin
ADest := TJclIntfPtrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfPtrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfPtrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfPtrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfPtrHashMap then
TJclIntfPtrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfPtrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfPtrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.ContainsValue(Value: Pointer): Boolean;
var
I, J: Integer;
Bucket: TJclIntfPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.Extract(const Key: IInterface): Pointer;
var
Bucket: TJclIntfPtrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.GetValue(const Key: IInterface): Pointer;
var
I: Integer;
Bucket: TJclIntfPtrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfPtrHashMap.KeyOfValue(Value: Pointer): IInterface;
var
I, J: Integer;
Bucket: TJclIntfPtrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.MapEquals(const AMap: IJclIntfPtrMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfPtrHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfPtrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfPtrHashMap.PutAll(const AMap: IJclIntfPtrMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfPtrHashMap.PutValue(const Key: IInterface; Value: Pointer);
var
Index: Integer;
Bucket: TJclIntfPtrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfPtrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.Remove(const Key: IInterface): Pointer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfPtrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfPtrHashMap.Values: IJclPtrCollection;
var
I, J: Integer;
Bucket: TJclIntfPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclPtrArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfPtrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfPtrHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclIntfPtrHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfPtrHashMap.FreeValue(var Value: Pointer): Pointer;
begin
Result := Value;
Value := nil;
end;
function TJclIntfPtrHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfPtrHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfPtrHashMap.ValuesEqual(A, B: Pointer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclPtrPtrBucket } ==========================================
procedure TJclPtrPtrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclPtrPtrHashMap } ==========================================
constructor TJclPtrPtrHashMap.Create(ACapacity: Integer);
begin
inherited Create;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclPtrPtrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclPtrPtrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclPtrPtrBucket;
ADest: TJclPtrPtrHashMap;
AMap: IJclPtrPtrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclPtrPtrHashMap then
begin
ADest := TJclPtrPtrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclPtrPtrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclPtrPtrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrPtrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclPtrPtrHashMap then
TJclPtrPtrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclPtrPtrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclPtrPtrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.ContainsKey(Key: Pointer): Boolean;
var
I: Integer;
Bucket: TJclPtrPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.ContainsValue(Value: Pointer): Boolean;
var
I, J: Integer;
Bucket: TJclPtrPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.Extract(Key: Pointer): Pointer;
var
Bucket: TJclPtrPtrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.GetValue(Key: Pointer): Pointer;
var
I: Integer;
Bucket: TJclPtrPtrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclPtrPtrHashMap.KeyOfValue(Value: Pointer): Pointer;
var
I, J: Integer;
Bucket: TJclPtrPtrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.KeySet: IJclPtrSet;
var
I, J: Integer;
Bucket: TJclPtrPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclPtrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.MapEquals(const AMap: IJclPtrPtrMap): Boolean;
var
I, J: Integer;
Bucket: TJclPtrPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrPtrHashMap.Pack;
var
I: Integer;
Bucket: TJclPtrPtrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrPtrHashMap.PutAll(const AMap: IJclPtrPtrMap);
var
It: IJclPtrIterator;
Key: Pointer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrPtrHashMap.PutValue(Key: Pointer; Value: Pointer);
var
Index: Integer;
Bucket: TJclPtrPtrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclPtrPtrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.Remove(Key: Pointer): Pointer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrPtrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclPtrPtrHashMap.Values: IJclPtrCollection;
var
I, J: Integer;
Bucket: TJclPtrPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclPtrArrayList.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrPtrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclPtrPtrHashMap.Create(FCapacity);
AssignPropertiesTo(Result);
end;
function TJclPtrPtrHashMap.FreeKey(var Key: Pointer): Pointer;
begin
Result := Key;
Key := nil;
end;
function TJclPtrPtrHashMap.FreeValue(var Value: Pointer): Pointer;
begin
Result := Value;
Value := nil;
end;
function TJclPtrPtrHashMap.KeysEqual(A, B: Pointer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclPtrPtrHashMap.ValuesEqual(A, B: Pointer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
//=== { TJclIntfBucket } ==========================================
procedure TJclIntfBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntfHashMap } ==========================================
constructor TJclIntfHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntfHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntfHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntfBucket;
ADest: TJclIntfHashMap;
AMap: IJclIntfMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntfHashMap then
begin
ADest := TJclIntfHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntfBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntfMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntfHashMap then
TJclIntfHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntfHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.ContainsKey(const Key: IInterface): Boolean;
var
I: Integer;
Bucket: TJclIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.Extract(const Key: IInterface): TObject;
var
Bucket: TJclIntfBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.GetValue(const Key: IInterface): TObject;
var
I: Integer;
Bucket: TJclIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntfHashMap.KeyOfValue(Value: TObject): IInterface;
var
I, J: Integer;
Bucket: TJclIntfBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.KeySet: IJclIntfSet;
var
I, J: Integer;
Bucket: TJclIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntfArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.MapEquals(const AMap: IJclIntfMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfHashMap.Pack;
var
I: Integer;
Bucket: TJclIntfBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfHashMap.PutAll(const AMap: IJclIntfMap);
var
It: IJclIntfIterator;
Key: IInterface;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfHashMap.PutValue(const Key: IInterface; Value: TObject);
var
Index: Integer;
Bucket: TJclIntfBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntfBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.Remove(const Key: IInterface): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntfHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntfHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclIntfBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntfHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntfHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclIntfHashMap.FreeKey(var Key: IInterface): IInterface;
begin
Result := Key;
Key := nil;
end;
function TJclIntfHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclIntfHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclIntfHashMap.Hash(const AInterface: IInterface): Integer;
begin
Result := Integer(AInterface);
end;
function TJclIntfHashMap.KeysEqual(const A, B: IInterface): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclIntfHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclAnsiStrBucket } ==========================================
procedure TJclAnsiStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclAnsiStrHashMap } ==========================================
constructor TJclAnsiStrHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclAnsiStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclAnsiStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclAnsiStrBucket;
ADest: TJclAnsiStrHashMap;
AMap: IJclAnsiStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclAnsiStrHashMap then
begin
ADest := TJclAnsiStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclAnsiStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclAnsiStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclAnsiStrHashMap then
TJclAnsiStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclAnsiStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclAnsiStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.ContainsKey(const Key: AnsiString): Boolean;
var
I: Integer;
Bucket: TJclAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.Extract(const Key: AnsiString): TObject;
var
Bucket: TJclAnsiStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.GetValue(const Key: AnsiString): TObject;
var
I: Integer;
Bucket: TJclAnsiStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclAnsiStrHashMap.KeyOfValue(Value: TObject): AnsiString;
var
I, J: Integer;
Bucket: TJclAnsiStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.KeySet: IJclAnsiStrSet;
var
I, J: Integer;
Bucket: TJclAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclAnsiStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.MapEquals(const AMap: IJclAnsiStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrHashMap.Pack;
var
I: Integer;
Bucket: TJclAnsiStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrHashMap.PutAll(const AMap: IJclAnsiStrMap);
var
It: IJclAnsiStrIterator;
Key: AnsiString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrHashMap.PutValue(const Key: AnsiString; Value: TObject);
var
Index: Integer;
Bucket: TJclAnsiStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclAnsiStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.Remove(const Key: AnsiString): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclAnsiStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclAnsiStrHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclAnsiStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclAnsiStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclAnsiStrHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclAnsiStrHashMap.FreeKey(var Key: AnsiString): AnsiString;
begin
Result := Key;
Key := '';
end;
function TJclAnsiStrHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclAnsiStrHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclAnsiStrHashMap.KeysEqual(const A, B: AnsiString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclAnsiStrHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclWideStrBucket } ==========================================
procedure TJclWideStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclWideStrHashMap } ==========================================
constructor TJclWideStrHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclWideStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclWideStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclWideStrBucket;
ADest: TJclWideStrHashMap;
AMap: IJclWideStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclWideStrHashMap then
begin
ADest := TJclWideStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclWideStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclWideStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclWideStrHashMap then
TJclWideStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclWideStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclWideStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.ContainsKey(const Key: WideString): Boolean;
var
I: Integer;
Bucket: TJclWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.Extract(const Key: WideString): TObject;
var
Bucket: TJclWideStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.GetValue(const Key: WideString): TObject;
var
I: Integer;
Bucket: TJclWideStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclWideStrHashMap.KeyOfValue(Value: TObject): WideString;
var
I, J: Integer;
Bucket: TJclWideStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.KeySet: IJclWideStrSet;
var
I, J: Integer;
Bucket: TJclWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclWideStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.MapEquals(const AMap: IJclWideStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrHashMap.Pack;
var
I: Integer;
Bucket: TJclWideStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrHashMap.PutAll(const AMap: IJclWideStrMap);
var
It: IJclWideStrIterator;
Key: WideString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrHashMap.PutValue(const Key: WideString; Value: TObject);
var
Index: Integer;
Bucket: TJclWideStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclWideStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.Remove(const Key: WideString): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclWideStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclWideStrHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclWideStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclWideStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclWideStrHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclWideStrHashMap.FreeKey(var Key: WideString): WideString;
begin
Result := Key;
Key := '';
end;
function TJclWideStrHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclWideStrHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclWideStrHashMap.KeysEqual(const A, B: WideString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclWideStrHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
{$IFDEF SUPPORTS_UNICODE_STRING}
//=== { TJclUnicodeStrBucket } ==========================================
procedure TJclUnicodeStrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclUnicodeStrHashMap } ==========================================
constructor TJclUnicodeStrHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclUnicodeStrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclUnicodeStrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclUnicodeStrBucket;
ADest: TJclUnicodeStrHashMap;
AMap: IJclUnicodeStrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclUnicodeStrHashMap then
begin
ADest := TJclUnicodeStrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclUnicodeStrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclUnicodeStrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclUnicodeStrHashMap then
TJclUnicodeStrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclUnicodeStrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclUnicodeStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.ContainsKey(const Key: UnicodeString): Boolean;
var
I: Integer;
Bucket: TJclUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.Extract(const Key: UnicodeString): TObject;
var
Bucket: TJclUnicodeStrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.GetValue(const Key: UnicodeString): TObject;
var
I: Integer;
Bucket: TJclUnicodeStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclUnicodeStrHashMap.KeyOfValue(Value: TObject): UnicodeString;
var
I, J: Integer;
Bucket: TJclUnicodeStrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := '';
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.KeySet: IJclUnicodeStrSet;
var
I, J: Integer;
Bucket: TJclUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclUnicodeStrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.MapEquals(const AMap: IJclUnicodeStrMap): Boolean;
var
I, J: Integer;
Bucket: TJclUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrHashMap.Pack;
var
I: Integer;
Bucket: TJclUnicodeStrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrHashMap.PutAll(const AMap: IJclUnicodeStrMap);
var
It: IJclUnicodeStrIterator;
Key: UnicodeString;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrHashMap.PutValue(const Key: UnicodeString; Value: TObject);
var
Index: Integer;
Bucket: TJclUnicodeStrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, '') and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclUnicodeStrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.Remove(const Key: UnicodeString): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclUnicodeStrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclUnicodeStrHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclUnicodeStrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclUnicodeStrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclUnicodeStrHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclUnicodeStrHashMap.FreeKey(var Key: UnicodeString): UnicodeString;
begin
Result := Key;
Key := '';
end;
function TJclUnicodeStrHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclUnicodeStrHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclUnicodeStrHashMap.KeysEqual(const A, B: UnicodeString): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclUnicodeStrHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
{$ENDIF SUPPORTS_UNICODE_STRING}
//=== { TJclSingleBucket } ==========================================
procedure TJclSingleBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclSingleHashMap } ==========================================
constructor TJclSingleHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclSingleHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclSingleHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclSingleBucket;
ADest: TJclSingleHashMap;
AMap: IJclSingleMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclSingleHashMap then
begin
ADest := TJclSingleHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclSingleBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclSingleMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclSingleHashMap then
TJclSingleHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclSingleHashMap.Clear;
var
I, J: Integer;
Bucket: TJclSingleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.ContainsKey(const Key: Single): Boolean;
var
I: Integer;
Bucket: TJclSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.Extract(const Key: Single): TObject;
var
Bucket: TJclSingleBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.GetValue(const Key: Single): TObject;
var
I: Integer;
Bucket: TJclSingleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclSingleHashMap.KeyOfValue(Value: TObject): Single;
var
I, J: Integer;
Bucket: TJclSingleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.KeySet: IJclSingleSet;
var
I, J: Integer;
Bucket: TJclSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclSingleArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.MapEquals(const AMap: IJclSingleMap): Boolean;
var
I, J: Integer;
Bucket: TJclSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleHashMap.Pack;
var
I: Integer;
Bucket: TJclSingleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleHashMap.PutAll(const AMap: IJclSingleMap);
var
It: IJclSingleIterator;
Key: Single;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleHashMap.PutValue(const Key: Single; Value: TObject);
var
Index: Integer;
Bucket: TJclSingleBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclSingleBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.Remove(const Key: Single): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclSingleHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclSingleHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclSingleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclSingleHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclSingleHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclSingleHashMap.FreeKey(var Key: Single): Single;
begin
Result := Key;
Key := 0.0;
end;
function TJclSingleHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclSingleHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclSingleHashMap.KeysEqual(const A, B: Single): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclSingleHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclDoubleBucket } ==========================================
procedure TJclDoubleBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclDoubleHashMap } ==========================================
constructor TJclDoubleHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclDoubleHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclDoubleHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclDoubleBucket;
ADest: TJclDoubleHashMap;
AMap: IJclDoubleMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclDoubleHashMap then
begin
ADest := TJclDoubleHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclDoubleBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclDoubleMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclDoubleHashMap then
TJclDoubleHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclDoubleHashMap.Clear;
var
I, J: Integer;
Bucket: TJclDoubleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.ContainsKey(const Key: Double): Boolean;
var
I: Integer;
Bucket: TJclDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.Extract(const Key: Double): TObject;
var
Bucket: TJclDoubleBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.GetValue(const Key: Double): TObject;
var
I: Integer;
Bucket: TJclDoubleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclDoubleHashMap.KeyOfValue(Value: TObject): Double;
var
I, J: Integer;
Bucket: TJclDoubleBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.KeySet: IJclDoubleSet;
var
I, J: Integer;
Bucket: TJclDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclDoubleArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.MapEquals(const AMap: IJclDoubleMap): Boolean;
var
I, J: Integer;
Bucket: TJclDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleHashMap.Pack;
var
I: Integer;
Bucket: TJclDoubleBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleHashMap.PutAll(const AMap: IJclDoubleMap);
var
It: IJclDoubleIterator;
Key: Double;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleHashMap.PutValue(const Key: Double; Value: TObject);
var
Index: Integer;
Bucket: TJclDoubleBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclDoubleBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.Remove(const Key: Double): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclDoubleHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclDoubleHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclDoubleBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclDoubleHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclDoubleHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclDoubleHashMap.FreeKey(var Key: Double): Double;
begin
Result := Key;
Key := 0.0;
end;
function TJclDoubleHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclDoubleHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclDoubleHashMap.KeysEqual(const A, B: Double): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclDoubleHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclExtendedBucket } ==========================================
procedure TJclExtendedBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclExtendedHashMap } ==========================================
constructor TJclExtendedHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclExtendedHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclExtendedHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclExtendedBucket;
ADest: TJclExtendedHashMap;
AMap: IJclExtendedMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclExtendedHashMap then
begin
ADest := TJclExtendedHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclExtendedBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclExtendedMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclExtendedHashMap then
TJclExtendedHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclExtendedHashMap.Clear;
var
I, J: Integer;
Bucket: TJclExtendedBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.ContainsKey(const Key: Extended): Boolean;
var
I: Integer;
Bucket: TJclExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.Extract(const Key: Extended): TObject;
var
Bucket: TJclExtendedBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.GetValue(const Key: Extended): TObject;
var
I: Integer;
Bucket: TJclExtendedBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclExtendedHashMap.KeyOfValue(Value: TObject): Extended;
var
I, J: Integer;
Bucket: TJclExtendedBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0.0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.KeySet: IJclExtendedSet;
var
I, J: Integer;
Bucket: TJclExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclExtendedArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.MapEquals(const AMap: IJclExtendedMap): Boolean;
var
I, J: Integer;
Bucket: TJclExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedHashMap.Pack;
var
I: Integer;
Bucket: TJclExtendedBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedHashMap.PutAll(const AMap: IJclExtendedMap);
var
It: IJclExtendedIterator;
Key: Extended;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedHashMap.PutValue(const Key: Extended; Value: TObject);
var
Index: Integer;
Bucket: TJclExtendedBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0.0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclExtendedBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.Remove(const Key: Extended): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclExtendedHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclExtendedHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclExtendedBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclExtendedHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclExtendedHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclExtendedHashMap.FreeKey(var Key: Extended): Extended;
begin
Result := Key;
Key := 0.0;
end;
function TJclExtendedHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclExtendedHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclExtendedHashMap.KeysEqual(const A, B: Extended): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclExtendedHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclIntegerBucket } ==========================================
procedure TJclIntegerBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclIntegerHashMap } ==========================================
constructor TJclIntegerHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclIntegerHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclIntegerHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclIntegerBucket;
ADest: TJclIntegerHashMap;
AMap: IJclIntegerMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclIntegerHashMap then
begin
ADest := TJclIntegerHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclIntegerBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclIntegerMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclIntegerHashMap then
TJclIntegerHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclIntegerHashMap.Clear;
var
I, J: Integer;
Bucket: TJclIntegerBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.ContainsKey(Key: Integer): Boolean;
var
I: Integer;
Bucket: TJclIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.Extract(Key: Integer): TObject;
var
Bucket: TJclIntegerBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.GetValue(Key: Integer): TObject;
var
I: Integer;
Bucket: TJclIntegerBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclIntegerHashMap.KeyOfValue(Value: TObject): Integer;
var
I, J: Integer;
Bucket: TJclIntegerBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.KeySet: IJclIntegerSet;
var
I, J: Integer;
Bucket: TJclIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclIntegerArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.MapEquals(const AMap: IJclIntegerMap): Boolean;
var
I, J: Integer;
Bucket: TJclIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerHashMap.Pack;
var
I: Integer;
Bucket: TJclIntegerBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerHashMap.PutAll(const AMap: IJclIntegerMap);
var
It: IJclIntegerIterator;
Key: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerHashMap.PutValue(Key: Integer; Value: TObject);
var
Index: Integer;
Bucket: TJclIntegerBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclIntegerBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.Remove(Key: Integer): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclIntegerHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclIntegerHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclIntegerBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclIntegerHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclIntegerHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclIntegerHashMap.FreeKey(var Key: Integer): Integer;
begin
Result := Key;
Key := 0;
end;
function TJclIntegerHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclIntegerHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclIntegerHashMap.KeysEqual(A, B: Integer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclIntegerHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclCardinalBucket } ==========================================
procedure TJclCardinalBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclCardinalHashMap } ==========================================
constructor TJclCardinalHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclCardinalHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclCardinalHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclCardinalBucket;
ADest: TJclCardinalHashMap;
AMap: IJclCardinalMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclCardinalHashMap then
begin
ADest := TJclCardinalHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclCardinalBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclCardinalMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclCardinalHashMap then
TJclCardinalHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclCardinalHashMap.Clear;
var
I, J: Integer;
Bucket: TJclCardinalBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.ContainsKey(Key: Cardinal): Boolean;
var
I: Integer;
Bucket: TJclCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.Extract(Key: Cardinal): TObject;
var
Bucket: TJclCardinalBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.GetValue(Key: Cardinal): TObject;
var
I: Integer;
Bucket: TJclCardinalBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclCardinalHashMap.KeyOfValue(Value: TObject): Cardinal;
var
I, J: Integer;
Bucket: TJclCardinalBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.KeySet: IJclCardinalSet;
var
I, J: Integer;
Bucket: TJclCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclCardinalArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.MapEquals(const AMap: IJclCardinalMap): Boolean;
var
I, J: Integer;
Bucket: TJclCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalHashMap.Pack;
var
I: Integer;
Bucket: TJclCardinalBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalHashMap.PutAll(const AMap: IJclCardinalMap);
var
It: IJclCardinalIterator;
Key: Cardinal;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalHashMap.PutValue(Key: Cardinal; Value: TObject);
var
Index: Integer;
Bucket: TJclCardinalBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclCardinalBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.Remove(Key: Cardinal): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclCardinalHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclCardinalHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclCardinalBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclCardinalHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclCardinalHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclCardinalHashMap.FreeKey(var Key: Cardinal): Cardinal;
begin
Result := Key;
Key := 0;
end;
function TJclCardinalHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclCardinalHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclCardinalHashMap.KeysEqual(A, B: Cardinal): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclCardinalHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclInt64Bucket } ==========================================
procedure TJclInt64Bucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclInt64HashMap } ==========================================
constructor TJclInt64HashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclInt64HashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclInt64HashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclInt64Bucket;
ADest: TJclInt64HashMap;
AMap: IJclInt64Map;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclInt64HashMap then
begin
ADest := TJclInt64HashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclInt64Bucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclInt64Map, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64HashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclInt64HashMap then
TJclInt64HashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclInt64HashMap.Clear;
var
I, J: Integer;
Bucket: TJclInt64Bucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.ContainsKey(const Key: Int64): Boolean;
var
I: Integer;
Bucket: TJclInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.Extract(const Key: Int64): TObject;
var
Bucket: TJclInt64Bucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.GetValue(const Key: Int64): TObject;
var
I: Integer;
Bucket: TJclInt64Bucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclInt64HashMap.KeyOfValue(Value: TObject): Int64;
var
I, J: Integer;
Bucket: TJclInt64Bucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := 0;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.KeySet: IJclInt64Set;
var
I, J: Integer;
Bucket: TJclInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclInt64ArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.MapEquals(const AMap: IJclInt64Map): Boolean;
var
I, J: Integer;
Bucket: TJclInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64HashMap.Pack;
var
I: Integer;
Bucket: TJclInt64Bucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64HashMap.PutAll(const AMap: IJclInt64Map);
var
It: IJclInt64Iterator;
Key: Int64;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64HashMap.PutValue(const Key: Int64; Value: TObject);
var
Index: Integer;
Bucket: TJclInt64Bucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, 0) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclInt64Bucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.Remove(const Key: Int64): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclInt64HashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclInt64HashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclInt64Bucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclInt64HashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclInt64HashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclInt64HashMap.FreeKey(var Key: Int64): Int64;
begin
Result := Key;
Key := 0;
end;
function TJclInt64HashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclInt64HashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclInt64HashMap.KeysEqual(const A, B: Int64): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclInt64HashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclPtrBucket } ==========================================
procedure TJclPtrBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclPtrHashMap } ==========================================
constructor TJclPtrHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean);
begin
inherited Create;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclPtrHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclPtrHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclPtrBucket;
ADest: TJclPtrHashMap;
AMap: IJclPtrMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclPtrHashMap then
begin
ADest := TJclPtrHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclPtrBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclPtrMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclPtrHashMap then
TJclPtrHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclPtrHashMap.Clear;
var
I, J: Integer;
Bucket: TJclPtrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.ContainsKey(Key: Pointer): Boolean;
var
I: Integer;
Bucket: TJclPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.Extract(Key: Pointer): TObject;
var
Bucket: TJclPtrBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.GetValue(Key: Pointer): TObject;
var
I: Integer;
Bucket: TJclPtrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclPtrHashMap.KeyOfValue(Value: TObject): Pointer;
var
I, J: Integer;
Bucket: TJclPtrBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.KeySet: IJclPtrSet;
var
I, J: Integer;
Bucket: TJclPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclPtrArraySet.Create(FSize);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.MapEquals(const AMap: IJclPtrMap): Boolean;
var
I, J: Integer;
Bucket: TJclPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrHashMap.Pack;
var
I: Integer;
Bucket: TJclPtrBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrHashMap.PutAll(const AMap: IJclPtrMap);
var
It: IJclPtrIterator;
Key: Pointer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrHashMap.PutValue(Key: Pointer; Value: TObject);
var
Index: Integer;
Bucket: TJclPtrBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclPtrBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.Remove(Key: Pointer): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclPtrHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclPtrHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclPtrBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclPtrHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclPtrHashMap.Create(FCapacity, False);
AssignPropertiesTo(Result);
end;
function TJclPtrHashMap.FreeKey(var Key: Pointer): Pointer;
begin
Result := Key;
Key := nil;
end;
function TJclPtrHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclPtrHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclPtrHashMap.KeysEqual(A, B: Pointer): Boolean;
begin
Result := ItemsEqual(A, B);
end;
function TJclPtrHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
//=== { TJclBucket } ==========================================
procedure TJclBucket.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclHashMap } ==========================================
constructor TJclHashMap.Create(ACapacity: Integer; AOwnsValues: Boolean; AOwnsKeys: Boolean);
begin
inherited Create;
FOwnsKeys := AOwnsKeys;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclHashMap.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclHashMap.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TJclBucket;
ADest: TJclHashMap;
AMap: IJclMap;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclHashMap then
begin
ADest := TJclHashMap(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TJclBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclMap, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclHashMap then
TJclHashMap(Dest).HashFunction := HashFunction;
end;
procedure TJclHashMap.Clear;
var
I, J: Integer;
Bucket: TJclBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.ContainsKey(Key: TObject): Boolean;
var
I: Integer;
Bucket: TJclBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.ContainsValue(Value: TObject): Boolean;
var
I, J: Integer;
Bucket: TJclBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.Extract(Key: TObject): TObject;
var
Bucket: TJclBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := nil;
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.GetValue(Key: TObject): TObject;
var
I: Integer;
Bucket: TJclBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclHashMap.KeyOfValue(Value: TObject): TObject;
var
I, J: Integer;
Bucket: TJclBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := nil;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.KeySet: IJclSet;
var
I, J: Integer;
Bucket: TJclBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArraySet.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.MapEquals(const AMap: IJclMap): Boolean;
var
I, J: Integer;
Bucket: TJclBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap.Pack;
var
I: Integer;
Bucket: TJclBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap.PutAll(const AMap: IJclMap);
var
It: IJclIterator;
Key: TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap.PutValue(Key: TObject; Value: TObject);
var
Index: Integer;
Bucket: TJclBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, nil) and not ValuesEqual(Value, nil)) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TJclBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.Remove(Key: TObject): TObject;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.Size: Integer;
begin
Result := FSize;
end;
function TJclHashMap.Values: IJclCollection;
var
I, J: Integer;
Bucket: TJclBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := TJclArrayList.Create(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclHashMap.Create(FCapacity, False, False);
AssignPropertiesTo(Result);
end;
function TJclHashMap.FreeKey(var Key: TObject): TObject;
begin
if FOwnsKeys then
begin
Result := nil;
FreeAndNil(Key);
end
else
begin
Result := Key;
Key := nil;
end;
end;
function TJclHashMap.FreeValue(var Value: TObject): TObject;
begin
if FOwnsValues then
begin
Result := nil;
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := nil;
end;
end;
function TJclHashMap.GetOwnsKeys: Boolean;
begin
Result := FOwnsKeys;
end;
function TJclHashMap.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
function TJclHashMap.Hash(AObject: TObject): Integer;
begin
Result := Integer(AObject);
end;
function TJclHashMap.KeysEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
function TJclHashMap.ValuesEqual(A, B: TObject): Boolean;
begin
Result := Integer(A) = Integer(B);
end;
{$IFDEF SUPPORTS_GENERICS}
//=== { TJclBucket<TKey, TValue> } ==========================================
procedure TJclBucket<TKey, TValue>.MoveArray(FromIndex, ToIndex, Count: Integer);
begin
if Count > 0 then
begin
Move(Entries[FromIndex], Entries[ToIndex], Count * SizeOf(Entries[0]));
{ Keep reference counting working }
if FromIndex < ToIndex then
begin
if (ToIndex - FromIndex) < Count then
FillChar(Entries[FromIndex], (ToIndex - FromIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end
else
if FromIndex > ToIndex then
begin
if (FromIndex - ToIndex) < Count then
FillChar(Entries[ToIndex + Count], (FromIndex - ToIndex) * SizeOf(Entries[0]), 0)
else
FillChar(Entries[FromIndex], Count * SizeOf(Entries[0]), 0);
end;
end;
end;
//=== { TJclHashMap<TKey, TValue> } ==========================================
constructor TJclHashMap<TKey, TValue>.Create(ACapacity: Integer; AOwnsValues: Boolean; AOwnsKeys: Boolean);
begin
inherited Create;
FOwnsKeys := AOwnsKeys;
FOwnsValues := AOwnsValues;
SetCapacity(ACapacity);
FHashFunction := HashMul;
end;
destructor TJclHashMap<TKey, TValue>.Destroy;
begin
FReadOnly := False;
Clear;
inherited Destroy;
end;
procedure TJclHashMap<TKey, TValue>.AssignDataTo(Dest: TJclAbstractContainerBase);
var
I, J: Integer;
SelfBucket, NewBucket: TBucket;
ADest: TJclHashMap<TKey, TValue>;
AMap: IJclMap<TKey, TValue>;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
inherited AssignDataTo(Dest);
if Dest is TJclHashMap<TKey, TValue> then
begin
ADest := TJclHashMap<TKey, TValue>(Dest);
ADest.Clear;
for I := 0 to FCapacity - 1 do
begin
SelfBucket := FBuckets[I];
if SelfBucket <> nil then
begin
NewBucket := TBucket.Create;
SetLength(NewBucket.Entries, SelfBucket.Size);
for J := 0 to SelfBucket.Size - 1 do
begin
NewBucket.Entries[J].Key := SelfBucket.Entries[J].Key;
NewBucket.Entries[J].Value := SelfBucket.Entries[J].Value;
end;
NewBucket.Size := SelfBucket.Size;
ADest.FBuckets[I] := NewBucket;
end;
end;
end
else
if Supports(IInterface(Dest), IJclMap<TKey, TValue>, AMap) then
begin
AMap.Clear;
AMap.PutAll(Self);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap<TKey, TValue>.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
begin
inherited AssignPropertiesto(Dest);
if Dest is TJclHashMap<TKey, TValue> then
TJclHashMap<TKey, TValue>(Dest).HashFunction := HashFunction;
end;
procedure TJclHashMap<TKey, TValue>.Clear;
var
I, J: Integer;
Bucket: TBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
for J := 0 to Bucket.Size - 1 do
begin
FreeKey(Bucket.Entries[J].Key);
FreeValue(Bucket.Entries[J].Value);
end;
FreeAndNil(FBuckets[I]);
end;
end;
FSize := 0;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.ContainsKey(const Key: TKey): Boolean;
var
I: Integer;
Bucket: TBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := True;
Break;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.ContainsValue(const Value: TValue): Boolean;
var
I, J: Integer;
Bucket: TBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := True;
Break;
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.Extract(const Key: TKey): TValue;
var
Bucket: TBucket;
I, NewCapacity: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Default(TValue);
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Bucket.Entries[I].Value := Default(TValue);
FreeKey(Bucket.Entries[I].Key);
if I < Length(Bucket.Entries) - 1 then
Bucket.MoveArray(I + 1, I, Bucket.Size - I - 1);
Dec(Bucket.Size);
Dec(FSize);
Break;
end;
NewCapacity := CalcPackCapacity(Length(Bucket.Entries), Bucket.Size);
if NewCapacity < Length(Bucket.Entries) then
SetLength(Bucket.Entries, NewCapacity);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.GetValue(const Key: TKey): TValue;
var
I: Integer;
Bucket: TBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := Default(TValue);
Bucket := FBuckets[FHashFunction(Hash(Key), FCapacity)];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
Result := Bucket.Entries[I].Value;
Found := True;
Break;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.IsEmpty: Boolean;
begin
Result := FSize = 0;
end;
function TJclHashMap<TKey, TValue>.KeyOfValue(const Value: TValue): TKey;
var
I, J: Integer;
Bucket: TBucket;
Found: Boolean;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Found := False;
Result := Default(TKey);
for J := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[J];
if Bucket <> nil then
for I := 0 to Bucket.Size - 1 do
if ValuesEqual(Bucket.Entries[I].Value, Value) then
begin
Result := Bucket.Entries[I].Key;
Found := True;
Break;
end;
end;
if (not Found) and (not FReturnDefaultElements) then
raise EJclNoSuchElementError.Create('');
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.KeySet: IJclSet<TKey>;
var
I, J: Integer;
Bucket: TBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := CreateEmptyArraySet(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Key);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.MapEquals(const AMap: IJclMap<TKey, TValue>): Boolean;
var
I, J: Integer;
Bucket: TBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := False;
if AMap = nil then
Exit;
if FSize <> AMap.Size then
Exit;
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
if AMap.ContainsKey(Bucket.Entries[J].Key) then
begin
if not ValuesEqual(AMap.GetValue(Bucket.Entries[J].Key), Bucket.Entries[J].Value) then
Exit;
end
else
Exit;
end;
Result := True;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap<TKey, TValue>.Pack;
var
I: Integer;
Bucket: TBucket;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
begin
if Bucket.Size > 0 then
SetLength(Bucket.Entries, Bucket.Size)
else
FreeAndNil(FBuckets[I]);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap<TKey, TValue>.PutAll(const AMap: IJclMap<TKey, TValue>);
var
It: IJclIterator<TKey>;
Key: TKey;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if AMap = nil then
Exit;
It := AMap.KeySet.First;
while It.HasNext do
begin
Key := It.Next;
PutValue(Key, AMap.GetValue(Key));
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap<TKey, TValue>.PutValue(const Key: TKey; const Value: TValue);
var
Index: Integer;
Bucket: TBucket;
I: Integer;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FAllowDefaultElements or (not KeysEqual(Key, Default(TKey)) and not ValuesEqual(Value, Default(TValue))) then
begin
Index := FHashFunction(Hash(Key), FCapacity);
Bucket := FBuckets[Index];
if Bucket <> nil then
begin
for I := 0 to Bucket.Size - 1 do
if KeysEqual(Bucket.Entries[I].Key, Key) then
begin
FreeValue(Bucket.Entries[I].Value);
Bucket.Entries[I].Value := Value;
Exit;
end;
end
else
begin
Bucket := TBucket.Create;
SetLength(Bucket.Entries, 1);
FBuckets[Index] := Bucket;
end;
if Bucket.Size = Length(Bucket.Entries) then
SetLength(Bucket.Entries, CalcGrowCapacity(Bucket.Size, Bucket.Size));
if Bucket.Size < Length(Bucket.Entries) then
begin
Bucket.Entries[Bucket.Size].Key := Key;
Bucket.Entries[Bucket.Size].Value := Value;
Inc(Bucket.Size);
Inc(FSize);
end;
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.Remove(const Key: TKey): TValue;
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
Result := Extract(Key);
Result := FreeValue(Result);
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
procedure TJclHashMap<TKey, TValue>.SetCapacity(Value: Integer);
begin
if ReadOnly then
raise EJclReadOnlyError.Create;
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginWrite;
try
{$ENDIF THREADSAFE}
if FSize = 0 then
begin
SetLength(FBuckets, Value);
inherited SetCapacity(Value);
end
else
raise EJclOperationNotSupportedError.Create;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndWrite;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.Size: Integer;
begin
Result := FSize;
end;
function TJclHashMap<TKey, TValue>.Values: IJclCollection<TValue>;
var
I, J: Integer;
Bucket: TBucket;
begin
{$IFDEF THREADSAFE}
if FThreadSafe then
SyncReaderWriter.BeginRead;
try
{$ENDIF THREADSAFE}
Result := CreateEmptyArrayList(FSize, False);
for I := 0 to FCapacity - 1 do
begin
Bucket := FBuckets[I];
if Bucket <> nil then
for J := 0 to Bucket.Size - 1 do
Result.Add(Bucket.Entries[J].Value);
end;
{$IFDEF THREADSAFE}
finally
if FThreadSafe then
SyncReaderWriter.EndRead;
end;
{$ENDIF THREADSAFE}
end;
function TJclHashMap<TKey, TValue>.FreeKey(var Key: TKey): TKey;
begin
if FOwnsKeys then
begin
Result := Default(TKey);
FreeAndNil(Key);
end
else
begin
Result := Key;
Key := Default(TKey);
end;
end;
function TJclHashMap<TKey, TValue>.FreeValue(var Value: TValue): TValue;
begin
if FOwnsValues then
begin
Result := Default(TValue);
FreeAndNil(Value);
end
else
begin
Result := Value;
Value := Default(TValue);
end;
end;
function TJclHashMap<TKey, TValue>.GetOwnsKeys: Boolean;
begin
Result := FOwnsKeys;
end;
function TJclHashMap<TKey, TValue>.GetOwnsValues: Boolean;
begin
Result := FOwnsValues;
end;
//=== { TJclHashMapE<TKey, TValue> } =========================================
constructor TJclHashMapE<TKey, TValue>.Create(const AKeyEqualityComparer: IJclEqualityComparer<TKey>;
const AKeyHashConverter: IJclHashConverter<TKey>; const AValueEqualityComparer: IJclEqualityComparer<TValue>;
const AKeyComparer: IJclComparer<TKey>; ACapacity: Integer; AOwnsValues: Boolean; AOwnsKeys: Boolean);
begin
inherited Create(ACapacity, AOwnsKeys, AOwnsValues);
FKeyEqualityComparer := AKeyEqualityComparer;
FKeyHashConverter := AKeyHashConverter;
FValueEqualityComparer := AValueEqualityComparer;
FKeyComparer := AKeyComparer;
end;
procedure TJclHashMapE<TKey, TValue>.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
var
ADest: TJclHashMapE<TKey, TValue>;
begin
inherited AssignPropertiesTo(Dest);
if Dest is TJclHashMapE<TKey, TValue> then
begin
ADest := TJclHashMapE<TKey, TValue>(Dest);
ADest.FKeyEqualityComparer := FKeyEqualityComparer;
ADest.FKeyHashConverter := FKeyHashConverter;
ADest.FValueEqualityComparer := FValueEqualityComparer;
ADest.FKeyComparer := FKeyComparer;
end;
end;
function TJclHashMapE<TKey, TValue>.CreateEmptyArrayList(ACapacity: Integer; AOwnsObjects: Boolean): IJclCollection<TValue>;
begin
Result := TArrayList.Create(ValueEqualityComparer, ACapacity, AOwnsObjects);
end;
function TJclHashMapE<TKey, TValue>.CreateEmptyArraySet(ACapacity: Integer; AOwnsObjects: Boolean): IJclSet<TKey>;
begin
Result := TArraySet.Create(KeyComparer, ACapacity, AOwnsObjects);
end;
function TJclHashMapE<TKey, TValue>.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclHashMapE<TKey, TValue>.Create(KeyEqualityComparer, KeyHashConverter, ValueEqualityComparer,
KeyComparer, FCapacity, False, False);
AssignPropertiesTo(Result);
end;
function TJclHashMapE<TKey, TValue>.Hash(const AKey: TKey): Integer;
begin
if KeyEqualityComparer = nil then
raise EJclNoHashConverterError.Create;
Result := KeyHashConverter.Hash(AKey);
end;
function TJclHashMapE<TKey, TValue>.KeysEqual(const A, B: TKey): Boolean;
begin
if KeyEqualityComparer = nil then
raise EJclNoEqualityComparerError.Create;
Result := KeyEqualityComparer.ItemsEqual(A, B);
end;
function TJclHashMapE<TKey, TValue>.ValuesEqual(const A, B: TValue): Boolean;
begin
if ValueEqualityComparer = nil then
raise EJclNoEqualityComparerError.Create;
Result := ValueEqualityComparer.ItemsEqual(A, B);
end;
//=== { TJclHashMapF<TKey, TValue> } =========================================
constructor TJclHashMapF<TKey, TValue>.Create(AKeyEqualityCompare: TEqualityCompare<TKey>;
AKeyHash: THashConvert<TKey>; AValueEqualityCompare: TEqualityCompare<TValue>; AKeyCompare: TCompare<TKey>;
ACapacity: Integer; AOwnsValues: Boolean; AOwnsKeys: Boolean);
begin
inherited Create(ACapacity, AOwnsKeys, AOwnsValues);
FKeyEqualityCompare := AKeyEqualityCompare;
FKeyHash := AKeyHash;
FValueEqualityCompare := AValueEqualityCompare;
FKeyCompare := AKeyCompare;
end;
procedure TJclHashMapF<TKey, TValue>.AssignPropertiesTo(Dest: TJclAbstractContainerBase);
var
ADest: TJclHashMapF<TKey, TValue>;
begin
inherited AssignPropertiesTo(Dest);
if Dest is TJclHashMapF<TKey, TValue> then
begin
ADest := TJclHashMapF<TKey, TValue>(Dest);
ADest.FKeyEqualityCompare := FKeyEqualityCompare;
ADest.FKeyHash := FKeyHash;
ADest.FValueEqualityCompare := FValueEqualityCompare;
ADest.FKeyCompare := FKeyCompare;
end;
end;
function TJclHashMapF<TKey, TValue>.CreateEmptyArrayList(ACapacity: Integer; AOwnsObjects: Boolean): IJclCollection<TValue>;
begin
Result := TArrayList.Create(ValueEqualityCompare, ACapacity, AOwnsObjects);
end;
function TJclHashMapF<TKey, TValue>.CreateEmptyArraySet(ACapacity: Integer; AOwnsObjects: Boolean): IJclSet<TKey>;
begin
Result := TArraySet.Create(KeyCompare, ACapacity, AOwnsObjects);
end;
function TJclHashMapF<TKey, TValue>.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclHashMapF<TKey, TValue>.Create(KeyEqualityCompare, KeyHash, ValueEqualityCompare, KeyCompare, FCapacity,
False, False);
AssignPropertiesTo(Result);
end;
function TJclHashMapF<TKey, TValue>.Hash(const AKey: TKey): Integer;
begin
if not Assigned(KeyHash) then
raise EJclNoHashConverterError.Create;
Result := KeyHash(AKey);
end;
function TJclHashMapF<TKey, TValue>.KeysEqual(const A, B: TKey): Boolean;
begin
if not Assigned(KeyEqualityCompare) then
raise EJclNoEqualityComparerError.Create;
Result := KeyEqualityCompare(A, B);
end;
function TJclHashMapF<TKey, TValue>.ValuesEqual(const A, B: TValue): Boolean;
begin
if not Assigned(ValueEqualityCompare) then
raise EJclNoEqualityComparerError.Create;
Result := ValueEqualityCompare(A, B);
end;
//=== { TJclHashMapI<TKey, TValue> } =========================================
function TJclHashMapI<TKey, TValue>.CreateEmptyArrayList(ACapacity: Integer; AOwnsObjects: Boolean): IJclCollection<TValue>;
begin
Result := TArrayList.Create(ACapacity, AOwnsObjects);
end;
function TJclHashMapI<TKey, TValue>.CreateEmptyArraySet(ACapacity: Integer; AOwnsObjects: Boolean): IJclSet<TKey>;
begin
Result := TArraySet.Create(ACapacity, AOwnsObjects);
end;
function TJclHashMapI<TKey, TValue>.CreateEmptyContainer: TJclAbstractContainerBase;
begin
Result := TJclHashMapI<TKey, TValue>.Create(FCapacity, False, False);
AssignPropertiesTo(Result);
end;
function TJclHashMapI<TKey, TValue>.Hash(const AKey: TKey): Integer;
begin
Result := AKey.GetHashCode;
end;
function TJclHashMapI<TKey, TValue>.KeysEqual(const A, B: TKey): Boolean;
begin
Result := A.Equals(B);
end;
function TJclHashMapI<TKey, TValue>.ValuesEqual(const A, B: TValue): Boolean;
begin
Result := A.Equals(B);
end;
{$ENDIF SUPPORTS_GENERICS}
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.