diff --git a/components/sparta/generics/examples/TStack/TStackProject.lpr b/components/sparta/generics/examples/TStack/TStackProject.lpr index 1a53e1872a..a6ddad9b81 100644 --- a/components/sparta/generics/examples/TStack/TStackProject.lpr +++ b/components/sparta/generics/examples/TStack/TStackProject.lpr @@ -8,7 +8,6 @@ program TStackProject; uses SysUtils, - Windows, Generics.Collections; type diff --git a/components/sparta/generics/source/generics.collections.pas b/components/sparta/generics/source/generics.collections.pas index a430fb7d29..907f6acfe7 100644 --- a/components/sparta/generics/source/generics.collections.pas +++ b/components/sparta/generics/source/generics.collections.pas @@ -14,6 +14,14 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Acknowledgment + + Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring + many new types and major refactoring of entire library + + Thanks to mORMot (http://synopse.info) project for the best implementations + of hashing functions like crc32c and xxHash32 :) + **********************************************************************} unit Generics.Collections; @@ -24,6 +32,7 @@ unit Generics.Collections; {$DEFINE CUSTOM_DICTIONARY_CONSTRAINTS := TKey, TValue, THashFactory} {$DEFINE OPEN_ADDRESSING_CONSTRAINTS := TKey, TValue, THashFactory, TProbeSequence} {$DEFINE CUCKOO_CONSTRAINTS := TKey, TValue, THashFactory, TCuckooCfg} +{$DEFINE TREE_CONSTRAINTS := TKey, TValue, TInfo} {$WARNINGS OFF} {$HINTS OFF} {$OVERFLOWCHECKS OFF} @@ -32,7 +41,7 @@ unit Generics.Collections; interface uses - Classes, SysUtils, Generics.MemoryExpanders, Generics.Defaults, + RtlConsts, Classes, SysUtils, Generics.MemoryExpanders, Generics.Defaults, Generics.Helpers, Generics.Strings; { FPC BUGS related to Generics.* (54 bugs, 19 fixed) @@ -54,11 +63,26 @@ uses OTHER: 25595, 25612, 25615, 25617, 25618, 25619 } +{.$define EXTRA_WARNINGS} +{.$define ENABLE_METHODS_WITH_TEnumerableWithPointers} + type - TArray = array of T; // for name TArray conflict with TArray record implementation (bug #26030) + EAVLTree = class(Exception); + EIndexedAVLTree = class(EAVLTree); + + TDuplicates = Classes.TDuplicates; + + {$ifdef VER3_0_0} + TArray = array of T; + {$endif} // bug #24254 workaround // should be TArray = record class procedure Sort(...) etc. + TBinarySearchResult = record + FoundIndex, CandidateIndex: SizeInt; + CompareResult: SizeInt; + end; + TCustomArrayHelper = class abstract private type @@ -75,6 +99,9 @@ type class procedure Sort(var AValues: array of T; const AComparer: IComparer; AIndex, ACount: SizeInt); overload; + class function BinarySearch(constref AValues: array of T; constref AItem: T; + out ASearchResult: TBinarySearchResult; const AComparer: IComparer; + AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload; class function BinarySearch(constref AValues: array of T; constref AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload; @@ -82,17 +109,24 @@ type out AFoundIndex: SizeInt; const AComparer: IComparer): Boolean; overload; class function BinarySearch(constref AValues: array of T; constref AItem: T; out AFoundIndex: SizeInt): Boolean; overload; - end experimental; // will be renamed to TCustomArray (bug #24254) + class function BinarySearch(constref AValues: array of T; constref AItem: T; + out ASearchResult: TBinarySearchResult; const AComparer: IComparer): Boolean; overload; + class function BinarySearch(constref AValues: array of T; constref AItem: T; + out ASearchResult: TBinarySearchResult): Boolean; overload; + end {$ifdef EXTRA_WARNINGS}experimental{$endif}; // will be renamed to TCustomArray (bug #24254) TArrayHelper = class(TCustomArrayHelper) protected // modified QuickSort from classes\lists.inc class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer); override; public + class function BinarySearch(constref AValues: array of T; constref AItem: T; + out ASearchResult: TBinarySearchResult; const AComparer: IComparer; + AIndex, ACount: SizeInt): Boolean; override; overload; class function BinarySearch(constref AValues: array of T; constref AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; override; overload; - end experimental; // will be renamed to TArray (bug #24254) + end {$ifdef EXTRA_WARNINGS}experimental{$endif}; // will be renamed to TArray (bug #24254) TCollectionNotification = (cnAdded, cnRemoved, cnExtracted); TCollectionNotifyEvent = procedure(ASender: TObject; constref AItem: T; AAction: TCollectionNotification) @@ -112,6 +146,10 @@ type { TEnumerable } TEnumerable = class abstract + public type + PT = ^T; + protected // no forward generics declarations (needed by TPointersCollection), this should be moved into TEnumerableWithPointers + function GetPtrEnumerator: TEnumerator; virtual; abstract; protected function ToArrayImpl(ACount: SizeInt): TArray; overload; // used by descendants protected @@ -121,11 +159,35 @@ type function ToArray: TArray; virtual; overload; end; + // error: no memory left for TCustomPointersEnumerator version + TCustomPointersEnumerator = class abstract(TEnumerator); + + TCustomPointersCollection = object + strict private type + TLocalEnumerable = TEnumerable; // compiler has bug for directly usage of TEnumerable + protected + function Enumerable: TLocalEnumerable; inline; + public + function GetEnumerator: TEnumerator; + end; + + TEnumerableWithPointers = class(TEnumerable) + strict private type + TPointersCollection = TCustomPointersCollection; + PPointersCollection = ^TPointersCollection; + private + function GetPtr: PPointersCollection; inline; + public + property Ptr: PPointersCollection read GetPtr; + end; + // More info: http://stackoverflow.com/questions/5232198/about-vectors-growth // TODO: custom memory managers (as constraints) {$DEFINE CUSTOM_LIST_CAPACITY_INC := Result + Result div 2} // ~approximation to golden ratio: n = n * 1.5 } // {$DEFINE CUSTOM_LIST_CAPACITY_INC := Result * 2} // standard inc - TCustomList = class abstract(TEnumerable) + TCustomList = class abstract(TEnumerableWithPointers) + public type + PT = ^T; protected type // bug #24282 TArrayHelperBugHack = TArrayHelper; @@ -133,7 +195,7 @@ type FOnNotify: TCollectionNotifyEvent; function GetCapacity: SizeInt; inline; protected - FItemsLength: SizeInt; + FLength: SizeInt; FItems: array of T; function PrepareAddingItem: SizeInt; virtual; @@ -148,9 +210,11 @@ type property Count: SizeInt read GetCount; property Capacity: SizeInt read GetCapacity write SetCapacity; property OnNotify: TCollectionNotifyEvent read FOnNotify write FOnNotify; + + procedure TrimExcess; virtual; abstract; end; - TCustomListEnumerator = class abstract(TEnumerator< T >) + TCustomListEnumerator = class abstract(TEnumerator) private FList: TCustomList; FIndex: SizeInt; @@ -162,7 +226,22 @@ type constructor Create(AList: TCustomList); end; - TList = class(TCustomList) + TCustomListWithPointers = class(TCustomList) + public type + TPointersEnumerator = class(TCustomPointersEnumerator) + protected + FList: TCustomListWithPointers; + FIndex: SizeInt; + function DoMoveNext: boolean; override; + function DoGetCurrent: PT; override; + public + constructor Create(AList: TCustomListWithPointers); + end; + protected + function GetPtrEnumerator: TEnumerator; override; + end; + + TList = class(TCustomListWithPointers) private var FComparer: IComparer; protected @@ -180,6 +259,8 @@ type protected procedure SetCapacity(AValue: SizeInt); override; procedure SetCount(AValue: SizeInt); + procedure InitializeList; virtual; + procedure InternalInsert(AIndex: SizeInt; constref AValue: T); private function GetItem(AIndex: SizeInt): T; procedure SetItem(AIndex: SizeInt; const AValue: T); @@ -187,17 +268,26 @@ type constructor Create; overload; constructor Create(const AComparer: IComparer); overload; constructor Create(ACollection: TEnumerable); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers); overload; + {$ENDIF} destructor Destroy; override; - function Add(constref AValue: T): SizeInt; - procedure AddRange(constref AValues: array of T); overload; + function Add(constref AValue: T): SizeInt; virtual; + procedure AddRange(constref AValues: array of T); virtual; overload; procedure AddRange(const AEnumerable: IEnumerable); overload; procedure AddRange(AEnumerable: TEnumerable); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + procedure AddRange(AEnumerable: TEnumerableWithPointers); overload; + {$ENDIF} - procedure Insert(AIndex: SizeInt; constref AValue: T); - procedure InsertRange(AIndex: SizeInt; constref AValues: array of T); overload; + procedure Insert(AIndex: SizeInt; constref AValue: T); virtual; + procedure InsertRange(AIndex: SizeInt; constref AValues: array of T); virtual; overload; procedure InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable); overload; procedure InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + procedure InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerableWithPointers); overload; + {$ENDIF} function Remove(constref AValue: T): SizeInt; procedure Delete(AIndex: SizeInt); inline; @@ -205,8 +295,8 @@ type function ExtractIndex(const AIndex: SizeInt): T; overload; function Extract(constref AValue: T): T; overload; - procedure Exchange(AIndex1, AIndex2: SizeInt); - procedure Move(AIndex, ANewIndex: SizeInt); + procedure Exchange(AIndex1, AIndex2: SizeInt); virtual; + procedure Move(AIndex, ANewIndex: SizeInt); virtual; function First: T; inline; function Last: T; inline; @@ -219,17 +309,43 @@ type procedure Reverse; - procedure TrimExcess; + procedure TrimExcess; override; procedure Sort; overload; procedure Sort(const AComparer: IComparer); overload; function BinarySearch(constref AItem: T; out AIndex: SizeInt): Boolean; overload; function BinarySearch(constref AItem: T; out AIndex: SizeInt; const AComparer: IComparer): Boolean; overload; - property Count: SizeInt read FItemsLength write SetCount; + property Count: SizeInt read FLength write SetCount; property Items[Index: SizeInt]: T read GetItem write SetItem; default; end; + TCollectionSortStyle = (cssNone,cssUser,cssAuto); + TCollectionSortStyles = Set of TCollectionSortStyle; + + TSortedList = class(TList) + private + FDuplicates: TDuplicates; + FSortStyle: TCollectionSortStyle; + function GetSorted: boolean; + procedure SetSorted(AValue: boolean); + procedure SetSortStyle(AValue: TCollectionSortStyle); + protected + procedure InitializeList; override; + public + function Add(constref AValue: T): SizeInt; override; overload; + procedure AddRange(constref AValues: array of T); override; overload; + procedure Insert(AIndex: SizeInt; constref AValue: T); override; + procedure Exchange(AIndex1, AIndex2: SizeInt); override; + procedure Move(AIndex, ANewIndex: SizeInt); override; + procedure InsertRange(AIndex: SizeInt; constref AValues: array of T); override; overload; + property Duplicates: TDuplicates read FDuplicates write FDuplicates; + property Sorted: Boolean read GetSorted write SetSorted; + property SortStyle: TCollectionSortStyle read FSortStyle write SetSortStyle; + + function ConsistencyCheck(ARaiseException: boolean = true): boolean; virtual; + end; + TThreadList = class private FList: TList; @@ -250,6 +366,18 @@ type end; TQueue = class(TCustomList) + public type + TPointersEnumerator = class(TCustomPointersEnumerator) + protected + FQueue: TQueue; + FIndex: SizeInt; + function DoMoveNext: boolean; override; + function DoGetCurrent: PT; override; + public + constructor Create(AQueue: TQueue); + end; + protected + function GetPtrEnumerator: TEnumerator; override; protected // bug #24287 - workaround for generics type name conflict (Identifier not found) // next bug workaround - for another error related to previous workaround @@ -271,16 +399,19 @@ type function GetCount: SizeInt; override; public constructor Create(ACollection: TEnumerable); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers); overload; + {$ENDIF} destructor Destroy; override; procedure Enqueue(constref AValue: T); function Dequeue: T; function Extract: T; function Peek: T; procedure Clear; - procedure TrimExcess; + procedure TrimExcess; override; end; - TStack = class(TCustomList) + TStack = class(TCustomListWithPointers) protected // bug #24287 - workaround for generics type name conflict (Identifier not found) // next bug workaround - for another error related to previous workaround @@ -296,13 +427,16 @@ type procedure SetCapacity(AValue: SizeInt); override; public constructor Create(ACollection: TEnumerable); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers); overload; + {$ENDIF} destructor Destroy; override; procedure Clear; procedure Push(constref AValue: T); function Pop: T; inline; function Peek: T; function Extract: T; inline; - procedure TrimExcess; + procedure TrimExcess; override; end; TObjectList = class(TList) @@ -314,6 +448,9 @@ type constructor Create(AOwnsObjects: Boolean = True); overload; constructor Create(const AComparer: IComparer; AOwnsObjects: Boolean = True); overload; constructor Create(ACollection: TEnumerable; AOwnsObjects: Boolean = True); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean = True); overload; + {$ENDIF} property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; end; @@ -325,6 +462,9 @@ type public constructor Create(AOwnsObjects: Boolean = True); overload; constructor Create(ACollection: TEnumerable; AOwnsObjects: Boolean = True); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean = True); overload; + {$ENDIF} procedure Dequeue; property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; end; @@ -337,6 +477,9 @@ type public constructor Create(AOwnsObjects: Boolean = True); overload; constructor Create(ACollection: TEnumerable; AOwnsObjects: Boolean = True); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean = True); overload; + {$ENDIF} function Pop: T; property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; end; @@ -345,8 +488,476 @@ type {$I inc\generics.dictionariesh.inc} + { TCustomHashSet } + + TCustomSet = class(TEnumerableWithPointers) + protected + FOnNotify: TCollectionNotifyEvent; + public type + PT = ^T; + protected type + TCustomSetEnumerator = class(TEnumerator) + protected var + FEnumerator: TEnumerator; + function DoMoveNext: boolean; override; + function DoGetCurrent: T; override; + function GetCurrent: T; virtual; abstract; + public + constructor Create(ASet: TCustomSet); virtual; abstract; + destructor Destroy; override; + end; + protected + function DoGetEnumerator: TEnumerator; override; + function GetCount: SizeInt; virtual; abstract; + function GetCapacity: SizeInt; virtual; abstract; + procedure SetCapacity(AValue: SizeInt); virtual; abstract; + function GetOnNotify: TCollectionNotifyEvent; virtual; abstract; + procedure SetOnNotify(AValue: TCollectionNotifyEvent); virtual; abstract; + public + constructor Create; virtual; abstract; overload; + constructor Create(ACollection: TEnumerable); overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers); overload; + {$ENDIF} + function GetEnumerator: TCustomSetEnumerator; reintroduce; virtual; abstract; + + function Add(constref AValue: T): Boolean; virtual; abstract; + function Remove(constref AValue: T): Boolean; virtual; abstract; + function Extract(constref AValue: T): T; virtual; abstract; + + procedure Clear; virtual; abstract; + function Contains(constref AValue: T): Boolean; virtual; abstract; + function AddRange(constref AValues: array of T): Boolean; overload; + function AddRange(const AEnumerable: IEnumerable): Boolean; overload; + function AddRange(AEnumerable: TEnumerable): Boolean; overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + function AddRange(AEnumerable: TEnumerableWithPointers): Boolean; overload; + {$ENDIF} + procedure UnionWith(AHashSet: TCustomSet); + procedure IntersectWith(AHashSet: TCustomSet); + procedure ExceptWith(AHashSet: TCustomSet); + procedure SymmetricExceptWith(AHashSet: TCustomSet); + + property Count: SizeInt read GetCount; + property Capacity: SizeInt read GetCapacity write SetCapacity; + procedure TrimExcess; virtual; abstract; + + property OnNotify: TCollectionNotifyEvent read GetOnNotify write SetOnNotify; + end; + + { THashSet } + + THashSet = class(TCustomSet) + private + procedure InternalDictionaryNotify(ASender: TObject; constref AItem: T; AAction: TCollectionNotification); + protected + FInternalDictionary: TOpenAddressingLP; + public type + THashSetEnumerator = class(TCustomSetEnumerator) + protected type + TDictionaryEnumerator = TDictionary.TKeyEnumerator; + function GetCurrent: T; override; + public + constructor Create(ASet: TCustomSet); override; + end; + + TPointersEnumerator = class(TCustomPointersEnumerator) + protected + FEnumerator: TEnumerator; + function DoMoveNext: boolean; override; + function DoGetCurrent: PT; override; + public + constructor Create(AHashSet: THashSet); + end; + protected + function GetPtrEnumerator: TEnumerator; override; + function GetCount: SizeInt; override; + function GetCapacity: SizeInt; override; + procedure SetCapacity(AValue: SizeInt); override; + function GetOnNotify: TCollectionNotifyEvent; override; + procedure SetOnNotify(AValue: TCollectionNotifyEvent); override; + public + constructor Create; override; overload; + constructor Create(const AComparer: IEqualityComparer); virtual; overload; + destructor Destroy; override; + function GetEnumerator: TCustomSetEnumerator; override; + + function Add(constref AValue: T): Boolean; override; + function Remove(constref AValue: T): Boolean; override; + function Extract(constref AValue: T): T; override; + + procedure Clear; override; + function Contains(constref AValue: T): Boolean; override; + + procedure TrimExcess; override; + end; + + TPair = record + public + Key: TKey; + Value: TValue; + Info: TInfo; + end; + + TAVLTreeNode = record + private type + TNodePair = TPair; + public type + PNode = ^TAVLTreeNode; + public + Parent, Left, Right: PNode; + Balance: Integer; + Data: TNodePair; + function Successor: PNode; + function Precessor: PNode; + function TreeDepth: integer; + procedure ConsistencyCheck(ATree: TObject); // workaround for internal error 2012101001 (no generic forward declarations) + function GetCount: SizeInt; + property Key: TKey read Data.Key write Data.Key; + property Value: TValue read Data.Value write Data.Value; + property Info: TInfo read Data.Info write Data.Info; + end; + + TCustomTreeEnumerator = class abstract(TEnumerator) + protected + FCurrent: PNode; + FTree: TTree; + function DoGetCurrent: T; override; + function GetCurrent: T; virtual; abstract; + public + constructor Create(ATree: TObject); + property Current: T read GetCurrent; + end; + + TTreeEnumerable = class abstract(TEnumerableWithPointers) + private + FTree: TTree; + function GetCount: SizeInt; inline; + protected + function GetPtrEnumerator: TEnumerator; override; + function DoGetEnumerator: TTreeEnumerator; override; + public + constructor Create(ATree: TTree); + function ToArray: TArray; override; final; + property Count: SizeInt read GetCount; + end; + + TAVLTreeEnumerator = class(TCustomTreeEnumerator) + protected + FLowToHigh: boolean; + function DoMoveNext: Boolean; override; + public + constructor Create(ATree: TObject; ALowToHigh: boolean = true); + property LowToHigh: boolean read FLowToHigh; + end; + + TNodeNotifyEvent = procedure(ASender: TObject; ANode: PNode; AAction: TCollectionNotification; ADispose: boolean) of object; + + TCustomAVLTreeMap = class + private type + TTree = class(TCustomAVLTreeMap); + public type + TNode = TAVLTreeNode; + PNode = ^TNode; + PPNode = ^PNode; + TTreePair = TPair; + PKey = ^TKey; + PValue = ^TValue; + private type + // type exist only for generic constraint in TNodeCollection (non functional - PPNode has no sense) + TPNodeEnumerator = class(TAVLTreeEnumerator); + private var + FDuplicates: TDuplicates; + FComparer: IComparer; + protected + FCount: SizeInt; + FRoot: PNode; + FKeys: TEnumerable; + FValues: TEnumerable; + FOnNodeNotify: TNodeNotifyEvent; + FOnKeyNotify: TCollectionNotifyEvent; + FOnValueNotify: TCollectionNotifyEvent; + + procedure NodeAdded(ANode: PNode); virtual; + procedure DeletingNode(ANode: PNode; AOrigin: boolean); virtual; + + function DoRemove(ANode: PNode; ACollectionNotification: TCollectionNotification; ADispose: boolean): TValue; + procedure DisposeAllNodes(ANode: PNode); overload; + + function Compare(constref ALeft, ARight: TKey): Integer; inline; + function FindPredecessor(ANode: PNode): PNode; + function FindInsertNode(ANode: PNode; out AInsertNode: PNode): Integer; + + procedure RotateRightRight(ANode: PNode); virtual; + procedure RotateLeftLeft(ANode: PNode); virtual; + procedure RotateRightLeft(ANode: PNode); virtual; + procedure RotateLeftRight(ANode: PNode); virtual; + + procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); inline; + procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); inline; + procedure NodeNotify(ANode: PNode; ACollectionNotification: TCollectionNotification; ADispose: boolean); inline; + procedure SetValue(var AValue: TValue; constref ANewValue: TValue); + + // for reporting + procedure WriteStr(AStream: TStream; const AText: string); + public type + TPairEnumerator = class(TAVLTreeEnumerator) + protected + function GetCurrent: TTreePair; override; + end; + + TNodeEnumerator = class(TAVLTreeEnumerator) + protected + function GetCurrent: PNode; override; + end; + + TKeyEnumerator = class(TAVLTreeEnumerator) + protected + function GetCurrent: TKey; override; + end; + + TPKeyEnumerator = class(TAVLTreeEnumerator) + protected + function GetCurrent: PKey; override; + end; + + TValueEnumerator = class(TAVLTreeEnumerator) + protected + function GetCurrent: TValue; override; + end; + + TPValueEnumerator = class(TAVLTreeEnumerator) + protected + function GetCurrent: PValue; override; + end; + + TNodeCollection = class(TTreeEnumerable) + private + property Ptr; // PPNode has no sense, so hide enumerator for PPNode + end; + + TKeyCollection = class(TTreeEnumerable); + + TValueCollection = class(TTreeEnumerable); + private + FNodes: TNodeCollection; + function GetNodeCollection: TNodeCollection; + procedure InternalAdd(ANode, AParent: PNode); overload; + function InternalAdd(ANode: PNode; ADispisable: boolean): PNode; overload; + procedure InternalDelete(ANode: PNode); + function GetKeys: TKeyCollection; + function GetValues: TValueCollection; + public + constructor Create; virtual; overload; + constructor Create(const AComparer: IComparer); virtual; overload; + + function NewNode: PNode; + function NewNodeArray(ACount: SizeInt): PNode; overload; + procedure NewNodeArray(out AArray: TArray; ACount: SizeInt); overload; + procedure DisposeNode(ANode: PNode); + procedure DisposeNodeArray(ANode: PNode; ACount: SizeInt); overload; + procedure DisposeNodeArray(var AArray: TArray); overload; + + destructor Destroy; override; + function AddNode(ANode: PNode): boolean; overload; inline; + function Add(constref APair: TTreePair): PNode; overload; inline; + function Add(constref AKey: TKey; constref AValue: TValue): PNode; overload; inline; + function Remove(constref AKey: TKey; ADisposeNode: boolean = true): boolean; + function ExtractPair(constref AKey: TKey; ADisposeNode: boolean = true): TTreePair; overload; + function ExtractPair(constref ANode: PNode; ADispose: boolean = true): TTreePair; overload; + function ExtractNode(constref AKey: TKey; ADisposeNode: boolean): PNode; overload; + function ExtractNode(ANode: PNode; ADispose: boolean): PNode; overload; + procedure Delete(ANode: PNode; ADispose: boolean = true); inline; + + function GetEnumerator: TPairEnumerator; + property Nodes: TNodeCollection read GetNodeCollection; + + procedure Clear(ADisposeNodes: Boolean = true); virtual; + + function FindLowest: PNode; + function FindHighest: PNode; + + property Count: SizeInt read FCount; + + property Root: PNode read FRoot; + function Find(constref AKey: TKey): PNode; + function ContainsKey(constref AKey: TKey; out ANode: PNode): boolean; overload; inline; + function ContainsKey(constref AKey: TKey): boolean; overload; inline; + + procedure ConsistencyCheck; virtual; + procedure WriteTreeNode(AStream: TStream; ANode: PNode); + procedure WriteReportToStream(AStream: TStream); + function NodeToReportStr(ANode: PNode): string; virtual; + function ReportAsString: string; + + property Keys: TKeyCollection read GetKeys; + property Values: TValueCollection read GetValues; + property Duplicates: TDuplicates read FDuplicates write FDuplicates; + + property OnNodeNotify: TNodeNotifyEvent read FOnNodeNotify write FOnNodeNotify; + property OnKeyNotify: TCollectionNotifyEvent read FOnKeyNotify write FOnKeyNotify; + property OnValueNotify: TCollectionNotifyEvent read FOnValueNotify write FOnValueNotify; + end; + + TAVLTreeMap = class(TCustomAVLTreeMap) + end; + + TIndexedAVLTreeMap = class(TCustomAVLTreeMap) + protected + FLastNode: PNode; + FLastIndex: SizeInt; + + procedure RotateRightRight(ANode: PNode); override; + procedure RotateLeftLeft(ANode: PNode); override; + procedure RotateRightLeft(ANode: PNode); override; + procedure RotateLeftRight(ANode: PNode); override; + + procedure NodeAdded(ANode: PNode); override; + procedure DeletingNode(ANode: PNode; AOrigin: boolean); override; + public + function GetNodeAtIndex(AIndex: SizeInt): PNode; + function NodeToIndex(ANode: PNode): SizeInt; + + procedure ConsistencyCheck; override; + function NodeToReportStr(ANode: PNode): string; override; + end; + + TAVLTree = class(TAVLTreeMap) + protected + property OnKeyNotify; + property OnValueNotify; + public type + TItemEnumerator = TKeyEnumerator; + public + function Add(constref AValue: T): PNode; reintroduce; inline; + function AddNode(ANode: PNode): boolean; reintroduce; inline; + + property OnNotify: TCollectionNotifyEvent read FOnKeyNotify write FOnKeyNotify; + end; + + TIndexedAVLTree = class(TIndexedAVLTreeMap) + protected + property OnKeyNotify; + property OnValueNotify; + public type + TItemEnumerator = TKeyEnumerator; + public + function Add(constref AValue: T): PNode; reintroduce; inline; + function AddNode(ANode: PNode): boolean; reintroduce; inline; + + property OnNotify: TCollectionNotifyEvent read FOnKeyNotify write FOnKeyNotify; + end; + + TSortedSet = class(TCustomSet) + private + procedure InternalAVLTreeNotify(ASender: TObject; constref AItem: T; AAction: TCollectionNotification); + protected + FInternalTree: TAVLTree; + public type + TSortedSetEnumerator = class(TCustomSetEnumerator) + protected type + TTreeEnumerator = TAVLTree.TItemEnumerator; + function GetCurrent: T; override; + public + constructor Create(ASet: TCustomSet); override; + end; + + TPointersEnumerator = class(TCustomPointersEnumerator) + protected + FEnumerator: TEnumerator; + function DoMoveNext: boolean; override; + function DoGetCurrent: PT; override; + public + constructor Create(ASortedSet: TSortedSet); + end; + protected + function GetPtrEnumerator: TEnumerator; override; + function GetCount: SizeInt; override; + function GetCapacity: SizeInt; override; + procedure SetCapacity(AValue: SizeInt); override; + function GetOnNotify: TCollectionNotifyEvent; override; + procedure SetOnNotify(AValue: TCollectionNotifyEvent); override; + public + constructor Create; override; overload; + constructor Create(const AComparer: IComparer); virtual; overload; + destructor Destroy; override; + function GetEnumerator: TCustomSetEnumerator; override; + + function Add(constref AValue: T): Boolean; override; + function Remove(constref AValue: T): Boolean; override; + function Extract(constref AValue: T): T; override; + procedure Clear; override; + function Contains(constref AValue: T): Boolean; override; + + procedure TrimExcess; override; + end; + + TSortedHashSet = class(TCustomSet) + private + procedure InternalDictionaryNotify(ASender: TObject; constref AItem: PT; AAction: TCollectionNotification); + protected + FInternalDictionary: TOpenAddressingLP; + FInternalTree: TAVLTree; + function DoGetEnumerator: TEnumerator; override; + function GetCount: SizeInt; override; + function GetCapacity: SizeInt; override; + procedure SetCapacity(AValue: SizeInt); override; + function GetOnNotify: TCollectionNotifyEvent; override; + procedure SetOnNotify(AValue: TCollectionNotifyEvent); override; + protected type + TSortedHashSetEqualityComparer = class(TInterfacedObject, IEqualityComparer) + private + FComparer: IComparer; + FEqualityComparer: IEqualityComparer; + function Equals(constref ALeft, ARight: PT): Boolean; + function GetHashCode(constref AValue: PT): UInt32; + public + constructor Create(const AComparer: IComparer); overload; + constructor Create(const AEqualityComparer: IEqualityComparer); overload; + constructor Create(const AComparer: IComparer; const AEqualityComparer: IEqualityComparer); overload; + end; + public type + TSortedHashSetEnumerator = class(TCustomSetEnumerator) + protected type + TTreeEnumerator = TAVLTree.TItemEnumerator; + function GetCurrent: T; override; + public + constructor Create(ASet: TCustomSet); override; + end; + + TPointersEnumerator = class(TCustomPointersEnumerator) + protected + FEnumerator: TEnumerator; + function DoMoveNext: boolean; override; + function DoGetCurrent: PT; override; + public + constructor Create(ASortedHashSet: TSortedHashSet); + end; + protected + function GetPtrEnumerator: TEnumerator; override; + public + constructor Create; override; overload; + constructor Create(const AComparer: IEqualityComparer); overload; + constructor Create(const AComparer: IComparer); overload; + constructor Create(const AComparer: IComparer; const AEqualityComparer: IEqualityComparer); overload; + destructor Destroy; override; + function GetEnumerator: TCustomSetEnumerator; override; + + function Add(constref AValue: T): Boolean; override; + function Remove(constref AValue: T): Boolean; override; + function Extract(constref AValue: T): T; override; + procedure Clear; override; + function Contains(constref AValue: T): Boolean; override; + + procedure TrimExcess; override; + end; + function InCircularRange(ABottom, AItem, ATop: SizeInt): Boolean; +var + EmptyRecord: TEmptyRecord; + implementation function InCircularRange(ABottom, AItem, ATop: SizeInt): Boolean; @@ -371,6 +982,18 @@ begin Result := BinarySearch(AValues, AItem, AFoundIndex, TComparerBugHack.Default, Low(AValues), Length(AValues)); end; +class function TCustomArrayHelper.BinarySearch(constref AValues: array of T; constref AItem: T; + out ASearchResult: TBinarySearchResult; const AComparer: IComparer): Boolean; +begin + Result := BinarySearch(AValues, AItem, ASearchResult, AComparer, Low(AValues), Length(AValues)); +end; + +class function TCustomArrayHelper.BinarySearch(constref AValues: array of T; constref AItem: T; + out ASearchResult: TBinarySearchResult): Boolean; +begin + Result := BinarySearch(AValues, AItem, ASearchResult, TComparerBugHack.Default, Low(AValues), Length(AValues)); +end; + class procedure TCustomArrayHelper.Sort(var AValues: array of T); begin QuickSort(AValues, Low(AValues), High(AValues), TComparerBugHack.Default); @@ -406,9 +1029,9 @@ begin P := AValues[ALeft + (ARight - ALeft) shr 1]; repeat while AComparer.Compare(AValues[I], P) < 0 do - I += 1; + Inc(I); while AComparer.Compare(AValues[J], P) > 0 do - J -= 1; + Dec(J); if I <= J then begin if I <> J then @@ -417,8 +1040,8 @@ begin AValues[I] := AValues[J]; AValues[J] := Q; end; - I += 1; - J -= 1; + Inc(I); + Dec(J); end; until I > J; // sort the smaller range recursively @@ -439,6 +1062,69 @@ begin until ALeft >= ARight; end; +class function TArrayHelper.BinarySearch(constref AValues: array of T; constref AItem: T; + out ASearchResult: TBinarySearchResult; const AComparer: IComparer; + AIndex, ACount: SizeInt): Boolean; +var + imin, imax, imid: Int32; +begin + // continually narrow search until just one element remains + imin := AIndex; + imax := Pred(AIndex + ACount); + + // http://en.wikipedia.org/wiki/Binary_search_algorithm + while (imin < imax) do + begin + imid := imin + ((imax - imin) shr 1); + + // code must guarantee the interval is reduced at each iteration + // assert(imid < imax); + // note: 0 <= imin < imax implies imid will always be less than imax + + ASearchResult.CompareResult := AComparer.Compare(AValues[imid], AItem); + // reduce the search + if (ASearchResult.CompareResult < 0) then + imin := imid + 1 + else + begin + imax := imid; + if ASearchResult.CompareResult = 0 then + begin + ASearchResult.FoundIndex := imid; + ASearchResult.CandidateIndex := imid; + Exit(True); + end; + end; + end; + // At exit of while: + // if A[] is empty, then imax < imin + // otherwise imax == imin + + // deferred test for equality + + if (imax = imin) then + begin + ASearchResult.CompareResult := AComparer.Compare(AValues[imin], AItem); + ASearchResult.CandidateIndex := imin; + if (ASearchResult.CompareResult = 0) then + begin + ASearchResult.FoundIndex := imin; + Exit(True); + end else + begin + ASearchResult.FoundIndex := -1; + Exit(False); + end; + end + else + begin + ASearchResult.CompareResult := 0; + ASearchResult.FoundIndex := -1; + ASearchResult.CandidateIndex := -1; + Exit(False); + end; +end; + class function TArrayHelper.BinarySearch(constref AValues: array of T; constref AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; @@ -546,21 +1232,40 @@ begin end; end; +{ TCustomPointersCollection } + +function TCustomPointersCollection.Enumerable: TLocalEnumerable; +begin + Result := TLocalEnumerable(@Self); +end; + +function TCustomPointersCollection.GetEnumerator: TEnumerator; +begin + Result := Enumerable.GetPtrEnumerator; +end; + +{ TEnumerableWithPointers } + +function TEnumerableWithPointers.GetPtr: PPointersCollection; +begin + Result := PPointersCollection(Self); +end; + { TCustomList } function TCustomList.PrepareAddingItem: SizeInt; begin Result := Length(FItems); - if (FItemsLength < 4) and (Result < 4) then + if (FLength < 4) and (Result < 4) then SetLength(FItems, 4) - else if FItemsLength = High(FItemsLength) then + else if FLength = High(FLength) then OutOfMemoryError - else if FItemsLength = Result then + else if FLength = Result then SetLength(FItems, CUSTOM_LIST_CAPACITY_INC); - Result := FItemsLength; - Inc(FItemsLength); + Result := FLength; + Inc(FLength); end; function TCustomList.PrepareAddingRange(ACount: SizeInt): SizeInt; @@ -568,22 +1273,22 @@ begin if ACount < 0 then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); if ACount = 0 then - Exit(FItemsLength - 1); + Exit(FLength - 1); - if (FItemsLength = 0) and (Length(FItems) = 0) then + if (FLength = 0) and (Length(FItems) = 0) then SetLength(FItems, 4) - else if FItemsLength = High(FItemsLength) then + else if FLength = High(FLength) then OutOfMemoryError; Result := Length(FItems); - while Pred(FItemsLength + ACount) >= Result do + while Pred(FLength + ACount) >= Result do begin SetLength(FItems, CUSTOM_LIST_CAPACITY_INC); Result := Length(FItems); end; - Result := FItemsLength; - Inc(FItemsLength, ACount); + Result := FLength; + Inc(FLength, ACount); end; function TCustomList.ToArray: TArray; @@ -593,7 +1298,7 @@ end; function TCustomList.GetCount: SizeInt; begin - Result := FItemsLength; + Result := FLength; end; procedure TCustomList.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); @@ -604,17 +1309,17 @@ end; function TCustomList.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; begin - if (AIndex < 0) or (AIndex >= FItemsLength) then + if (AIndex < 0) or (AIndex >= FLength) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); Result := FItems[AIndex]; - Dec(FItemsLength); + Dec(FLength); FItems[AIndex] := Default(T); - if AIndex <> FItemsLength then + if AIndex <> FLength then begin - System.Move(FItems[AIndex + 1], FItems[AIndex], (FItemsLength - AIndex) * SizeOf(T)); - FillChar(FItems[FItemsLength], SizeOf(T), 0); + System.Move(FItems[AIndex + 1], FItems[AIndex], (FLength - AIndex) * SizeOf(T)); + FillChar(FItems[FLength], SizeOf(T), 0); end; Notify(Result, ACollectionNotification); @@ -630,7 +1335,7 @@ end; function TCustomListEnumerator.DoMoveNext: boolean; begin Inc(FIndex); - Result := (FList.FItemsLength <> 0) and (FIndex < FList.FItemsLength) + Result := (FList.FLength <> 0) and (FIndex < FList.FLength) end; function TCustomListEnumerator.DoGetCurrent: T; @@ -650,15 +1355,48 @@ begin FList := AList; end; +{ TCustomListWithPointers.TPointersEnumerator } + +function TCustomListWithPointers.TPointersEnumerator.DoMoveNext: boolean; +begin + Inc(FIndex); + Result := (FList.FLength <> 0) and (FIndex < FList.FLength) +end; + +function TCustomListWithPointers.TPointersEnumerator.DoGetCurrent: PT; +begin + Result := @FList.FItems[FIndex];; +end; + +constructor TCustomListWithPointers.TPointersEnumerator.Create(AList: TCustomListWithPointers); +begin + inherited Create; + FIndex := -1; + FList := AList; +end; + +{ TCustomListWithPointers } + +function TCustomListWithPointers.GetPtrEnumerator: TEnumerator; +begin + Result := TPointersEnumerator.Create(Self); +end; + { TList } +procedure TList.InitializeList; +begin +end; + constructor TList.Create; begin + InitializeList; FComparer := TComparer.Default; end; constructor TList.Create(const AComparer: IComparer); begin + InitializeList; FComparer := AComparer; end; @@ -671,6 +1409,17 @@ begin Add(LItem); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TList.Create(ACollection: TEnumerableWithPointers); +var + LItem: PT; +begin + Create; + for LItem in ACollection.Ptr^ do + Add(LItem^); +end; +{$ENDIF} + destructor TList.Destroy; begin SetCapacity(0); @@ -694,7 +1443,7 @@ begin if AValue < Count then DeleteRange(AValue, Count - AValue); - FItemsLength := AValue; + FLength := AValue; end; function TList.GetItem(AIndex: SizeInt): T; @@ -708,9 +1457,10 @@ end; procedure TList.SetItem(AIndex: SizeInt; const AValue: T); begin if (AIndex < 0) or (AIndex >= Count) then - raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); - + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + Notify(FItems[AIndex], cnRemoved); FItems[AIndex] := AValue; + Notify(AValue, cnAdded); end; function TList.GetEnumerator: TEnumerator; @@ -751,11 +1501,18 @@ begin Add(LValue); end; -procedure TList.Insert(AIndex: SizeInt; constref AValue: T); +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +procedure TList.AddRange(AEnumerable: TEnumerableWithPointers); +var + LValue: PT; begin - if (AIndex < 0) or (AIndex > Count) then - raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + for LValue in AEnumerable.Ptr^ do + Add(LValue^); +end; +{$ENDIF} +procedure TList.InternalInsert(AIndex: SizeInt; constref AValue: T); +begin if AIndex <> PrepareAddingItem then begin System.Move(FItems[AIndex], FItems[AIndex + 1], ((Count - AIndex) - 1) * SizeOf(T)); @@ -766,6 +1523,14 @@ begin Notify(AValue, cnAdded); end; +procedure TList.Insert(AIndex: SizeInt; constref AValue: T); +begin + if (AIndex < 0) or (AIndex > Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + InternalInsert(AIndex, AValue); +end; + procedure TList.InsertRange(AIndex: SizeInt; constref AValues: array of T); var i: SizeInt; @@ -805,7 +1570,7 @@ begin i := 0; for LValue in AEnumerable do begin - Insert(Aindex + i, LValue); + InternalInsert(Aindex + i, LValue); Inc(i); end; end; @@ -821,11 +1586,29 @@ begin i := 0; for LValue in AEnumerable do begin - Insert(Aindex + i, LValue); + InternalInsert(Aindex + i, LValue); Inc(i); end; end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +procedure TList.InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerableWithPointers); +var + LValue: PT; + i: SizeInt; +begin + if (AIndex < 0) or (AIndex > Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + i := 0; + for LValue in AEnumerable.Ptr^ do + begin + InternalInsert(Aindex + i, LValue^); + Inc(i); + end; +end; +{$ENDIF} + function TList.Remove(constref AValue: T): SizeInt; begin Result := IndexOf(AValue); @@ -850,7 +1633,7 @@ begin if (ACount < 0) or (AIndex < 0) or (AIndex + ACount > Count) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); - SetLength(LDeleted, Count); + SetLength(LDeleted, ACount); System.Move(FItems[AIndex], LDeleted[0], ACount * SizeOf(T)); LMoveDelta := Count - (AIndex + ACount); @@ -863,7 +1646,7 @@ begin FillChar(FItems[Count - ACount], ACount * SizeOf(T), #0); end; - FItemsLength -= ACount; + Dec(FLength, ACount); for i := 0 to High(LDeleted) do Notify(LDeleted[i], cnRemoved); @@ -991,12 +1774,142 @@ end; function TList.BinarySearch(constref AItem: T; out AIndex: SizeInt): Boolean; begin - Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex); + Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex, FComparer, 0, Count); end; function TList.BinarySearch(constref AItem: T; out AIndex: SizeInt; const AComparer: IComparer): Boolean; begin - Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex, AComparer); + Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex, AComparer, 0, Count); +end; + +{ TSortedList } + +procedure TSortedList.InitializeList; +begin + FSortStyle := cssAuto; +end; + +function TSortedList.Add(constref AValue: T): SizeInt; +var + LSearchResult: TBinarySearchResult; +begin + if SortStyle <> cssAuto then + Exit(inherited Add(AValue)); + if TArrayHelperBugHack.BinarySearch(FItems, AValue, LSearchResult, FComparer, 0, Count) then + case FDuplicates of + dupAccept: Result := LSearchResult.FoundIndex; + dupIgnore: Exit(LSearchResult.FoundIndex); + dupError: raise EListError.Create(SCollectionDuplicate); + end + else + begin + if LSearchResult.CandidateIndex = -1 then + Result := 0 + else + if LSearchResult.CompareResult > 0 then + Result := LSearchResult.CandidateIndex + else + Result := LSearchResult.CandidateIndex + 1; + end; + + InternalInsert(Result, AValue); +end; + +procedure TSortedList.Insert(AIndex: SizeInt; constref AValue: T); +begin + if FSortStyle = cssAuto then + raise EListError.Create(SSortedListError) + else + inherited; +end; + +procedure TSortedList.Exchange(AIndex1, AIndex2: SizeInt); +begin + if FSortStyle = cssAuto then + raise EListError.Create(SSortedListError) + else + inherited; +end; + +procedure TSortedList.Move(AIndex, ANewIndex: SizeInt); +begin + if FSortStyle = cssAuto then + raise EListError.Create(SSortedListError) + else + inherited; +end; + +procedure TSortedList.AddRange(constref AValues: array of T); +var + i: T; +begin + for i in AValues do + Add(i); +end; + +procedure TSortedList.InsertRange(AIndex: SizeInt; constref AValues: array of T); +var + LValue: T; + i: SizeInt; +begin + if (AIndex < 0) or (AIndex > Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + i := 0; + for LValue in AValues do + begin + InternalInsert(AIndex + i, LValue); + Inc(i); + end; +end; + +function TSortedList.GetSorted: boolean; +begin + Result := FSortStyle in [cssAuto, cssUser]; +end; + +procedure TSortedList.SetSorted(AValue: boolean); +begin + if AValue then + SortStyle := cssAuto + else + SortStyle := cssNone; +end; + +procedure TSortedList.SetSortStyle(AValue: TCollectionSortStyle); +begin + if FSortStyle = AValue then + Exit; + if AValue = cssAuto then + Sort; + FSortStyle := AValue; +end; + +function TSortedList.ConsistencyCheck(ARaiseException: boolean = true): boolean; +var + i: Integer; + LCompare: SizeInt; +begin + if Sorted then + for i := 0 to Count-2 do + begin + LCompare := FComparer.Compare(FItems[i], FItems[i+1]); + if LCompare = 0 then + begin + if Duplicates <> dupAccept then + if ARaiseException then + raise EListError.Create(SCollectionDuplicate) + else + Exit(False) + end + else + if LCompare > 0 then + if ARaiseException then + raise EListError.Create(SCollectionInconsistency) + else + Exit(False) + end; + Result := True; end; { TThreadList } @@ -1005,7 +1918,9 @@ constructor TThreadList.Create; begin inherited Create; FDuplicates:=dupIgnore; +{$ifdef FPC_HAS_FEATURE_THREADING} InitCriticalSection(FLock); +{$endif} FList := TList.Create; end; @@ -1017,7 +1932,9 @@ begin inherited Destroy; finally UnlockList; +{$ifdef FPC_HAS_FEATURE_THREADING} DoneCriticalSection(FLock); +{$endif} end; end; @@ -1057,12 +1974,36 @@ end; function TThreadList.LockList: TList; begin Result:=FList; +{$ifdef FPC_HAS_FEATURE_THREADING} System.EnterCriticalSection(FLock); +{$endif} end; procedure TThreadList.UnlockList; begin +{$ifdef FPC_HAS_FEATURE_THREADING} System.LeaveCriticalSection(FLock); +{$endif} +end; + +{ TQueue.TPointersEnumerator } + +function TQueue.TPointersEnumerator.DoMoveNext: boolean; +begin + Inc(FIndex); + Result := (FQueue.FLength <> 0) and (FIndex < FQueue.FLength) +end; + +function TQueue.TPointersEnumerator.DoGetCurrent: PT; +begin + Result := @FQueue.FItems[FIndex]; +end; + +constructor TQueue.TPointersEnumerator.Create(AQueue: TQueue); +begin + inherited Create; + FIndex := Pred(AQueue.FLow); + FQueue := AQueue; end; { TQueue.TEnumerator } @@ -1076,6 +2017,11 @@ end; { TQueue } +function TQueue.GetPtrEnumerator: TEnumerator; +begin + Result := TPointersenumerator.Create(Self); +end; + function TQueue.GetEnumerator: TEnumerator; begin Result := TEnumerator.Create(Self); @@ -1090,13 +2036,13 @@ function TQueue.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectio begin Result := FItems[AIndex]; FItems[AIndex] := Default(T); - Notify(Result, ACollectionNotification); - FLow += 1; - if FLow = FItemsLength then + Inc(FLow); + if FLow = FLength then begin FLow := 0; - FItemsLength := 0; + FLength := 0; end; + Notify(Result, ACollectionNotification); end; procedure TQueue.SetCapacity(AValue: SizeInt); @@ -1104,23 +2050,23 @@ begin if AValue < Count then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); - if AValue = FItemsLength then + if AValue = FLength then Exit; if (Count > 0) and (FLow > 0) then begin Move(FItems[FLow], FItems[0], Count * SizeOf(T)); - FillChar(FItems[Count], (FItemsLength - Count) * SizeOf(T), #0); + FillChar(FItems[Count], (FLength - Count) * SizeOf(T), #0); end; SetLength(FItems, AValue); - FItemsLength := Count; + FLength := Count; FLow := 0; end; function TQueue.GetCount: SizeInt; begin - Result := FItemsLength - FLow; + Result := FLength - FLow; end; constructor TQueue.Create(ACollection: TEnumerable); @@ -1131,6 +2077,16 @@ begin Enqueue(LItem); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TQueue.Create(ACollection: TEnumerableWithPointers); +var + LItem: PT; +begin + for LItem in ACollection.Ptr^ do + Enqueue(LItem^); +end; +{$ENDIF} + destructor TQueue.Destroy; begin Clear; @@ -1168,7 +2124,7 @@ begin while Count <> 0 do Dequeue; FLow := 0; - FItemsLength := 0; + FLength := 0; end; procedure TQueue.TrimExcess; @@ -1196,6 +2152,16 @@ begin Push(LItem); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TStack.Create(ACollection: TEnumerableWithPointers); +var + LItem: PT; +begin + for LItem in ACollection.Ptr^ do + Push(LItem^); +end; +{$ENDIF} + function TStack.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; begin if AIndex < 0 then @@ -1203,7 +2169,7 @@ begin Result := FItems[AIndex]; FItems[AIndex] := Default(T); - FItemsLength -= 1; + Dec(FLength); Notify(Result, ACollectionNotification); end; @@ -1237,7 +2203,7 @@ end; function TStack.Pop: T; begin - Result := DoRemove(FItemsLength - 1, cnRemoved); + Result := DoRemove(FLength - 1, cnRemoved); end; function TStack.Peek: T; @@ -1245,12 +2211,12 @@ begin if (Count = 0) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); - Result := FItems[FItemsLength - 1]; + Result := FItems[FLength - 1]; end; function TStack.Extract: T; begin - Result := DoRemove(FItemsLength - 1, cnExtracted); + Result := DoRemove(FLength - 1, cnExtracted); end; procedure TStack.TrimExcess; @@ -1289,6 +2255,15 @@ begin FObjectsOwner := AOwnsObjects; end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TObjectList.Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean); +begin + inherited Create(ACollection); + + FObjectsOwner := AOwnsObjects; +end; +{$ENDIF} + { TObjectQueue } procedure TObjectQueue.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); @@ -1312,6 +2287,15 @@ begin FObjectsOwner := AOwnsObjects; end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TObjectQueue.Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean); +begin + inherited Create(ACollection); + + FObjectsOwner := AOwnsObjects; +end; +{$ENDIF} + procedure TObjectQueue.Dequeue; begin inherited Dequeue; @@ -1340,6 +2324,15 @@ begin FObjectsOwner := AOwnsObjects; end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TObjectStack.Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean); +begin + inherited Create(ACollection); + + FObjectsOwner := AOwnsObjects; +end; +{$ENDIF} + function TObjectStack.Pop: T; begin Result := inherited Pop; @@ -1347,4 +2340,1819 @@ end; {$I inc\generics.dictionaries.inc} +{ TCustomSet.TCustomSetEnumerator } + +function TCustomSet.TCustomSetEnumerator.DoMoveNext: boolean; +begin + Result := FEnumerator.DoMoveNext; +end; + +function TCustomSet.TCustomSetEnumerator.DoGetCurrent: T; +begin + Result := FEnumerator.DoGetCurrent; +end; + +destructor TCustomSet.TCustomSetEnumerator.Destroy; +begin + FEnumerator.Free; +end; + +{ TCustomSet } + +function TCustomSet.DoGetEnumerator: Generics.Collections.TEnumerator; +begin + Result := GetEnumerator; +end; + +constructor TCustomSet.Create(ACollection: TEnumerable); +var + i: T; +begin + Create; + for i in ACollection do + Add(i); +end; + +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TCustomSet.Create(ACollection: TEnumerableWithPointers); +var + i: PT; +begin + Create; + for i in ACollection.Ptr^ do + Add(i^); +end; +{$ENDIF} + +function TCustomSet.AddRange(constref AValues: array of T): Boolean; +var + i: T; +begin + Result := True; + for i in AValues do + Result := Add(i) and Result; +end; + +function TCustomSet.AddRange(const AEnumerable: IEnumerable): Boolean; +var + i: T; +begin + Result := True; + for i in AEnumerable do + Result := Add(i) and Result; +end; + +function TCustomSet.AddRange(AEnumerable: TEnumerable): Boolean; +var + i: T; +begin + Result := True; + for i in AEnumerable do + Result := Add(i) and Result; +end; + +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +function TCustomSet.AddRange(AEnumerable: TEnumerableWithPointers): Boolean; +var + i: PT; +begin + Result := True; + for i in AEnumerable.Ptr^ do + Result := Add(i^) and Result; +end; +{$ENDIF} + +procedure TCustomSet.UnionWith(AHashSet: TCustomSet); +var + i: PT; +begin + for i in AHashSet.Ptr^ do + Add(i^); +end; + +procedure TCustomSet.IntersectWith(AHashSet: TCustomSet); +var + LList: TList; + i: PT; +begin + LList := TList.Create; + + for i in Ptr^ do + if not AHashSet.Contains(i^) then + LList.Add(i); + + for i in LList do + Remove(i^); + + LList.Free; +end; + +procedure TCustomSet.ExceptWith(AHashSet: TCustomSet); +var + i: PT; +begin + for i in AHashSet.Ptr^ do + Remove(i^); +end; + +procedure TCustomSet.SymmetricExceptWith(AHashSet: TCustomSet); +var + LList: TList; + i: PT; +begin + LList := TList.Create; + + for i in AHashSet.Ptr^ do + if Contains(i^) then + LList.Add(i) + else + Add(i^); + + for i in LList do + Remove(i^); + + LList.Free; +end; + +{ THashSet.THashSetEnumerator } + +function THashSet.THashSetEnumerator.GetCurrent: T; +begin + Result := TDictionaryEnumerator(FEnumerator).GetCurrent; +end; + +constructor THashSet.THashSetEnumerator.Create(ASet: TCustomSet); +begin + TDictionaryEnumerator(FEnumerator) := THashSet(ASet).FInternalDictionary.Keys.DoGetEnumerator; +end; + +{ THashSet.TPointersEnumerator } + +function THashSet.TPointersEnumerator.DoMoveNext: boolean; +begin + Result := FEnumerator.MoveNext; +end; + +function THashSet.TPointersEnumerator.DoGetCurrent: PT; +begin + Result := FEnumerator.Current; +end; + +constructor THashSet.TPointersEnumerator.Create(AHashSet: THashSet); +begin + FEnumerator := AHashSet.FInternalDictionary.Keys.Ptr^.GetEnumerator; +end; + +{ THashSet } + +procedure THashSet.InternalDictionaryNotify(ASender: TObject; constref AItem: T; AAction: TCollectionNotification); +begin + FOnNotify(Self, AItem, AAction); +end; + +function THashSet.GetPtrEnumerator: TEnumerator; +begin + Result := TPointersEnumerator.Create(Self); +end; + +function THashSet.GetCount: SizeInt; +begin + Result := FInternalDictionary.Count; +end; + +function THashSet.GetCapacity: SizeInt; +begin + Result := FInternalDictionary.Capacity; +end; + +procedure THashSet.SetCapacity(AValue: SizeInt); +begin + FInternalDictionary.Capacity := AValue; +end; + +function THashSet.GetOnNotify: TCollectionNotifyEvent; +begin + Result := FInternalDictionary.OnKeyNotify; +end; + +procedure THashSet.SetOnNotify(AValue: TCollectionNotifyEvent); +begin + FOnNotify := AValue; + if Assigned(AValue) then + FInternalDictionary.OnKeyNotify := InternalDictionaryNotify + else + FInternalDictionary.OnKeyNotify := nil; +end; + +function THashSet.GetEnumerator: TCustomSetEnumerator; +begin + Result := THashSetEnumerator.Create(Self); +end; + +constructor THashSet.Create; +begin + FInternalDictionary := TOpenAddressingLP.Create; +end; + +constructor THashSet.Create(const AComparer: IEqualityComparer); +begin + FInternalDictionary := TOpenAddressingLP.Create(AComparer); +end; + +destructor THashSet.Destroy; +begin + FInternalDictionary.Free; +end; + +function THashSet.Add(constref AValue: T): Boolean; +begin + Result := not FInternalDictionary.ContainsKey(AValue); + if Result then + FInternalDictionary.Add(AValue, EmptyRecord); +end; + +function THashSet.Remove(constref AValue: T): Boolean; +var + LIndex: SizeInt; +begin + LIndex := FInternalDictionary.FindBucketIndex(AValue); + Result := LIndex >= 0; + if Result then + FInternalDictionary.DoRemove(LIndex, cnRemoved); +end; + +function THashSet.Extract(constref AValue: T): T; +var + LIndex: SizeInt; +begin + LIndex := FInternalDictionary.FindBucketIndex(AValue); + if LIndex < 0 then + Exit(Default(T)); + + Result := AValue; + FInternalDictionary.DoRemove(LIndex, cnExtracted); +end; + +procedure THashSet.Clear; +begin + FInternalDictionary.Clear; +end; + +function THashSet.Contains(constref AValue: T): Boolean; +begin + Result := FInternalDictionary.ContainsKey(AValue); +end; + +procedure THashSet.TrimExcess; +begin + FInternalDictionary.TrimExcess; +end; + +{ TAVLTreeNode } + +function TAVLTreeNode.Successor: PNode; +begin + Result:=Right; + if Result<>nil then begin + while (Result.Left<>nil) do Result:=Result.Left; + end else begin + Result:=@Self; + while (Result.Parent<>nil) and (Result.Parent.Right=Result) do + Result:=Result.Parent; + Result:=Result.Parent; + end; +end; + +function TAVLTreeNode.Precessor: PNode; +begin + Result:=Left; + if Result<>nil then begin + while (Result.Right<>nil) do Result:=Result.Right; + end else begin + Result:=@Self; + while (Result.Parent<>nil) and (Result.Parent.Left=Result) do + Result:=Result.Parent; + Result:=Result.Parent; + end; +end; + +function TAVLTreeNode.TreeDepth: integer; +// longest WAY down. e.g. only one node => 0 ! +var LeftDepth, RightDepth: integer; +begin + if Left<>nil then + LeftDepth:=Left.TreeDepth+1 + else + LeftDepth:=0; + if Right<>nil then + RightDepth:=Right.TreeDepth+1 + else + RightDepth:=0; + if LeftDepth>RightDepth then + Result:=LeftDepth + else + Result:=RightDepth; +end; + +procedure TAVLTreeNode.ConsistencyCheck(ATree: TObject); +var + LTree: TTree absolute ATree; + LeftDepth: SizeInt; + RightDepth: SizeInt; +begin + // test left child + if Left<>nil then begin + if Left.Parent<>@Self then + raise EAVLTree.Create('Left.Parent<>Self'); + if LTree.Compare(Left.Data.Key,Data.Key)>0 then + raise EAVLTree.Create('Compare(Left.Data,Data)>0'); + Left.ConsistencyCheck(LTree); + end; + // test right child + if Right<>nil then begin + if Right.Parent<>@Self then + raise EAVLTree.Create('Right.Parent<>Self'); + if LTree.Compare(Data.Key,Right.Data.Key)>0 then + raise EAVLTree.Create('Compare(Data,Right.Data)>0'); + Right.ConsistencyCheck(LTree); + end; + // test balance + if Left<>nil then + LeftDepth:=Left.TreeDepth+1 + else + LeftDepth:=0; + if Right<>nil then + RightDepth:=Right.TreeDepth+1 + else + RightDepth:=0; + if Balance<>(LeftDepth-RightDepth) then + raise EAVLTree.CreateFmt('Balance[%d]<>(RightDepth[%d]-LeftDepth[%d])', [Balance, RightDepth, LeftDepth]); +end; + +function TAVLTreeNode.GetCount: SizeInt; +begin + Result:=1; + if Assigned(Left) then Inc(Result,Left.GetCount); + if Assigned(Right) then Inc(Result,Right.GetCount); +end; + +{ TCustomTreeEnumerator } + +function TCustomTreeEnumerator.DoGetCurrent: T; +begin + Result := GetCurrent; +end; + +constructor TCustomTreeEnumerator.Create(ATree: TObject); +begin + TObject(FTree) := ATree; +end; + +{ TTreeEnumerable } + +function TTreeEnumerable.GetCount: SizeInt; +begin + Result := FTree.Count; +end; + +function TTreeEnumerable.GetPtrEnumerator: TEnumerator; +begin + Result := TTreePointersEnumerator.Create(FTree); +end; + +constructor TTreeEnumerable.Create( + ATree: TTree); +begin + FTree := ATree; +end; + +function TTreeEnumerable. + DoGetEnumerator: TTreeEnumerator; +begin + Result := TTreeEnumerator.Create(FTree); +end; + +function TTreeEnumerable.ToArray: TArray; +begin + Result := ToArrayImpl(FTree.Count); +end; + +{ TAVLTreeEnumerator } + +function TAVLTreeEnumerator.DoMoveNext: Boolean; +begin + if FLowToHigh then begin + if FCurrent<>nil then + FCurrent:=FCurrent.Successor + else + FCurrent:=FTree.FindLowest; + end else begin + if FCurrent<>nil then + FCurrent:=FCurrent.Precessor + else + FCurrent:=FTree.FindHighest; + end; + Result:=FCurrent<>nil; +end; + +constructor TAVLTreeEnumerator.Create(ATree: TObject; ALowToHigh: boolean); +begin + inherited Create(ATree); + FLowToHigh:=aLowToHigh; +end; + +{ TCustomAVLTreeMap.TPairEnumerator } + +function TCustomAVLTreeMap.TPairEnumerator.GetCurrent: TTreePair; +begin + Result := TTreePair((@FCurrent.Data)^); +end; + +{ TCustomAVLTreeMap.TNodeEnumerator } + +function TCustomAVLTreeMap.TNodeEnumerator.GetCurrent: PNode; +begin + Result := FCurrent; +end; + +{ TCustomAVLTreeMap.TKeyEnumerator } + +function TCustomAVLTreeMap.TKeyEnumerator.GetCurrent: TKey; +begin + Result := FCurrent.Key; +end; + +{ TCustomAVLTreeMap.TPKeyEnumerator } + +function TCustomAVLTreeMap.TPKeyEnumerator.GetCurrent: PKey; +begin + Result := @FCurrent.Data.Key; +end; + +{ TCustomAVLTreeMap.TValueEnumerator } + +function TCustomAVLTreeMap.TValueEnumerator.GetCurrent: TValue; +begin + Result := FCurrent.Value; +end; + +{ TCustomAVLTreeMap.TValueEnumerator } + +function TCustomAVLTreeMap.TPValueEnumerator.GetCurrent: PValue; +begin + Result := @FCurrent.Data.Value; +end; + +{ TCustomAVLTreeMap } + +procedure TCustomAVLTreeMap.NodeAdded(ANode: PNode); +begin +end; + +procedure TCustomAVLTreeMap.DeletingNode(ANode: PNode; AOrigin: boolean); +begin +end; + +function TCustomAVLTreeMap.DoRemove(ANode: PNode; + ACollectionNotification: TCollectionNotification; ADispose: boolean): TValue; +begin + if ANode=nil then + raise EArgumentNilException.CreateRes(@SArgumentNilNode); + + if (ANode.Left = nil) or (ANode.Right = nil) then + DeletingNode(ANode, true); + + InternalDelete(ANode); + + Dec(FCount); + NodeNotify(ANode, ACollectionNotification, ADispose); + + if ADispose then + Dispose(ANode); +end; + +procedure TCustomAVLTreeMap.DisposeAllNodes(ANode: PNode); +begin + if ANode.Left<>nil then + DisposeAllNodes(ANode.Left); + if ANode.Right<>nil then + DisposeAllNodes(ANode.Right); + + NodeNotify(ANode, cnRemoved, true); + Dispose(ANode); +end; + +function TCustomAVLTreeMap.Compare(constref ALeft, ARight: TKey): Integer; inline; +begin + Result := FComparer.Compare(ALeft, ARight); +end; + +function TCustomAVLTreeMap.FindPredecessor(ANode: PNode): PNode; +begin + if ANode <> nil then + begin + if ANode.Left <> nil then + begin + ANode := ANode.Left; + while ANode.Right <> nil do ANode := ANode.Right; + end + else + repeat + Result := ANode; + ANode := ANode.Parent; + until (ANode = nil) or (ANode.Right = Result); + end; + Result := ANode; +end; + +function TCustomAVLTreeMap.FindInsertNode(ANode: PNode; out AInsertNode: PNode): Integer; +begin + AInsertNode := FRoot; + if AInsertNode = nil then // first item in tree + Exit(0); + + repeat + Result := Compare(ANode.Key,AInsertNode.Key); + if Result < 0 then + begin + Result:=-1; + if AInsertNode.Left = nil then + Exit; + AInsertNode := AInsertNode.Left; + end + else + begin + if Result > 0 then + Result:=1; + if AInsertNode.Right = nil then + Exit; + AInsertNode := AInsertNode.Right; + if Result = 0 then + Break; + end; + until false; + + // for equal items (when item already exist) we need to keep 0 result + while true do + if Compare(ANode.Key,AInsertNode.Key) < 0 then + begin + if AInsertNode.Left = nil then + Exit; + AInsertNode := AInsertNode.Left; + end + else + begin + if AInsertNode.Right = nil then + Exit; + AInsertNode := AInsertNode.Right; + end; +end; + +procedure TCustomAVLTreeMap.RotateRightRight(ANode: PNode); +var + LNode, LParent: PNode; +begin + LNode := ANode.Right; + LParent := ANode.Parent; + + ANode.Right := LNode.Left; + if ANode.Right <> nil then + ANode.Right.Parent := ANode; + + LNode.Left := ANode; + LNode.Parent := LParent; + ANode.Parent := LNode; + + if LParent <> nil then + begin + if LParent.Left = ANode then + LParent.Left := LNode + else + LParent.Right := LNode; + end + else + FRoot := LNode; + + if LNode.Balance = -1 then + begin + ANode.Balance := 0; + LNode.Balance := 0; + end + else + begin + ANode.Balance := -1; + LNode.Balance := 1; + end +end; + +procedure TCustomAVLTreeMap.RotateLeftLeft(ANode: PNode); +var + LNode, LParent: PNode; +begin + LNode := ANode.Left; + LParent := ANode.Parent; + + ANode.Left := LNode.Right; + if ANode.Left <> nil then + ANode.Left.Parent := ANode; + + LNode.Right := ANode; + LNode.Parent := LParent; + ANode.Parent := LNode; + + if LParent <> nil then + begin + if LParent.Left = ANode then + LParent.Left := LNode + else + LParent.Right := LNode; + end + else + FRoot := LNode; + + if LNode.Balance = 1 then + begin + ANode.Balance := 0; + LNode.Balance := 0; + end + else + begin + ANode.Balance := 1; + LNode.Balance := -1; + end +end; + +procedure TCustomAVLTreeMap.RotateRightLeft(ANode: PNode); +var + LRight, LLeft, LParent: PNode; +begin + LRight := ANode.Right; + LLeft := LRight.Left; + LParent := ANode.Parent; + + LRight.Left := LLeft.Right; + if LRight.Left <> nil then + LRight.Left.Parent := LRight; + + ANode.Right := LLeft.Left; + if ANode.Right <> nil then + ANode.Right.Parent := ANode; + + LLeft.Left := ANode; + LLeft.Right := LRight; + ANode.Parent := LLeft; + LRight.Parent := LLeft; + + LLeft.Parent := LParent; + + if LParent <> nil then + begin + if LParent.Left = ANode then + LParent.Left := LLeft + else + LParent.Right := LLeft; + end + else + FRoot := LLeft; + + if LLeft.Balance = -1 then + ANode.Balance := 1 + else + ANode.Balance := 0; + + if LLeft.Balance = 1 then + LRight.Balance := -1 + else + LRight.Balance := 0; + + LLeft.Balance := 0; +end; + +procedure TCustomAVLTreeMap.RotateLeftRight(ANode: PNode); +var + LLeft, LRight, LParent: PNode; +begin + LLeft := ANode.Left; + LRight := LLeft.Right; + LParent := ANode.Parent; + + LLeft.Right := LRight.Left; + if LLeft.Right <> nil then + LLeft.Right.Parent := LLeft; + + ANode.Left := LRight.Right; + if ANode.Left <> nil then + ANode.Left.Parent := ANode; + + LRight.Right := ANode; + LRight.Left := LLeft; + ANode.Parent := LRight; + LLeft.Parent := LRight; + + LRight.Parent := LParent; + + if LParent <> nil then + begin + if LParent.Left = ANode then + LParent.Left := LRight + else + LParent.Right := LRight; + end + else + FRoot := LRight; + + if LRight.Balance = 1 then + ANode.Balance := -1 + else + ANode.Balance := 0; + if LRight.Balance = -1 then + LLeft.Balance := 1 + else + LLeft.Balance := 0; + + LRight.Balance := 0; +end; + +procedure TCustomAVLTreeMap.KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); +begin + if Assigned(FOnKeyNotify) then + FOnKeyNotify(Self, AKey, ACollectionNotification); +end; + +procedure TCustomAVLTreeMap.ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); +begin + if Assigned(FOnValueNotify) then + FOnValueNotify(Self, AValue, ACollectionNotification); +end; + +procedure TCustomAVLTreeMap.NodeNotify(ANode: PNode; ACollectionNotification: TCollectionNotification; ADispose: boolean); +begin + if Assigned(FOnValueNotify) then + FOnNodeNotify(Self, ANode, ACollectionNotification, ADispose); + KeyNotify(ANode.Key, ACollectionNotification); + ValueNotify(ANode.Value, ACollectionNotification); +end; + +procedure TCustomAVLTreeMap.SetValue(var AValue: TValue; constref ANewValue: TValue); +var + LOldValue: TValue; +begin + LOldValue := AValue; + AValue := ANewValue; + + ValueNotify(LOldValue, cnRemoved); + ValueNotify(ANewValue, cnAdded); +end; + +procedure TCustomAVLTreeMap.WriteStr(AStream: TStream; const AText: string); +begin + if AText='' then exit; + AStream.Write(AText[1],Length(AText)); +end; + +function TCustomAVLTreeMap.GetNodeCollection: TNodeCollection; +begin + if not Assigned(FNodes) then + FNodes := TNodeCollection.Create(TTree(Self)); + Result := FNodes; +end; + +procedure TCustomAVLTreeMap.InternalAdd(ANode, AParent: PNode); +begin + Inc(FCount); + + ANode.Parent := AParent; + NodeAdded(ANode); + + if AParent=nil then + begin + FRoot := ANode; + Exit; + end; + + // balance after insert + + if AParent.Balance<>0 then + AParent.Balance := 0 + else + begin + if AParent.Left = ANode then + AParent.Balance := 1 + else + AParent.Balance := -1; + + ANode := AParent.Parent; + + while ANode <> nil do + begin + if ANode.Balance<>0 then + begin + if ANode.Balance = 1 then + begin + if ANode.Right = AParent then + ANode.Balance := 0 + else if AParent.Balance = -1 then + RotateLeftRight(ANode) + else + RotateLeftLeft(ANode); + end + else + begin + if ANode.Left = AParent then + ANode.Balance := 0 + else if AParent^.Balance = 1 then + RotateRightLeft(ANode) + else + RotateRightRight(ANode); + end; + Break; + end; + + if ANode.Left = AParent then + ANode.Balance := 1 + else + ANode.Balance := -1; + + AParent := ANode; + ANode := ANode.Parent; + end; + end; +end; + +function TCustomAVLTreeMap.InternalAdd(ANode: PNode; ADispisable: boolean): PNode; +var + LParent: PNode; +begin + Result := ANode; + case FindInsertNode(ANode, LParent) of + -1: LParent.Left := ANode; + 0: + if Assigned(LParent) then + case FDuplicates of + dupAccept: LParent.Right := ANode; + dupIgnore: + begin + LParent.Right := nil; + if ADispisable then + Dispose(ANode); + Exit(LParent); + end; + dupError: + begin + LParent.Right := nil; + if ADispisable then + Dispose(ANode); + Result := nil; + raise EListError.Create(SCollectionDuplicate); + end; + end; + 1: LParent.Right := ANode; + end; + + InternalAdd(ANode, LParent); + NodeNotify(ANode, cnAdded, false); +end; + +procedure TCustomAVLTreeMap.InternalDelete(ANode: PNode); +var + t, y, z: PNode; + LNest: boolean; +begin + if (ANode.Left <> nil) and (ANode.Right <> nil) then + begin + y := FindPredecessor(ANode); + y.Info := ANode.Info; + DeletingNode(y, false); + InternalDelete(y); + LNest := false; + end + else + begin + if ANode.Left <> nil then + begin + y := ANode.Left; + ANode.Left := nil; + end + else + begin + y := ANode.Right; + ANode.Right := nil; + end; + ANode.Balance := 0; + LNest := true; + end; + + if y <> nil then + begin + y.Parent := ANode.Parent; + y.Left := ANode.Left; + if y.Left <> nil then + y.Left.Parent := y; + y.Right := ANode.Right; + if y.Right <> nil then + y.Right.Parent := y; + y.Balance := ANode.Balance; + end; + + if ANode.Parent <> nil then + begin + if ANode.Parent.Left = ANode then + ANode.Parent.Left := y + else + ANode.Parent.Right := y; + end + else + FRoot := y; + + if LNest then + begin + z := y; + y := ANode.Parent; + while y <> nil do + begin + if y.Balance = 0 then + begin + if y.Left = z then + y.Balance := -1 + else + y.Balance := 1; + break; + end + else + begin + if ((y.Balance = 1) and (y.Left = z)) or ((y.Balance = -1) and (y.Right = z)) then + begin + y.Balance := 0; + z := y; + y := y.Parent; + end + else + begin + if y.Left = z then + t := y.Right + else + t := y.Left; + if t.Balance = 0 then + begin + if y.Balance = 1 then + RotateLeftLeft(y) + else + RotateRightRight(y); + break; + end + else if y.Balance = t.Balance then + begin + if y.Balance = 1 then + RotateLeftLeft(y) + else + RotateRightRight(y); + z := t; + y := t.Parent; + end + else + begin + if y.Balance = 1 then + RotateLeftRight(y) + else + RotateRightLeft(y); + z := y.Parent; + y := z.Parent; + end + end + end + end + end; +end; + +function TCustomAVLTreeMap.GetKeys: TKeyCollection; +begin + if not Assigned(FKeys) then + FKeys := TKeyCollection.Create(TTree(Self)); + Result := TKeyCollection(FKeys); +end; + +function TCustomAVLTreeMap.GetValues: TValueCollection; +begin + if not Assigned(FValues) then + FValues := TValueCollection.Create(TTree(Self)); + Result := TValueCollection(FValues); +end; + +constructor TCustomAVLTreeMap.Create; +begin + FComparer := TComparer.Default; +end; + +constructor TCustomAVLTreeMap.Create(const AComparer: IComparer); +begin + FComparer := AComparer; +end; + +function TCustomAVLTreeMap.NewNode: PNode; +begin + Result := AllocMem(SizeOf(TNode)); + Initialize(Result^); +end; + +function TCustomAVLTreeMap.NewNodeArray(ACount: SizeInt): PNode; +begin + Result := AllocMem(ACount * SizeOf(TNode)); + Initialize(Result^, ACount); +end; + +procedure TCustomAVLTreeMap.NewNodeArray(out AArray: TArray; ACount: SizeInt); +var + i: Integer; +begin + SetLength(AArray, ACount); + for i := 0 to ACount-1 do + AArray[i] := NewNode; +end; + +procedure TCustomAVLTreeMap.DisposeNode(ANode: PNode); +begin + Dispose(ANode); +end; + +procedure TCustomAVLTreeMap.DisposeNodeArray(ANode: PNode; ACount: SizeInt); +begin + Finalize(ANode^, ACount); + FreeMem(ANode); +end; + +procedure TCustomAVLTreeMap.DisposeNodeArray(var AArray: TArray); +var + i: Integer; +begin + for i := 0 to High(AArray) do + Dispose(AArray[i]); + AArray := nil; +end; + +destructor TCustomAVLTreeMap.Destroy; +begin + FKeys.Free; + FValues.Free; + FNodes.Free; + Clear; +end; + +function TCustomAVLTreeMap.AddNode(ANode: PNode): boolean; +begin + Result := ANode=InternalAdd(ANode, false); +end; + +function TCustomAVLTreeMap.Add(constref APair: TTreePair): PNode; +begin + Result := NewNode; + Result.Data.Key := APair.Key; + Result.Data.Value := APair.Value; + Result := InternalAdd(Result, true); +end; + +function TCustomAVLTreeMap.Add(constref AKey: TKey; constref AValue: TValue): PNode; +begin + Result := NewNode; + Result.Data.Key := AKey; + Result.Data.Value := AValue; + Result := InternalAdd(Result, true); +end; + +function TCustomAVLTreeMap.Remove(constref AKey: TKey; ADisposeNode: boolean): boolean; +var + LNode: PNode; +begin + LNode:=Find(AKey); + if LNode<>nil then begin + Delete(LNode, ADisposeNode); + Result:=true; + end else + Result:=false; +end; + +function TCustomAVLTreeMap.ExtractPair(constref AKey: TKey; ADisposeNode: boolean): TTreePair; +var + LNode: PNode; +begin + LNode:=Find(AKey); + if LNode<>nil then + begin + Result.Key := AKey; + Result.Value := DoRemove(LNode, cnExtracted, ADisposeNode); + end else + Result := Default(TTreePair); +end; + +function TCustomAVLTreeMap.ExtractPair(constref ANode: PNode; ADispose: boolean = true): TTreePair; +begin + Result.Key := ANode.Key; + Result.Value := DoRemove(ANode, cnExtracted, ADispose); +end; + +function TCustomAVLTreeMap.ExtractNode(constref AKey: TKey; ADisposeNode: boolean): PNode; +begin + Result:=Find(AKey); + if Result<>nil then + begin + DoRemove(Result, cnExtracted, false); + if ADisposeNode then + Result := nil; + end; +end; + +function TCustomAVLTreeMap.ExtractNode(ANode: PNode; ADispose: boolean): PNode; +begin + DoRemove(ANode, cnExtracted, ADispose); + if ADispose then + Result := nil + else + Result := ANode; +end; + +procedure TCustomAVLTreeMap.Delete(ANode: PNode; ADispose: boolean); +begin + DoRemove(ANode, cnRemoved, ADispose); +end; + +procedure TCustomAVLTreeMap.Clear(ADisposeNodes: Boolean); +begin + if (FRoot<>nil) and ADisposeNodes then + DisposeAllNodes(FRoot); + fRoot:=nil; + FCount:=0; +end; + +function TCustomAVLTreeMap.GetEnumerator: TPairEnumerator; +begin + Result := TPairEnumerator.Create(Self, true); +end; + +function TCustomAVLTreeMap.FindLowest: PNode; +begin + Result:=FRoot; + if Result<>nil then + while Result.Left<>nil do Result:=Result.Left; +end; + +function TCustomAVLTreeMap.FindHighest: PNode; +begin + Result:=FRoot; + if Result<>nil then + while Result.Right<>nil do Result:=Result.Right; +end; + +function TCustomAVLTreeMap.Find(constref AKey: TKey): PNode; +var + LComp: SizeInt; +begin + Result:=FRoot; + while (Result<>nil) do + begin + LComp:=Compare(AKey,Result.Key); + if LComp=0 then + Exit; + if LComp<0 then + Result:=Result.Left + else + Result:=Result.Right + end; +end; + +function TCustomAVLTreeMap.ContainsKey(constref AKey: TKey; out ANode: PNode): boolean; +begin + ANode := Find(AKey); + Result := Assigned(ANode); +end; + +function TCustomAVLTreeMap.ContainsKey(constref AKey: TKey): boolean; overload; inline; +begin + Result := Assigned(Find(AKey)); +end; + +procedure TCustomAVLTreeMap.ConsistencyCheck; +var + RealCount: SizeInt; +begin + RealCount:=0; + if FRoot<>nil then begin + FRoot.ConsistencyCheck(Self); + RealCount:=FRoot.GetCount; + end; + if Count<>RealCount then + raise EAVLTree.Create('Count<>RealCount'); +end; + +procedure TCustomAVLTreeMap.WriteTreeNode(AStream: TStream; ANode: PNode); +var + b: String; + IsLeft: boolean; + LParent: PNode; + WasLeft: Boolean; +begin + if ANode=nil then exit; + WriteTreeNode(AStream, ANode.Right); + LParent:=ANode; + WasLeft:=false; + b:=''; + while LParent<>nil do begin + if LParent.Parent=nil then begin + if LParent=ANode then + b:='--'+b + else + b:=' '+b; + break; + end; + IsLeft:=LParent.Parent.Left=LParent; + if LParent=ANode then begin + if IsLeft then + b:='\-' + else + b:='/-'; + end else begin + if WasLeft=IsLeft then + b:=' '+b + else + b:='| '+b; + end; + WasLeft:=IsLeft; + LParent:=LParent.Parent; + end; + b:=b+NodeToReportStr(ANode)+LineEnding; + WriteStr(AStream, b); + WriteTreeNode(AStream, ANode.Left); +end; + +procedure TCustomAVLTreeMap.WriteReportToStream(AStream: TStream); +begin + WriteStr(AStream, '-Start-of-AVL-Tree-------------------'+LineEnding); + WriteTreeNode(AStream, fRoot); + WriteStr(AStream, '-End-Of-AVL-Tree---------------------'+LineEnding); +end; + +function TCustomAVLTreeMap.NodeToReportStr(ANode: PNode): string; +begin + Result:=Format(' Self=%p Parent=%p Balance=%d', [ANode, ANode.Parent, ANode.Balance]); +end; + +function TCustomAVLTreeMap.ReportAsString: string; +var ms: TMemoryStream; +begin + Result:=''; + ms:=TMemoryStream.Create; + try + WriteReportToStream(ms); + ms.Position:=0; + SetLength(Result,ms.Size); + if Result<>'' then + ms.Read(Result[1],length(Result)); + finally + ms.Free; + end; +end; + +{ TIndexedAVLTreeMap } + +procedure TIndexedAVLTreeMap.RotateRightRight(ANode: PNode); +var + LOldRight: PNode; +begin + LOldRight:=ANode.Right; + inherited; + Inc(LOldRight.Data.Info, (1 + ANode.Data.Info)); +end; + +procedure TIndexedAVLTreeMap.RotateLeftLeft(ANode: PNode); +var + LOldLeft: PNode; +begin + LOldLeft:=ANode.Left; + inherited; + Dec(ANode.Data.Info, (1 + LOldLeft.Data.Info)); +end; + +procedure TIndexedAVLTreeMap.RotateRightLeft(ANode: PNode); +var + LB, LC: PNode; +begin + LB := ANode.Right; + LC := LB.Left; + inherited; + Dec(LB.Data.Info, 1+LC.Info); + Inc(LC.Data.Info, 1+ANode.Info); +end; + +procedure TIndexedAVLTreeMap.RotateLeftRight(ANode: PNode); +var + LB, LC: PNode; +begin + LB := ANode.Left; + LC := LB.Right; + inherited; + Inc(LC.Data.Info, 1+LB.Info); + Dec(ANode.Data.Info, 1+LC.Info); +end; + + +procedure TIndexedAVLTreeMap.NodeAdded(ANode: PNode); +var + LParent, LNode: PNode; +begin + FLastNode := nil; + LNode := ANode; + repeat + LParent:=LNode.Parent; + if (LParent=nil) then break; + if LParent.Left=LNode then + Inc(LParent.Data.Info); + LNode:=LParent; + until false; +end; + +procedure TIndexedAVLTreeMap.DeletingNode(ANode: PNode; AOrigin: boolean); +var + LParent: PNode; +begin + if not AOrigin then + Dec(ANode.Data.Info); + FLastNode := nil; + repeat + LParent:=ANode.Parent; + if (LParent=nil) then exit; + if LParent.Left=ANode then + Dec(LParent.Data.Info); + ANode:=LParent; + until false; +end; + +function TIndexedAVLTreeMap.GetNodeAtIndex(AIndex: SizeInt): PNode; +begin + if (AIndex<0) or (AIndex>=Count) then + raise EIndexedAVLTree.CreateFmt('TIndexedAVLTree: AIndex %d out of bounds 0..%d', [AIndex, Count]); + + if FLastNode<>nil then begin + if AIndex=FLastIndex then + Exit(FLastNode) + else if AIndex=FLastIndex+1 then begin + FLastIndex:=AIndex; + FLastNode:=FLastNode.Successor; + Exit(FLastNode); + end else if AIndex=FLastIndex-1 then begin + FLastIndex:=AIndex; + FLastNode:=FLastNode.Precessor; + Exit(FLastNode); + end; + end; + + FLastIndex:=AIndex; + Result:=FRoot; + repeat + if Result.Info>AIndex then + Result:=Result.Left + else if Result.Info=AIndex then begin + FLastNode:=Result; + Exit; + end + else begin + Dec(AIndex, Result.Info+1); + Result:=Result.Right; + end; + until false; +end; + +function TIndexedAVLTreeMap.NodeToIndex(ANode: PNode): SizeInt; +var + LNode: PNode; + LParent: PNode; +begin + if ANode=nil then + Exit(-1); + + if FLastNode=ANode then + Exit(FLastIndex); + + LNode:=ANode; + Result:=LNode.Info; + repeat + LParent:=LNode.Parent; + if LParent=nil then break; + if LParent.Right=LNode then + inc(Result,LParent.Info+1); + LNode:=LParent; + until false; + + FLastNode:=ANode; + FLastIndex:=Result; +end; + +procedure TIndexedAVLTreeMap.ConsistencyCheck; +var + LNode: PNode; + i: SizeInt; + LeftCount: SizeInt = 0; +begin + inherited ConsistencyCheck; + i:=0; + for LNode in Self.Nodes do + begin + if LNode.Left<>nil then + LeftCount:=LNode.Left.GetCount + else + LeftCount:=0; + + if LNode.Info<>LeftCount then + raise EIndexedAVLTree.CreateFmt('LNode.LeftCount=%d<>%d',[LNode.Info,LeftCount]); + + if GetNodeAtIndex(i)<>LNode then + raise EIndexedAVLTree.CreateFmt('GetNodeAtIndex(%d)<>%P',[i,LNode]); + FLastNode:=nil; + if GetNodeAtIndex(i)<>LNode then + raise EIndexedAVLTree.CreateFmt('GetNodeAtIndex(%d)<>%P',[i,LNode]); + + if NodeToIndex(LNode)<>i then + raise EIndexedAVLTree.CreateFmt('NodeToIndex(%P)<>%d',[LNode,i]); + FLastNode:=nil; + if NodeToIndex(LNode)<>i then + raise EIndexedAVLTree.CreateFmt('NodeToIndex(%P)<>%d',[LNode,i]); + + inc(i); + end; +end; + +function TIndexedAVLTreeMap.NodeToReportStr(ANode: PNode): string; +begin + Result:=Format(' Self=%p Parent=%p Balance=%d Idx=%d Info=%d', + [ANode,ANode.Parent, ANode.Balance, NodeToIndex(ANode), ANode.Info]); +end; + +{ TAVLTree } + +function TAVLTree.Add(constref AValue: T): PNode; +begin + Result := inherited Add(AValue, EmptyRecord); +end; + +function TAVLTree.AddNode(ANode: PNode): boolean; +begin + Result := inherited AddNode(ANode); +end; + +{ TIndexedAVLTree } + +function TIndexedAVLTree.Add(constref AValue: T): PNode; +begin + Result := inherited Add(AValue, EmptyRecord); +end; + +function TIndexedAVLTree.AddNode(ANode: PNode): boolean; +begin + Result := inherited AddNode(ANode); +end; + +{ TSortedSet.TSortedSetEnumerator } + +function TSortedSet.TSortedSetEnumerator.GetCurrent: T; +begin + Result := TTreeEnumerator(FEnumerator).GetCurrent; +end; + +constructor TSortedSet.TSortedSetEnumerator.Create(ASet: TCustomSet); +begin + TTreeEnumerator(FEnumerator) := TSortedSet(ASet).FInternalTree.Keys.DoGetEnumerator; +end; + +{ TSortedSet.TPointersEnumerator } + +function TSortedSet.TPointersEnumerator.DoMoveNext: boolean; +begin + Result := FEnumerator.MoveNext; +end; + +function TSortedSet.TPointersEnumerator.DoGetCurrent: PT; +begin + Result := FEnumerator.Current; +end; + +constructor TSortedSet.TPointersEnumerator.Create(ASortedSet: TSortedSet); +begin + FEnumerator := ASortedSet.FInternalTree.Keys.Ptr^.GetEnumerator; +end; + +{ TSortedSet } + +procedure TSortedSet.InternalAVLTreeNotify(ASender: TObject; constref AItem: T; AAction: TCollectionNotification); +begin + FOnNotify(Self, AItem, AAction); +end; + +function TSortedSet.GetPtrEnumerator: TEnumerator; +begin + Result := TPointersEnumerator.Create(Self); +end; + +function TSortedSet.GetCount: SizeInt; +begin + Result := FInternalTree.Count; +end; + +function TSortedSet.GetCapacity: SizeInt; +begin + Result := FInternalTree.Count; +end; + +procedure TSortedSet.SetCapacity(AValue: SizeInt); +begin +end; + +function TSortedSet.GetOnNotify: TCollectionNotifyEvent; +begin + Result := FInternalTree.OnKeyNotify; +end; + +procedure TSortedSet.SetOnNotify(AValue: TCollectionNotifyEvent); +begin + FOnNotify := AValue; + if Assigned(AValue) then + FInternalTree.OnKeyNotify := InternalAVLTreeNotify + else + FInternalTree.OnKeyNotify := nil; +end; + +function TSortedSet.GetEnumerator: TCustomSetEnumerator; +begin + Result := TSortedSetEnumerator.Create(Self); +end; + +constructor TSortedSet.Create; +begin + FInternalTree := TAVLTree.Create; +end; + +constructor TSortedSet.Create(const AComparer: IComparer); +begin + FInternalTree := TAVLTree.Create(AComparer); +end; + +destructor TSortedSet.Destroy; +begin + FInternalTree.Free; +end; + +function TSortedSet.Add(constref AValue: T): Boolean; +var + LNodePtr, LParent: TAVLTree.PNode; + LNode: TAVLTree.TNode; + LCompare: Integer; +begin + LNode.Data.Key := AValue; + + LCompare := FInternalTree.FindInsertNode(@LNode, LParent); + + Result := not((LCompare=0) and Assigned(LParent)); + if not Result then + Exit; + + LNodePtr := FInternalTree.NewNode; + LNodePtr^.Data.Key := AValue; + + case LCompare of + -1: LParent.Left := LNodePtr; + 1: LParent.Right := LNodePtr; + end; + + FInternalTree.InternalAdd(LNodePtr, LParent); + FInternalTree.NodeNotify(LNodePtr, cnAdded, false); +end; + +function TSortedSet.Remove(constref AValue: T): Boolean; +var + LNode: TAVLTree.PNode; +begin + LNode := FInternalTree.Find(AValue); + Result := Assigned(LNode); + if Result then + FInternalTree.Delete(LNode); +end; + +function TSortedSet.Extract(constref AValue: T): T; +var + LNode: TAVLTree.PNode; +begin + LNode := FInternalTree.Find(AValue); + if not Assigned(LNode) then + Exit(Default(T)); + + Result := FInternalTree.ExtractPair(LNode).Key; +end; + +procedure TSortedSet.Clear; +begin + FInternalTree.Clear; +end; + +function TSortedSet.Contains(constref AValue: T): Boolean; +begin + Result := FInternalTree.ContainsKey(AValue); +end; + +procedure TSortedSet.TrimExcess; +begin +end; + +{ TSortedHashSet.TSortedHashSetEqualityComparer } + +function TSortedHashSet.TSortedHashSetEqualityComparer.Equals(constref ALeft, ARight: PT): Boolean; +begin + if Assigned(FComparer) then + Result := FComparer.Compare(ALeft^, ARight^) = 0 + else + Result := FEqualityComparer.Equals(ALeft^, ARight^); +end; + +function TSortedHashSet.TSortedHashSetEqualityComparer.GetHashCode(constref AValue: PT): UInt32; +begin + Result := FEqualityComparer.GetHashCode(AValue^); +end; + +constructor TSortedHashSet.TSortedHashSetEqualityComparer.Create(const AComparer: IComparer); +begin + FComparer := AComparer; + FEqualityComparer := TEqualityComparer.Default; +end; + +constructor TSortedHashSet.TSortedHashSetEqualityComparer.Create(const AEqualityComparer: IEqualityComparer); +begin + FEqualityComparer := AEqualityComparer; +end; + +constructor TSortedHashSet.TSortedHashSetEqualityComparer.Create(const AComparer: IComparer; const AEqualityComparer: IEqualityComparer); +begin + FComparer := AComparer; + FEqualityComparer := AEqualityComparer; +end; + +{ TSortedHashSet.TSortedHashSetEnumerator } + +function TSortedHashSet.TSortedHashSetEnumerator.GetCurrent: T; +begin + Result := TTreeEnumerator(FEnumerator).Current; +end; + +constructor TSortedHashSet.TSortedHashSetEnumerator.Create(ASet: TCustomSet); +begin + FEnumerator := TSortedHashSet(ASet).FInternalTree.Keys.GetEnumerator; +end; + +{ TSortedHashSet.TPointersEnumerator } + +function TSortedHashSet.TPointersEnumerator.DoMoveNext: boolean; +begin + Result := FEnumerator.MoveNext; +end; + +function TSortedHashSet.TPointersEnumerator.DoGetCurrent: PT; +begin + Result := FEnumerator.Current; +end; + +constructor TSortedHashSet.TPointersEnumerator.Create(ASortedHashSet: TSortedHashSet); +begin + FEnumerator := ASortedHashSet.FInternalTree.Keys.Ptr^.GetEnumerator; +end; + +{ TSortedHashSet } + +procedure TSortedHashSet.InternalDictionaryNotify(ASender: TObject; constref AItem: PT; AAction: TCollectionNotification); +begin + FOnNotify(Self, AItem^, AAction); +end; + +function TSortedHashSet.GetPtrEnumerator: TEnumerator; +begin + Result := TPointersEnumerator.Create(Self); +end; + +function TSortedHashSet.DoGetEnumerator: TEnumerator; +begin + Result := GetEnumerator; +end; + +function TSortedHashSet.GetCount: SizeInt; +begin + Result := FInternalDictionary.Count; +end; + +function TSortedHashSet.GetCapacity: SizeInt; +begin + Result := FInternalDictionary.Capacity; +end; + +procedure TSortedHashSet.SetCapacity(AValue: SizeInt); +begin + FInternalDictionary.Capacity := AValue; +end; + +function TSortedHashSet.GetOnNotify: TCollectionNotifyEvent; +begin + Result := FInternalTree.OnKeyNotify; +end; + +procedure TSortedHashSet.SetOnNotify(AValue: TCollectionNotifyEvent); +begin + FOnNotify := AValue; + if Assigned(AValue) then + FInternalDictionary.OnKeyNotify := InternalDictionaryNotify + else + FInternalDictionary.OnKeyNotify := nil; +end; + +function TSortedHashSet.GetEnumerator: TCustomSetEnumerator; +begin + Result := TSortedHashSetEnumerator.Create(Self); +end; + +function TSortedHashSet.Add(constref AValue: T): Boolean; +var + LNode: TAVLTree.PNode; +begin + Result := not FInternalDictionary.ContainsKey(@AValue); + if Result then + begin + LNode := FInternalTree.Add(AValue); + FInternalDictionary.Add(@LNode.Data.Key, EmptyRecord); + end; +end; + +function TSortedHashSet.Remove(constref AValue: T): Boolean; +var + LIndex: SizeInt; +begin + LIndex := FInternalDictionary.FindBucketIndex(@AValue); + Result := LIndex >= 0; + if Result then + begin + FInternalDictionary.DoRemove(LIndex, cnRemoved); + FInternalTree.Remove(AValue); + end; +end; + +function TSortedHashSet.Extract(constref AValue: T): T; +var + LIndex: SizeInt; +begin + LIndex := FInternalDictionary.FindBucketIndex(@AValue); + if LIndex >= 0 then + begin + FInternalDictionary.DoRemove(LIndex, cnExtracted); + FInternalTree.Remove(AValue); + Result := AValue; + end else + Result := Default(T); +end; + +procedure TSortedHashSet.Clear; +begin + FInternalDictionary.Clear; + FInternalTree.Clear; +end; + +function TSortedHashSet.Contains(constref AValue: T): Boolean; +begin + Result := FInternalDictionary.ContainsKey(@AValue); +end; + +constructor TSortedHashSet.Create; +begin + FInternalTree := TAVLTree.Create; + FInternalDictionary := TOpenAddressingLP.Create(TSortedHashSetEqualityComparer.Create(TEqualityComparer.Default)); +end; + +constructor TSortedHashSet.Create(const AComparer: IEqualityComparer); +begin + Create(TComparer.Default, AComparer); +end; + +constructor TSortedHashSet.Create(const AComparer: IComparer); +begin + FInternalTree := TAVLTree.Create(AComparer); + FInternalDictionary := TOpenAddressingLP.Create(TSortedHashSetEqualityComparer.Create(AComparer)); +end; + +constructor TSortedHashSet.Create(const AComparer: IComparer; const AEqualityComparer: IEqualityComparer); +begin + FInternalTree := TAVLTree.Create(AComparer); + FInternalDictionary := TOpenAddressingLP.Create(TSortedHashSetEqualityComparer.Create(AComparer,AEqualityComparer)); +end; + +destructor TSortedHashSet.Destroy; +begin + FInternalDictionary.Free; + FInternalTree.Free; + inherited; +end; + +procedure TSortedHashSet.TrimExcess; +begin + FInternalDictionary.TrimExcess; +end; + end. diff --git a/components/sparta/generics/source/generics.defaults.pas b/components/sparta/generics/source/generics.defaults.pas index 5656cffa03..3259d654ee 100644 --- a/components/sparta/generics/source/generics.defaults.pas +++ b/components/sparta/generics/source/generics.defaults.pas @@ -14,6 +14,14 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Acknowledgment + + Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring + many new types and major refactoring of entire library + + Thanks to mORMot (http://synopse.info) project for the best implementations + of hashing functions like crc32c and xxHash32 :) + **********************************************************************} unit Generics.Defaults; @@ -554,7 +562,7 @@ type EqualityComparer_Method_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Method ; GetHashCode: @THashFactory.Method ); EqualityComparer_Variant_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Variant; GetHashCode: @THashFactory.Variant); EqualityComparer_Pointer_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Pointer; GetHashCode: @THashFactory.Pointer); -{$WARNINGS ON} +{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields private class var // IEqualityComparer VMT FEqualityComparer_Int8_VMT : THashFactory.TEqualityComparerVMT; @@ -673,7 +681,7 @@ type ExtendedEqualityComparer_Method_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Method ; GetHashCode: @THashFactory.Method ; GetHashList: @TExtendedHashFactory.Method ); ExtendedEqualityComparer_Variant_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Variant; GetHashCode: @THashFactory.Variant; GetHashList: @TExtendedHashFactory.Variant); ExtendedEqualityComparer_Pointer_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Pointer; GetHashCode: @THashFactory.Pointer; GetHashList: @TExtendedHashFactory.Pointer); -{$WARNINGS ON} +{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields private class var // IExtendedEqualityComparer VMT FExtendedEqualityComparer_Int8_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT; @@ -857,6 +865,12 @@ type class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; end; + TmORMotHashFactory = class(THashFactory) + public + class function GetHashService: THashServiceClass; override; + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; + end; + { TAdler32HashFactory } TAdler32HashFactory = class(THashFactory) @@ -922,7 +936,7 @@ type class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); override; end; - TDefaultHashFactory = TDelphiQuadrupleHashFactory; + TDefaultHashFactory = TmORMotHashFactory; TDefaultGenericInterface = (giComparer, giEqualityComparer, giExtendedEqualityComparer); @@ -2782,6 +2796,18 @@ begin Result := DelphiHashLittle(AKey, ASize, AInitVal); end; +{ TmORMotHashFactory } + +class function TmORMotHashFactory.GetHashService: THashServiceClass; +begin + Result := THashService; +end; + +class function TmORMotHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32; +begin + Result := mORMotHasher(AInitVal, AKey, ASize); +end; + { TAdler32HashFactory } class function TAdler32HashFactory.GetHashService: THashServiceClass; @@ -2879,7 +2905,7 @@ begin else raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); end; -{$WARNINGS ON} +{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields end; { TDelphiQuadrupleHashFactory } @@ -3255,7 +3281,7 @@ begin giEqualityComparer: begin if AFactory = nil then - AFactory := TDelphiHashFactory; + AFactory := TDefaultHashFactory; Exit( AFactory.GetHashService.LookupEqualityComparer(ATypeInfo, ASize)); diff --git a/components/sparta/generics/source/generics.hashes.pas b/components/sparta/generics/source/generics.hashes.pas index ec1bb2b763..6dba729c95 100644 --- a/components/sparta/generics/source/generics.hashes.pas +++ b/components/sparta/generics/source/generics.hashes.pas @@ -14,6 +14,14 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Acknowledgment + + Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring + many new types and major refactoring of entire library. + + Thanks to mORMot (http://synopse.info) project for the best implementations + of hashing functions like crc32c and xxHash32 :) + **********************************************************************} unit Generics.Hashes; @@ -64,6 +72,14 @@ function SimpleChecksumHash(AKey: Pointer; ALength: SizeInt): UInt32; // https://code.google.com/p/hedgewars/source/browse/hedgewars/adler32.pas function Adler32(AKey: Pointer; ALength: SizeInt): UInt32; function sdbm(AKey: Pointer; ALength: SizeInt): UInt32; +function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal; + +type + THasher = function(crc: cardinal; buf: Pointer; len: cardinal): cardinal; + +var + crc32c: THasher; + mORMotHasher: THasher; implementation @@ -911,5 +927,663 @@ begin Result := Int32(c); end; +{$ifdef CPU64} + {$define PUREPASCAL} + {$ifdef CPUX64} + {$define CPUINTEL} + {$ASMMODE INTEL} + {$endif CPUX64} +{$else} + {$ifdef CPUX86} + {$ifndef FPC_PIC} + {$define CPUINTEL} + {$ASMMODE INTEL} + {$else} + { Assembler code uses references to static + variables with are not PIC ready } + {$define PUREPASCAL} + {$endif} + {$else CPUX86} + {$define PUREPASCAL} + {$endif} +{$endif CPU64} + +{$ifdef CPUARM} // circumvent FPC issue on ARM +function ToByte(value: cardinal): cardinal; inline; +begin + result := value and $ff; +end; +{$else} +type ToByte = byte; +{$endif} + +{$ifdef CPUINTEL} // use optimized x86/x64 asm versions for xxHash32 + +{$ifdef CPUX86} +function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal; +asm + xchg edx, ecx + push ebp + push edi + lea ebp, [ecx+edx] + push esi + push ebx + sub esp, 8 + cmp edx, 15 + mov ebx, eax + mov dword ptr [esp], edx + lea eax, [ebx+165667B1H] + jbe @2 + lea eax, [ebp-10H] + lea edi, [ebx+24234428H] + lea esi, [ebx-7A143589H] + mov dword ptr [esp+4H], ebp + mov edx, eax + lea eax, [ebx+61C8864FH] + mov ebp, edx +@1: mov edx, dword ptr [ecx] + imul edx, edx, -2048144777 + add edi, edx + rol edi, 13 + imul edi, edi, -1640531535 + mov edx, dword ptr [ecx+4] + imul edx, edx, -2048144777 + add esi, edx + rol esi, 13 + imul esi, esi, -1640531535 + mov edx, dword ptr [ecx+8] + imul edx, edx, -2048144777 + add ebx, edx + rol ebx, 13 + imul ebx, ebx, -1640531535 + mov edx, dword ptr [ecx+12] + lea ecx, [ecx+16] + imul edx, edx, -2048144777 + add eax, edx + rol eax, 13 + imul eax, eax, -1640531535 + cmp ebp, ecx + jnc @1 + rol edi, 1 + rol esi, 7 + rol ebx, 12 + add esi, edi + mov ebp, dword ptr [esp+4H] + ror eax, 14 + add ebx, esi + add eax, ebx +@2: lea esi, [ecx+4H] + add eax, dword ptr [esp] + cmp ebp, esi + jc @4 + mov ebx, esi + nop +@3: imul edx, dword ptr [ebx-4H], -1028477379 + add ebx, 4 + add eax, edx + ror eax, 15 + imul eax, eax, 668265263 + cmp ebp, ebx + jnc @3 + lea edx, [ebp-4H] + sub edx, ecx + mov ecx, edx + and ecx, 0FFFFFFFCH + add ecx, esi +@4: cmp ebp, ecx + jbe @6 +@5: movzx edx, byte ptr [ecx] + add ecx, 1 + imul edx, edx, 374761393 + add eax, edx + rol eax, 11 + imul eax, eax, -1640531535 + cmp ebp, ecx + jnz @5 + nop +@6: mov edx, eax + add esp, 8 + shr edx, 15 + xor eax, edx + imul eax, eax, -2048144777 + pop ebx + pop esi + mov edx, eax + shr edx, 13 + xor eax, edx + imul eax, eax, -1028477379 + pop edi + pop ebp + mov edx, eax + shr edx, 16 + xor eax, edx +end; +{$endif CPUX86} + +{$ifdef CPUX64} +function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal; +asm + {$ifndef WIN64} // crc=rdi P=rsi len=rdx + mov r8, rdi + mov rcx, rsi + {$else} // crc=r8 P=rcx len=rdx + mov r10, r8 + mov r8, rcx + mov rcx, rdx + mov rdx, r10 + push rsi // Win64 expects those registers to be preserved + push rdi + {$endif} + // P=r8 len=rcx crc=rdx + push rbx + lea r10, [rcx+rdx] + cmp rdx, 15 + lea eax, [r8+165667B1H] + jbe @2 + lea rsi, [r10-10H] + lea ebx, [r8+24234428H] + lea edi, [r8-7A143589H] + lea eax, [r8+61C8864FH] +@1: imul r9d, dword ptr [rcx], -2048144777 + add rcx, 16 + imul r11d, dword ptr [rcx-0CH], -2048144777 + add ebx, r9d + lea r9d, [r11+rdi] + rol ebx, 13 + rol r9d, 13 + imul ebx, ebx, -1640531535 + imul edi, r9d, -1640531535 + imul r9d, dword ptr [rcx-8H], -2048144777 + add r8d, r9d + imul r9d, dword ptr [rcx-4H], -2048144777 + rol r8d, 13 + imul r8d, r8d, -1640531535 + add eax, r9d + rol eax, 13 + imul eax, eax, -1640531535 + cmp rsi, rcx + jnc @1 + rol edi, 7 + rol ebx, 1 + rol r8d, 12 + mov r9d, edi + ror eax, 14 + add r9d, ebx + add r8d, r9d + add eax, r8d +@2: lea r9, [rcx+4H] + add eax, edx + cmp r10, r9 + jc @4 + mov r8, r9 +@3: imul edx, dword ptr [r8-4H], -1028477379 + add r8, 4 + add eax, edx + ror eax, 15 + imul eax, eax, 668265263 + cmp r10, r8 + jnc @3 + lea rdx, [r10-4H] + sub rdx, rcx + mov rcx, rdx + and rcx, 0FFFFFFFFFFFFFFFCH + add rcx, r9 +@4: cmp r10, rcx + jbe @6 +@5: movzx edx, byte ptr [rcx] + add rcx, 1 + imul edx, edx, 374761393 + add eax, edx + rol eax, 11 + imul eax, eax, -1640531535 + cmp r10, rcx + jnz @5 +@6: mov edx, eax + shr edx, 15 + xor eax, edx + imul eax, eax, -2048144777 + mov edx, eax + shr edx, 13 + xor eax, edx + imul eax, eax, -1028477379 + mov edx, eax + shr edx, 16 + xor eax, edx + pop rbx + {$ifdef WIN64} + pop rdi + pop rsi + {$endif} +end; +{$endif CPUX64} + +{$else not CPUINTEL} +const + PRIME32_1 = 2654435761; + PRIME32_2 = 2246822519; + PRIME32_3 = 3266489917; + PRIME32_4 = 668265263; + PRIME32_5 = 374761393; + +// RolDWord is an intrinsic function under FPC :) +function Rol13(value: cardinal): cardinal; inline; +begin + result := RolDWord(value, 13); +end; + +function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal; +var c1, c2, c3, c4: cardinal; + PLimit, PEnd: PAnsiChar; +begin + PEnd := P + len; + if len >= 16 then begin + PLimit := PEnd - 16; + c3 := crc; + c2 := c3 + PRIME32_2; + c1 := c2 + PRIME32_1; + c4 := c3 - PRIME32_1; + repeat + c1 := PRIME32_1 * Rol13(c1 + PRIME32_2 * PCardinal(P)^); + c2 := PRIME32_1 * Rol13(c2 + PRIME32_2 * PCardinal(P+4)^); + c3 := PRIME32_1 * Rol13(c3 + PRIME32_2 * PCardinal(P+8)^); + c4 := PRIME32_1 * Rol13(c4 + PRIME32_2 * PCardinal(P+12)^); + inc(P, 16); + until not (P <= PLimit); + result := RolDWord(c1, 1) + RolDWord(c2, 7) + RolDWord(c3, 12) + RolDWord(c4, 18); + end else + result := crc + PRIME32_5; + inc(result, len); + while P <= PEnd - 4 do begin + inc(result, PCardinal(P)^ * PRIME32_3); + result := RolDWord(result, 17) * PRIME32_4; + inc(P, 4); + end; + while P < PEnd do begin + inc(result, PByte(P)^ * PRIME32_5); + result := RolDWord(result, 11) * PRIME32_1; + inc(P); + end; + result := result xor (result shr 15); + result := result * PRIME32_2; + result := result xor (result shr 13); + result := result * PRIME32_3; + result := result xor (result shr 16); +end; +{$endif CPUINTEL} + +{$ifdef CPUINTEL} + +type + TRegisters = record + eax,ebx,ecx,edx: cardinal; + end; + +{$ifdef CPU64} +procedure GetCPUID(Param: Cardinal; var Registers: TRegisters); nostackframe; assembler; +asm + {$ifdef win64} + mov eax, ecx + mov r9, rdx + {$else} + mov eax, edi + mov r9, rsi + {$endif win64} + mov r10, rbx // preserve rbx + xor ebx, ebx + xor ecx, ecx + xor edx, edx + cpuid + mov TRegisters(r9).&eax, eax + mov TRegisters(r9).&ebx, ebx + mov TRegisters(r9).&ecx, ecx + mov TRegisters(r9).&edx, edx + mov rbx, r10 +end; + +function crc32csse42(crc: cardinal; buf: Pointer; len: cardinal): cardinal; nostackframe; assembler; +asm // ecx=crc, rdx=buf, r8=len (Linux: edi,rsi,rdx) + {$ifdef win64} + mov eax, ecx + {$else} + mov eax, edi + mov r8, rdx + mov rdx, rsi + {$endif win64} + not eax + test rdx, rdx + jz @0 + test r8, r8 + jz @0 +@7: test dl, 7 + jz @8 // align to 8 bytes boundary + crc32 eax, byte ptr[rdx] + inc rdx + dec r8 + jz @0 + test dl, 7 + jnz @7 +@8: mov rcx, r8 + shr r8, 3 + jz @2 +@1: + crc32 rax, qword [rdx] // hash 8 bytes per loop + dec r8 + lea rdx, [rdx + 8] + jnz @1 +@2: and ecx, 7 + jz @0 + cmp ecx, 4 + jb @4 + crc32 eax, dword ptr[rdx] + sub ecx, 4 + lea rdx, [rdx + 4] + jz @0 +@4: crc32 eax, byte ptr[rdx] + dec ecx + jz @0 + crc32 eax, byte ptr[rdx + 1] + dec ecx + jz @0 + crc32 eax, byte ptr[rdx + 2] +@0: not eax +end; +{$endif CPU64} + +{$ifdef CPUX86} +procedure GetCPUID(Param: Cardinal; var Registers: TRegisters); +asm + push esi + push edi + mov esi, edx + mov edi, eax + pushfd + pop eax + mov edx, eax + xor eax, $200000 + push eax + popfd + pushfd + pop eax + xor eax, edx + jz @nocpuid + push ebx + mov eax, edi + xor ecx, ecx + cpuid + mov TRegisters(esi).&eax, eax + mov TRegisters(esi).&ebx, ebx + mov TRegisters(esi).&ecx, ecx + mov TRegisters(esi).&edx, edx + pop ebx +@nocpuid: + pop edi + pop esi +end; + +function crc32csse42(crc: cardinal; buf: Pointer; len: cardinal): cardinal; +asm // eax=crc, edx=buf, ecx=len + not eax + test ecx, ecx + jz @0 + test edx, edx + jz @0 +@3: test edx, 3 + jz @8 // align to 4 bytes boundary + crc32 eax, byte ptr[edx] + inc edx + dec ecx + jz @0 + test edx, 3 + jnz @3 +@8: push ecx + shr ecx, 3 + jz @2 +@1: + crc32 eax, dword ptr[edx] + crc32 eax, dword ptr[edx + 4] + dec ecx + lea edx, [edx + 8] + jnz @1 +@2: pop ecx + and ecx, 7 + jz @0 + cmp ecx, 4 + jb @4 + crc32 eax, dword ptr[edx] + sub ecx, 4 + lea edx, [edx + 4] + jz @0 +@4: + crc32 eax, byte ptr[edx] + dec ecx + jz @0 + crc32 eax, byte ptr[edx + 1] + dec ecx + jz @0 + crc32 eax, byte ptr[edx + 2] +@0: not eax +end; +{$endif CPUX86} + +type + /// the potential features, retrieved from an Intel CPU + // - see https://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits + TIntelCpuFeature = + ( { in EDX } + cfFPU, cfVME, cfDE, cfPSE, cfTSC, cfMSR, cfPAE, cfMCE, + cfCX8, cfAPIC, cf_d10, cfSEP, cfMTRR, cfPGE, cfMCA, cfCMOV, + cfPAT, cfPSE36, cfPSN, cfCLFSH, cf_d20, cfDS, cfACPI, cfMMX, + cfFXSR, cfSSE, cfSSE2, cfSS, cfHTT, cfTM, cfIA64, cfPBE, + { in ECX } + cfSSE3, cfCLMUL, cfDS64, cfMON, cfDSCPL, cfVMX, cfSMX, cfEST, + cfTM2, cfSSSE3, cfCID, cfSDBG, cfFMA, cfCX16, cfXTPR, cfPDCM, + cf_c16, cfPCID, cfDCA, cfSSE41, cfSSE42, cfX2A, cfMOVBE, cfPOPCNT, + cfTSC2, cfAESNI, cfXS, cfOSXS, cfAVX, cfF16C, cfRAND, cfHYP, + { extended features in EBX, ECX } + cfFSGS, cf_b01, cfSGX, cfBMI1, cfHLE, cfAVX2, cf_b06, cfSMEP, + cfBMI2, cfERMS, cfINVPCID, cfRTM, cfPQM, cf_b13, cfMPX, cfPQE, + cfAVX512F, cfAVX512DQ, cfRDSEED, cfADX, cfSMAP, cfAVX512IFMA, cfPCOMMIT, cfCLFLUSH, + cfCLWB, cfIPT, cfAVX512PF, cfAVX512ER, cfAVX512CD, cfSHA, cfAVX512BW, cfAVX512VL, + cfPREFW1, cfAVX512VBMI, cfUMIP, cfPKU, cfOSPKE, cf_c05, cf_c06, cf_c07, + cf_c08, cf_c09, cf_c10, cf_c11, cf_c12, cf_c13, cfAVX512VPC, cf_c15, + cf_cc16, cf_c17, cf_c18, cf_c19, cf_c20, cf_c21, cfRDPID, cf_c23, + cf_c24, cf_c25, cf_c26, cf_c27, cf_c28, cf_c29, cfSGXLC, cf_c31, + cf_d0, cf_d1, cfAVX512NNI, cfAVX512MAS, cf_d4, cf_d5, cf_d6, cf_d7); + + /// all features, as retrieved from an Intel CPU + TIntelCpuFeatures = set of TIntelCpuFeature; + +var + /// the available CPU features, as recognized at program startup + CpuFeatures: TIntelCpuFeatures; + +procedure TestIntelCpuFeatures; +var regs: TRegisters; +begin + regs.edx := 0; + regs.ecx := 0; + GetCPUID(1,regs); + PIntegerArray(@CpuFeatures)^[0] := regs.edx; + PIntegerArray(@CpuFeatures)^[1] := regs.ecx; + GetCPUID(7,regs); + PIntegerArray(@CpuFeatures)^[2] := regs.ebx; + PIntegerArray(@CpuFeatures)^[3] := regs.ecx; + PByte(@PIntegerArray(@CpuFeatures)^[4])^ := regs.edx; +// assert(sizeof(CpuFeatures)=4*4+1); + {$ifdef Darwin} + {$ifdef CPU64} + // SSE42 asm does not (yet) work on Darwin x64 ... + Exclude(CpuFeatures, cfSSE42); + {$endif} + {$endif} +end; +{$endif CPUINTEL} + +var + crc32ctab: array[0..{$ifdef PUREPASCAL}3{$else}7{$endif},byte] of cardinal; + +function crc32cfast(crc: cardinal; buf: Pointer; len: cardinal): cardinal; +{$ifdef PUREPASCAL} +begin + result := not crc; + if (buf<>nil) and (len>0) then begin + repeat + if PtrUInt(buf) and 3=0 then // align to 4 bytes boundary + break; + result := crc32ctab[0,ToByte(result xor cardinal(buf^))] xor (result shr 8); + dec(len); + inc(buf); + until len=0; + while len>=4 do begin + result := result xor PCardinal(buf)^; + inc(buf,4); + result := crc32ctab[3,ToByte(result)] xor + crc32ctab[2,ToByte(result shr 8)] xor + crc32ctab[1,ToByte(result shr 16)] xor + crc32ctab[0,result shr 24]; + dec(len,4); + end; + while len>0 do begin + result := crc32ctab[0,ToByte(result xor cardinal(buf^))] xor (result shr 8); + dec(len); + inc(buf); + end; + end; + result := not result; +end; +{$else} +// adapted from fast Aleksandr Sharahov version +asm + test edx, edx + jz @ret + neg ecx + jz @ret + not eax + push ebx +@head: test dl, 3 + jz @aligned + movzx ebx, byte[edx] + inc edx + xor bl, al + shr eax, 8 + xor eax, dword ptr[ebx * 4 + crc32ctab] + inc ecx + jnz @head + pop ebx + not eax + ret +@ret: rep ret +@aligned: + sub edx, ecx + add ecx, 8 + jg @bodydone + push esi + push edi + mov edi, edx + mov edx, eax +@bodyloop: + mov ebx, [edi + ecx - 4] + xor edx, [edi + ecx - 8] + movzx esi, bl + mov eax, dword ptr[esi * 4 + crc32ctab + 1024 * 3] + movzx esi, bh + xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 2] + shr ebx, 16 + movzx esi, bl + xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 1] + movzx esi, bh + xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 0] + movzx esi, dl + xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 7] + movzx esi, dh + xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 6] + shr edx, 16 + movzx esi, dl + xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 5] + movzx esi, dh + xor eax, dword ptr[esi * 4 + crc32ctab + 1024 * 4] + add ecx, 8 + jg @done + mov ebx, [edi + ecx - 4] + xor eax, [edi + ecx - 8] + movzx esi, bl + mov edx, dword ptr[esi * 4 + crc32ctab + 1024 * 3] + movzx esi, bh + xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 2] + shr ebx, 16 + movzx esi, bl + xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 1] + movzx esi, bh + xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 0] + movzx esi, al + xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 7] + movzx esi, ah + xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 6] + shr eax, 16 + movzx esi, al + xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 5] + movzx esi, ah + xor edx, dword ptr[esi * 4 + crc32ctab + 1024 * 4] + add ecx, 8 + jle @bodyloop + mov eax, edx +@done: mov edx, edi + pop edi + pop esi +@bodydone: + sub ecx, 8 + jl @tail + pop ebx + not eax + ret +@tail: movzx ebx, byte[edx + ecx] + xor bl, al + shr eax, 8 + xor eax, dword ptr[ebx * 4 + crc32ctab] + inc ecx + jnz @tail + pop ebx + not eax +end; +{$endif PUREPASCAL} + +procedure InitializeCrc32ctab; +var + i, n: integer; + crc: cardinal; +begin + // initialize tables for crc32cfast() and SymmetricEncrypt/FillRandom + for i := 0 to 255 do begin + crc := i; + for n := 1 to 8 do + if (crc and 1)<>0 then // polynom is not the same as with zlib's crc32() + crc := (crc shr 1) xor $82f63b78 else + crc := crc shr 1; + crc32ctab[0,i] := crc; + end; + for i := 0 to 255 do begin + crc := crc32ctab[0,i]; + for n := 1 to high(crc32ctab) do begin + crc := (crc shr 8) xor crc32ctab[0,ToByte(crc)]; + crc32ctab[n,i] := crc; + end; + end; +end; + +begin + {$ifdef CPUINTEL} + TestIntelCpuFeatures; + if cfSSE42 in CpuFeatures then + begin + crc32c := @crc32csse42; + mORMotHasher := @crc32csse42; + end + else + {$endif CPUINTEL} + begin + InitializeCrc32ctab; + crc32c := @crc32cfast; + mORMotHasher := @xxHash32; + end; end. diff --git a/components/sparta/generics/source/generics.helpers.pas b/components/sparta/generics/source/generics.helpers.pas index 00c27297e5..a03cf8887a 100644 --- a/components/sparta/generics/source/generics.helpers.pas +++ b/components/sparta/generics/source/generics.helpers.pas @@ -20,6 +20,8 @@ unit Generics.Helpers; {$MODE DELPHI}{$H+} {$MODESWITCH TYPEHELPERS} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} interface diff --git a/components/sparta/generics/source/generics.memoryexpanders.pas b/components/sparta/generics/source/generics.memoryexpanders.pas index 156715486c..77acbf4448 100644 --- a/components/sparta/generics/source/generics.memoryexpanders.pas +++ b/components/sparta/generics/source/generics.memoryexpanders.pas @@ -21,6 +21,8 @@ unit Generics.MemoryExpanders; {$mode delphi} {$MACRO ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} {.$WARN 5024 OFF} {.$WARN 4079 OFF} @@ -38,7 +40,7 @@ type TLinearProbing = class(TProbeSequence) public - class function Probe(I, {%H-}M, Hash: UInt32): UInt32; static; inline; + class function Probe(I, Hash: UInt32): UInt32; static; inline; const MAX_LOAD_FACTOR = 1; const DEFAULT_LOAD_FACTOR = 0.75; @@ -47,13 +49,8 @@ type { TQuadraticProbing } TQuadraticProbing = class(TProbeSequence) - private - class constructor Create; public - class var C1: UInt32; - class var C2: UInt32; - - class function Probe(I, {%H-}M, Hash: UInt32): UInt32; static; inline; + class function Probe(I, Hash: UInt32): UInt32; static; inline; const MAX_LOAD_FACTOR = 0.5; const DEFAULT_LOAD_FACTOR = 0.5; @@ -63,7 +60,7 @@ type TDoubleHashing = class(TProbeSequence) public - class function Probe(I, {%H-}M, Hash1: UInt32; Hash2: UInt32 = 1): UInt32; static; inline; + class function Probe(I, Hash1: UInt32; Hash2: UInt32 = 1): UInt32; static; inline; const MAX_LOAD_FACTOR = 1; const DEFAULT_LOAD_FACTOR = 0.85; @@ -207,27 +204,21 @@ end; { TLinearProbing } -class function TLinearProbing.Probe(I, M, Hash: UInt32): UInt32; +class function TLinearProbing.Probe(I, Hash: UInt32): UInt32; begin Result := (Hash + I) end; { TQuadraticProbing } -class constructor TQuadraticProbing.Create; +class function TQuadraticProbing.Probe(I, Hash: UInt32): UInt32; begin - C1 := 1; - C2 := 1; -end; - -class function TQuadraticProbing.Probe(I, M, Hash: UInt32): UInt32; -begin - Result := (Hash + C1 * I {%H-}+ C2 * Sqr(I)); + Result := (Hash + Sqr(I)); end; { TDoubleHashingNoMod } -class function TDoubleHashing.Probe(I, M, Hash1: UInt32; Hash2: UInt32): UInt32; +class function TDoubleHashing.Probe(I, Hash1: UInt32; Hash2: UInt32): UInt32; begin Result := Hash1 + I * Hash2; end; diff --git a/components/sparta/generics/source/generics.strings.pas b/components/sparta/generics/source/generics.strings.pas index 1f9c2690ea..da80e01499 100644 --- a/components/sparta/generics/source/generics.strings.pas +++ b/components/sparta/generics/source/generics.strings.pas @@ -24,7 +24,10 @@ interface resourcestring SArgumentOutOfRange = 'Argument out of range'; + SArgumentNilNode = 'Node is nil'; SDuplicatesNotAllowed = 'Duplicates not allowed in dictionary'; + SCollectionInconsistency = 'Collection inconsistency'; + SCollectionDuplicate = 'Collection does not allow duplicates'; SDictionaryKeyDoesNotExist = 'Dictionary key does not exist'; SItemNotFound = 'Item not found'; diff --git a/components/sparta/generics/source/inc/generics.dictionaries.inc b/components/sparta/generics/source/inc/generics.dictionaries.inc index f7c0641c92..8405648346 100644 --- a/components/sparta/generics/source/inc/generics.dictionaries.inc +++ b/components/sparta/generics/source/inc/generics.dictionaries.inc @@ -16,6 +16,14 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Acknowledgment + + Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring + many new types and major refactoring of entire library + + Thanks to mORMot (http://synopse.info) project for the best implementations + of hashing functions like crc32c and xxHash32 :) + **********************************************************************} { TPair } @@ -29,7 +37,7 @@ end; { TCustomDictionary } -procedure TCustomDictionary.PairNotify(constref APair: TPair; +procedure TCustomDictionary.PairNotify(constref APair: TDictionaryPair; ACollectionNotification: TCollectionNotification); begin KeyNotify(APair.Key, ACollectionNotification); @@ -88,16 +96,35 @@ begin Create(ACollection, TEqualityComparer.Default(THashFactory)); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TCustomDictionary.Create(ACollection: TEnumerableWithPointers); +begin + Create(ACollection, TEqualityComparer.Default(THashFactory)); +end; +{$ENDIF} + constructor TCustomDictionary.Create(ACollection: TEnumerable; const AComparer: IEqualityComparer); overload; var - LItem: TPair; + LItem: TDictionaryPair; begin Create(AComparer); for LItem in ACollection do Add(LItem); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TCustomDictionary.Create(ACollection: TEnumerableWithPointers; + const AComparer: IEqualityComparer); overload; +var + LItem: PDictionaryPair; +begin + Create(AComparer); + for LItem in ACollection.Ptr^ do + Add(LItem^); +end; +{$ENDIF} + destructor TCustomDictionary.Destroy; begin Clear; @@ -143,27 +170,32 @@ begin Result := GetCurrent; end; -{ TDictionaryEnumerable } +{ TDictionaryEnumerable } -constructor TDictionaryEnumerable.Create( +function TDictionaryEnumerable.GetPtrEnumerator: TEnumerator; +begin + Result := TDictionaryPointersEnumerator.Create(FDictionary); +end; + +constructor TDictionaryEnumerable.Create( ADictionary: TCustomDictionary); begin FDictionary := ADictionary; end; -function TDictionaryEnumerable. +function TDictionaryEnumerable. DoGetEnumerator: TDictionaryEnumerator; begin Result := TDictionaryEnumerator(TDictionaryEnumerator.NewInstance); TCustomDictionaryEnumerator(Result).Create(FDictionary); end; -function TDictionaryEnumerable.GetCount: SizeInt; +function TDictionaryEnumerable.GetCount: SizeInt; begin Result := TCustomDictionary(FDictionary).Count; end; -function TDictionaryEnumerable.ToArray: TArray; +function TDictionaryEnumerable.ToArray: TArray; begin Result := ToArrayImpl(FDictionary.Count); end; @@ -193,6 +225,89 @@ begin Result := True; end; +{ TOpenAddressingPointersEnumerator } + +function TOpenAddressingPointersEnumerator.DoMoveNext: boolean; +var + LLength: SizeInt; +begin + Inc(FIndex); + + LLength := Length(FItems^); + + if FIndex >= LLength then + Exit(False); + + // maybe related to bug #24098 + // compiler error for (TDictionary(FDictionary).FItems[FIndex].Hash and UInt32.GetSignMask) = 0 + while (FItems^[FIndex].Hash and UInt32.GetSignMask) = 0 do + begin + Inc(FIndex); + if FIndex = LLength then + Exit(False); + end; + + Result := True; +end; + +function TOpenAddressingPointersEnumerator.DoGetCurrent: PDictionaryPair; +begin + Result := GetCurrent; +end; + +function TOpenAddressingPointersEnumerator.GetCurrent: PDictionaryPair; +begin + Result := @FItems^[FIndex].Pair; +end; + +constructor TOpenAddressingPointersEnumerator.Create(var AItems); +begin + FIndex := -1; + FItems := @AItems; +end; + +{ TOpenAddressingPointersCollection } + +function TOpenAddressingPointersCollection.Items: PArray; +begin + Result := PArray(@((@Self)^)); +end; + +function TOpenAddressingPointersCollection.GetCount: SizeInt; +begin + Result := PSizeInt(PByte(@((@Self)^))-SizeOf(SizeInt))^; +end; + +function TOpenAddressingPointersCollection.GetEnumerator: TPointersEnumerator; +begin + Result := TPointersEnumerator(TPointersEnumerator.NewInstance); + TPointersEnumerator(Result).Create(Items^); +end; + +function TOpenAddressingPointersCollection.ToArray: TArray; +{begin + Result := ToArrayImpl(FList.Count); +end;} +var + i: SizeInt; + LEnumerator: TPointersEnumerator; +begin + SetLength(Result, GetCount); + + try + LEnumerator := GetEnumerator; + + i := 0; + while LEnumerator.MoveNext do + begin + Result[i] := LEnumerator.Current; + Inc(i); + end; + finally + LEnumerator.Free; + end; +end; + { TOpenAddressing } constructor TOpenAddressing.Create(ACapacity: SizeInt; @@ -224,7 +339,7 @@ begin Result := FindBucketIndex(FItems, AKey, LHash); end; -function TOpenAddressing.PrepareAddingItem: SizeInt; +procedure TOpenAddressing.PrepareAddingItem; begin if RealItemsLength > FItemsThreshold then Rehash(Length(FItems) shl 1) @@ -235,9 +350,6 @@ begin end else if FItemsLength = $40000001 then // High(TIndex) ... Error: Type mismatch OutOfMemoryError; - - Result := FItemsLength; - Inc(FItemsLength); end; procedure TOpenAddressing.UpdateItemsThreshold(ASize: SizeInt); @@ -255,9 +367,17 @@ begin AItem.Pair.Key := AKey; AItem.Pair.Value := AValue; + // ! very important. FItemsLength must be increased after above code (because constref has meaning) + Inc(FItemsLength); + PairNotify(AItem.Pair, cnAdded); end; +function TOpenAddressing.GetPointers: PPointersCollection; +begin + Result := PPointersCollection(@FItems); +end; + procedure TOpenAddressing.Add(constref AKey: TKey; constref AValue: TValue); begin DoAdd(AKey, AValue); @@ -302,7 +422,7 @@ var LIndex: SizeInt; begin LIndex := FindBucketIndex(AKey); - if LIndex < 0 then + if LIndex < 0 then Exit; DoRemove(LIndex, cnRemoved); @@ -313,7 +433,7 @@ var LIndex: SizeInt; begin LIndex := FindBucketIndex(AKey); - if LIndex < 0 then + if LIndex < 0 then Exit(Default(TPair)); Result.Key := AKey; @@ -555,6 +675,13 @@ begin Result := TOpenAddressing(FDictionary).FItems[FIndex].Pair.Value; end; +{ TOpenAddressing.TPValueEnumerator } + +function TOpenAddressing.TPValueEnumerator.GetCurrent: PValue; +begin + Result := @(TOpenAddressing(FDictionary).FItems[FIndex].Pair.Value); +end; + { TOpenAddressing.TKeyEnumerator } function TOpenAddressing.TKeyEnumerator.GetCurrent: TKey; @@ -562,6 +689,13 @@ begin Result := TOpenAddressing(FDictionary).FItems[FIndex].Pair.Key; end; +{ TOpenAddressing.TPKeyEnumerator } + +function TOpenAddressing.TPKeyEnumerator.GetCurrent: PKey; +begin + Result := @(TOpenAddressing(FDictionary).FItems[FIndex].Pair.Key); +end; + { TOpenAddressingLP } procedure TOpenAddressingLP.NotifyIndexChange(AFrom, ATo: SizeInt); @@ -574,7 +708,7 @@ var LItem: PItem; LPair: TPair; LLengthMask: SizeInt; - i, m, LIndex, LGapIndex: SizeInt; + i, LIndex, LGapIndex: SizeInt; LHash, LBucket: UInt32; begin LItem := @FItems[AIndex]; @@ -583,12 +717,11 @@ begin // try fill gap LHash := LItem.Hash; LItem.Hash := 0; // prevents an infinite searching loop - m := Length(FItems); - LLengthMask := m - 1; + LLengthMask := Length(FItems) - 1; i := Succ(AIndex - (LHash and LLengthMask)); LGapIndex := AIndex; repeat - LIndex := TProbeSequence.Probe(i, m, LHash) and LLengthMask; + LIndex := TProbeSequence.Probe(i, LHash) and LLengthMask; LItem := @FItems[LIndex]; // Empty position @@ -650,7 +783,7 @@ begin Inc(i); - Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask; + Result := TProbeSequence.Probe(i, AHash) and LLengthMask; until false; end; @@ -743,7 +876,7 @@ begin Inc(i); - Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask; + Result := TProbeSequence.Probe(i, AHash) and LLengthMask; until false; end; @@ -783,11 +916,93 @@ begin Inc(i); - Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask; + Result := TProbeSequence.Probe(i, AHash) and LLengthMask; until false; end; +{ TOpenAddressingQP } + +procedure TOpenAddressingQP.UpdateItemsThreshold(ASize: SizeInt); +begin + if ASize = $40000000 then + FItemsThreshold := $40000001 + else + begin + FPrimaryNumberAsSizeApproximation := PrimaryNumbersJustLessThanPowerOfTwo[ + MultiplyDeBruijnBitPosition[UInt32(((ASize and -ASize) * $077CB531)) shr 27]]; + + FItemsThreshold := Pred(Round(FPrimaryNumberAsSizeApproximation * FMaxLoadFactor)); + end; +end; + +function TOpenAddressingQP.FindBucketIndex(constref AItems: TArray; + constref AKey: TKey; out AHash: UInt32): SizeInt; +var + LItem: {TOpenAddressing.}_TItem; // for workaround Lazarus bug #25613 + i: SizeInt; + LHash: UInt32; +begin + LHash := FEqualityComparer.GetHashCode(AKey); + + i := 0; + AHash := LHash or UInt32.GetSignMask; + + if Length(AItems) = 0 then + Exit(-1); + + for i := 0 to FPrimaryNumberAsSizeApproximation - 1 do + begin + Result := TProbeSequence.Probe(i, AHash) mod FPrimaryNumberAsSizeApproximation; + LItem := _TItem(AItems[Result]); + + // Empty position + if LItem.Hash = 0 then + Exit(not Result); // insert! + + // Same position? + if LItem.Hash = AHash then + if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then + Exit; + end; + + Result := -1; +end; + + +function TOpenAddressingQP.FindBucketIndexOrTombstone(constref AItems: TArray; + constref AKey: TKey; out AHash: UInt32): SizeInt; +var + LItem: {TOpenAddressing.}_TItem; // for workaround Lazarus bug #25613 + i: SizeInt; + LHash: UInt32; +begin + LHash := FEqualityComparer.GetHashCode(AKey); + + i := 0; + AHash := LHash or UInt32.GetSignMask; + + if Length(AItems) = 0 then + Exit(-1); + + for i := 0 to FPrimaryNumberAsSizeApproximation - 1 do + begin + Result := TProbeSequence.Probe(i, AHash) mod FPrimaryNumberAsSizeApproximation; + LItem := _TItem(AItems[Result]); + + // Empty position or tombstone + if LItem.Hash and UInt32.GetSignMask = 0 then + Exit(not Result); // insert! + + // Same position? + if LItem.Hash = AHash then + if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then + Exit; + end; + + Result := -1; +end; + { TOpenAddressingDH } constructor TOpenAddressingDH.Create(ACapacity: SizeInt; @@ -804,6 +1019,13 @@ constructor TOpenAddressingDH.Create(ACollection: T begin end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TOpenAddressingDH.Create(ACollection: TEnumerableWithPointers; + const AComparer: IEqualityComparer); +begin +end; +{$ENDIF} + constructor TOpenAddressingDH.Create(ACapacity: SizeInt); begin Create(ACapacity, TExtendedEqualityComparer.Default(THashFactory)); @@ -814,6 +1036,13 @@ begin Create(ACollection, TExtendedEqualityComparer.Default(THashFactory)); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TOpenAddressingDH.Create(ACollection: TEnumerableWithPointers); +begin + Create(ACollection, TExtendedEqualityComparer.Default(THashFactory)); +end; +{$ENDIF} + constructor TOpenAddressingDH.Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer); begin @@ -830,13 +1059,25 @@ end; constructor TOpenAddressingDH.Create(ACollection: TEnumerable; const AComparer: IExtendedEqualityComparer); var - LItem: TPair; + LItem: TDictionaryPair; begin Create(AComparer); for LItem in ACollection do Add(LItem); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TOpenAddressingDH.Create(ACollection: TEnumerableWithPointers; + const AComparer: IExtendedEqualityComparer); +var + LItem: PDictionaryPair; +begin + Create(AComparer); + for LItem in ACollection.Ptr^ do + Add(LItem^); +end; +{$ENDIF} + procedure TOpenAddressingDH.UpdateItemsThreshold(ASize: SizeInt); begin inherited; @@ -885,7 +1126,7 @@ begin Inc(i); - Result := TProbeSequence.Probe(i, m, AHash, LHash2) and LLengthMask; + Result := TProbeSequence.Probe(i, AHash, LHash2) and LLengthMask; until false; end; @@ -929,7 +1170,7 @@ begin Inc(i); - Result := TProbeSequence.Probe(i, m, AHash, LHash2) and LLengthMask; + Result := TProbeSequence.Probe(i, AHash, LHash2) and LLengthMask; until false; end; @@ -992,6 +1233,126 @@ begin Result := True; end; +{ TDeamortizedDArrayPointersEnumerator } + +function TDeamortizedDArrayPointersEnumerator.DoMoveNext: boolean; +var + LLength: SizeInt; + LArray: TItemsArray; +begin + Inc(FIndex); + + if (FMainIndex = TCuckooCfg.D) then // queue + begin + LLength := Length(FQueue.FItems); + if FIndex >= LLength then + Exit(False); + + while ((FQueue.FItems[FIndex].Hash) + and UInt32.GetSignMask) = 0 do + begin + Inc(FIndex); + if FIndex = LLength then + Exit(False); + end; + end + else // d-array + begin + LArray := FItems^[FMainIndex]; + LLength := Length(LArray); + if FIndex >= LLength then + begin + Inc(FMainIndex); + FIndex := -1; + Exit(DoMoveNext); + end; + + while (((LArray[FIndex]).Hash) and UInt32.GetSignMask) = 0 do + begin + Inc(FIndex); + if FIndex = LLength then + begin + Inc(FMainIndex); + FIndex := -1; + Exit(DoMoveNext); + end; + end; + end; + + Result := True; +end; + +function TDeamortizedDArrayPointersEnumerator.DoGetCurrent: PDictionaryPair; +begin + Result := GetCurrent; +end; + +function TDeamortizedDArrayPointersEnumerator.GetCurrent: PDictionaryPair; +begin + if FMainIndex = TCuckooCfg.D then + Result := @(FQueue.FItems[FIndex].Pair.Value.Pair) + else + Result := @((FItems^[FMainIndex])[FIndex].Pair); +end; + +constructor TDeamortizedDArrayPointersEnumerator.Create(var AItems; AQueue: TQueueDictionary; ACount: SizeInt); +begin + FIndex := -1; + if ACount = 0 then + FMainIndex := TCuckooCfg.D + else + FMainIndex := 0; + FQueue := AQueue; + FItems := @AItems; +end; + +{ TDeamortizedDArrayPointersCollection } + +function TDeamortizedDArrayPointersCollection.Items: PArray; +begin + Result := PArray(@((@Self)^)); +end; + +function TDeamortizedDArrayPointersCollection.GetCount: SizeInt; +begin + Result := SizeInt((@PByte(@((@Self)^))[-SizeOf(SizeInt)])^); +end; + +function TDeamortizedDArrayPointersCollection.GetQueue: TQueueDictionary; +begin + Result := TQueueDictionary((@PByte(@((@Self)^))[SizeOf(TItemsDArray)])^); +end; + +function TDeamortizedDArrayPointersCollection.GetEnumerator: TPointersEnumerator; +begin + Result := TPointersEnumerator(TPointersEnumerator.NewInstance); + TPointersEnumerator(Result).Create(Items^, GetQueue, GetCount); +end; + +function TDeamortizedDArrayPointersCollection.ToArray: TArray; +{begin + Result := ToArrayImpl(FList.Count); +end;} +var + i: SizeInt; + LEnumerator: TPointersEnumerator; +begin + SetLength(Result, GetCount); + + try + LEnumerator := GetEnumerator; + + i := 0; + while LEnumerator.MoveNext do + begin + Result[i] := LEnumerator.Current; + Inc(i); + end; + finally + LEnumerator.Free; + end; +end; + { TDeamortizedDArrayCuckooMap } function TDeamortizedDArrayCuckooMap.TQueueDictionary.Rehash(ASizePow2: SizeInt; @@ -1052,7 +1413,7 @@ end; function TDeamortizedDArrayCuckooMap.TQueueDictionary.Pop: Pointer; var - AIndex, LGap: SizeInt; + AIndex: SizeInt; //LResult: TDeamortizedDArrayCuckooMap.TItem; !!!bug #25917 begin AIndex := FIdx.DoRemove(FIdx.Count - 1, cnExtracted); @@ -1092,6 +1453,13 @@ constructor TDeamortizedDArrayCuckooMap.Create(ACollection: begin end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TDeamortizedDArrayCuckooMap.Create(ACollection: TEnumerableWithPointers; + const AComparer: IEqualityComparer); +begin +end; +{$ENDIF} + constructor TDeamortizedDArrayCuckooMap.Create; begin Create(0); @@ -1107,6 +1475,13 @@ begin Create(ACollection, TExtendedEqualityComparer.Default(THashFactory)); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TDeamortizedDArrayCuckooMap.Create(ACollection: TEnumerableWithPointers); +begin + Create(ACollection, TExtendedEqualityComparer.Default(THashFactory)); +end; +{$ENDIF} + constructor TDeamortizedDArrayCuckooMap.Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer); begin @@ -1136,13 +1511,25 @@ end; constructor TDeamortizedDArrayCuckooMap.Create(ACollection: TEnumerable; const AComparer: IExtendedEqualityComparer); var - LItem: TPair; + LItem: TDictionaryPair; begin Create(AComparer); for LItem in ACollection do Add(LItem); end; +{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} +constructor TDeamortizedDArrayCuckooMap.Create(ACollection: TEnumerableWithPointers; + const AComparer: IExtendedEqualityComparer); +var + LItem: PDictionaryPair; +begin + Create(AComparer); + for LItem in ACollection.Ptr^ do + Add(LItem^); +end; +{$ENDIF} + destructor TDeamortizedDArrayCuckooMap.Destroy; begin inherited; @@ -1164,6 +1551,11 @@ begin Result := TValueCollection(FValues); end; +function TDeamortizedDArrayCuckooMap.GetPointers: PPointersCollection; +begin + Result := PPointersCollection(@FItems); +end; + function TDeamortizedDArrayCuckooMap.Lookup(constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; begin @@ -1229,7 +1621,7 @@ begin Result := LR_NIL; end; -function TDeamortizedDArrayCuckooMap.PrepareAddingItem: SizeInt; +procedure TDeamortizedDArrayCuckooMap.PrepareAddingItem; var i: SizeInt; begin @@ -1243,9 +1635,6 @@ begin end else if FItemsLength = $40000001 then // High(TIndex) ... Error: Type mismatch OutOfMemoryError; - - Result := FItemsLength; - Inc(FItemsLength); end; procedure TDeamortizedDArrayCuckooMap.UpdateItemsThreshold(ASize: SizeInt); @@ -1267,7 +1656,7 @@ var y: boolean = false; b: UInt32; LIndex: UInt32; - i, j, LLengthMask: SizeInt; + i, LLengthMask: SizeInt; LTempItem: TItem; LHashList: array[0..1] of UInt32; LHashListParams: array[0..3] of UInt16 absolute LHashList; @@ -1330,10 +1719,11 @@ begin FQueue.InsertIntoHead(@LNewItem); end; -procedure TDeamortizedDArrayCuckooMap.DoAdd(constref AKey: TKey; constref AValue: TValue; +procedure TDeamortizedDArrayCuckooMap.DoAdd(const AKey: TKey; const AValue: TValue; const AHashList: PUInt32); begin AddItem(FItems, AKey, AValue, AHashList); + Inc(FItemsLength); KeyNotify(AKey, cnAdded); ValueNotify(AValue, cnAdded); end; @@ -1464,10 +1854,8 @@ end; procedure TDeamortizedDArrayCuckooMap.Rehash(ASizePow2: SizeInt); var LNewItems: TItemsDArray; - LHash: UInt32; - LIndex: SizeInt; i, j: SizeInt; - LItem, LNewItem: PItem; + LItem: PItem; LOldQueue: TQueueDictionary; var LHashList: array[0..1] of UInt32; @@ -1607,6 +1995,8 @@ end; procedure TDeamortizedDArrayCuckooMap.TrimExcess; begin SetCapacity(Succ(FItemsLength)); + FQueue.TrimExcess; + FQueue.FIdx.TrimExcess; end; procedure TDeamortizedDArrayCuckooMap.SetItem(constref AValue: TValue; @@ -1630,7 +2020,6 @@ var LHashList: array[0..TCuckooCfg.D] of UInt32; LHashListOrIndex: PUint32; LLookupResult: SizeInt; - LIndex: UInt32; begin LHashListOrIndex := @LHashList[0]; LLookupResult := Lookup(AKey, LHashListOrIndex); @@ -1666,16 +2055,17 @@ var LHashList: array[0..TCuckooCfg.D] of UInt32; LHashListOrIndex: PUint32; LLookupResult: SizeInt; - LIndex: UInt32; begin LHashListOrIndex := @LHashList[0]; LLookupResult := Lookup(AKey, LHashListOrIndex); if LLookupResult = LR_NIL then - begin - PrepareAddingItem; - DoAdd(AKey, AValue, LHashListOrIndex); - end + Add(AKey, AValue) + // more optimal version for AddOrSetValue has some bug : see Test_CuckooD2_Notification + //begin + // PrepareAddingItem; + // DoAdd(AKey, AValue, LHashListOrIndex); + //end else SetItem(AValue, LHashListOrIndex, LLookupResult); end; @@ -1751,6 +2141,16 @@ begin Result := TDeamortizedDArrayCuckooMap(FDictionary).FItems[FMainIndex][FIndex].Pair.Value; end; +{ TDeamortizedDArrayCuckooMap.TPValueEnumerator } + +function TDeamortizedDArrayCuckooMap.TPValueEnumerator.GetCurrent: PValue; +begin + if FMainIndex = TCuckooCfg.D then + Result := @(TDeamortizedDArrayCuckooMap(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Value) + else + Result := @(TDeamortizedDArrayCuckooMap(FDictionary).FItems[FMainIndex][FIndex].Pair.Value); +end; + { TDeamortizedDArrayCuckooMap.TKeyEnumerator } function TDeamortizedDArrayCuckooMap.TKeyEnumerator.GetCurrent: TKey; @@ -1761,6 +2161,16 @@ begin Result := TDeamortizedDArrayCuckooMap(FDictionary).FItems[FMainIndex][FIndex].Pair.Key; end; +{ TDeamortizedDArrayCuckooMap.TPKeyEnumerator } + +function TDeamortizedDArrayCuckooMap.TPKeyEnumerator.GetCurrent: TKey; +begin + if FMainIndex = TCuckooCfg.D then + Result := @(TDeamortizedDArrayCuckooMap(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Key) + else + Result := @(TDeamortizedDArrayCuckooMap(FDictionary).FItems[FMainIndex][FIndex].Pair.Key); +end; + { TObjectDictionary } procedure TObjectDeamortizedDArrayCuckooMap.KeyNotify( @@ -1769,7 +2179,7 @@ begin inherited; if (doOwnsKeys in FOwnerships) and (ACollectionNotification = cnRemoved) then - TObject(AKey).Free; + TObject((@AKey)^).Free; end; procedure TObjectDeamortizedDArrayCuckooMap.ValueNotify(constref AValue: TValue; @@ -1778,7 +2188,7 @@ begin inherited; if (doOwnsValues in FOwnerships) and (ACollectionNotification = cnRemoved) then - TObject(AValue).Free; + TObject((@AValue)^).Free; end; constructor TObjectDeamortizedDArrayCuckooMap.Create( @@ -1817,7 +2227,7 @@ begin inherited; if (doOwnsKeys in FOwnerships) and (ACollectionNotification = cnRemoved) then - TObject(AKey).Free; + TObject((@AKey)^).Free; end; procedure TObjectOpenAddressingLP.ValueNotify( @@ -1826,7 +2236,7 @@ begin inherited; if (doOwnsValues in FOwnerships) and (ACollectionNotification = cnRemoved) then - TObject(AValue).Free; + TObject((@AValue)^).Free; end; constructor TObjectOpenAddressingLP.Create(AOwnerships: TDictionaryOwnerships); diff --git a/components/sparta/generics/source/inc/generics.dictionariesh.inc b/components/sparta/generics/source/inc/generics.dictionariesh.inc index 9281a6e9de..e0a66db66a 100644 --- a/components/sparta/generics/source/inc/generics.dictionariesh.inc +++ b/components/sparta/generics/source/inc/generics.dictionariesh.inc @@ -16,6 +16,14 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Acknowledgment + + Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring + many new types and major refactoring of entire library + + Thanks to mORMot (http://synopse.info) project for the best implementations + of hashing functions like crc32c and xxHash32 :) + **********************************************************************} {$WARNINGS OFF} @@ -44,8 +52,7 @@ type PKey = ^TKey; PValue = ^TValue; THashFactoryClass = THashFactory; - public - FItemsLength: SizeInt; + protected FEqualityComparer: IEqualityComparer; FKeys: TEnumerable; FValues: TEnumerable; @@ -63,8 +70,6 @@ type property LoadFactor: single read GetLoadFactor; property Capacity: SizeInt read GetCapacity write SetCapacity; - property Count: SizeInt read FItemsLength; - procedure Clear; virtual; abstract; procedure Add(constref APair: TPair); virtual; abstract; strict private // bug #24283. workaround for this class because can't inherit from TEnumerable @@ -78,6 +83,10 @@ type constructor Create(const AComparer: IEqualityComparer); overload; constructor Create(ACollection: TEnumerable); virtual; overload; constructor Create(ACollection: TEnumerable; const AComparer: IEqualityComparer); virtual; overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers); virtual; overload; + constructor Create(ACollection: TEnumerableWithPointers; const AComparer: IEqualityComparer); virtual; overload; + {$ENDIF} destructor Destroy; override; private @@ -88,11 +97,15 @@ type procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); virtual; procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); virtual; - procedure PairNotify(constref APair: TPair; ACollectionNotification: TCollectionNotification); inline; + procedure PairNotify(constref APair: TDictionaryPair; ACollectionNotification: TCollectionNotification); inline; procedure SetValue(var AValue: TValue; constref ANewValue: TValue); public property OnKeyNotify: TCollectionNotifyEvent read FOnKeyNotify write FOnKeyNotify; property OnValueNotify: TCollectionNotifyEvent read FOnValueNotify write FOnValueNotify; + protected // FItemsLength must be declared at the end of TCustomDictionary + FItemsLength: SizeInt; + public + property Count: SizeInt read FItemsLength; end; { TCustomDictionaryEnumerator } @@ -110,27 +123,52 @@ type { TDictionaryEnumerable } - TDictionaryEnumerable = class abstract(TEnumerable) + TDictionaryEnumerable = class abstract(TEnumerableWithPointers) private FDictionary: TCustomDictionary; function GetCount: SizeInt; + protected + function GetPtrEnumerator: TEnumerator; override; + function DoGetEnumerator: TDictionaryEnumerator; override; public constructor Create(ADictionary: TCustomDictionary); - function DoGetEnumerator: TDictionaryEnumerator; override; function ToArray: TArray; override; final; property Count: SizeInt read GetCount; end; // more info : http://en.wikipedia.org/wiki/Open_addressing - { TDictionaryEnumerable } + { TOpenAddressingEnumerator } TOpenAddressingEnumerator = class abstract(TCustomDictionaryEnumerator) protected function DoMoveNext: Boolean; override; end; + TOpenAddressingPointersEnumerator = class abstract(TEnumerator) + private var + FItems: ^TArray; + FIndex: SizeInt; + protected + function DoMoveNext: boolean; override; + function DoGetCurrent: PDictionaryPair; override; + function GetCurrent: PDictionaryPair; virtual; + public + constructor Create(var AItems); + end; + + TOpenAddressingPointersCollection = record + private type + PArray = ^TArray; + function Items: PArray; inline; + function GetCount: SizeInt; inline; + public + function GetEnumerator: TPointersEnumerator; + function ToArray: TArray; + property Count: SizeInt read GetCount; + end; + TOnGetMemoryLayoutKeyPosition = procedure(Sender: TObject; AKeyPos: UInt32) of object; TOpenAddressing = class abstract(TCustomDictionary) @@ -142,12 +180,16 @@ type end; TItemsArray = array of TItem; - private var - FItemsThreshold: SizeInt; + TPointersEnumerator = class(TOpenAddressingPointersEnumerator); + TPointersCollection = TOpenAddressingPointersCollection; + public type + PPointersCollection = ^TPointersCollection; + private var // FItems must be declared as first field FItems: TItemsArray; + FItemsThreshold: SizeInt; procedure Resize(ANewSize: SizeInt); - function PrepareAddingItem: SizeInt; + procedure PrepareAddingItem; protected function RealItemsLength: SizeInt; virtual; function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): boolean; virtual; @@ -166,21 +208,32 @@ type function GetCurrent: TValue; override; end; + TPValueEnumerator = class(TOpenAddressingEnumerator) + protected + function GetCurrent: PValue; override; + end; + TKeyEnumerator = class(TOpenAddressingEnumerator) protected function GetCurrent: TKey; override; end; - // Collections - TValueCollection = class(TDictionaryEnumerable); + TPKeyEnumerator = class(TOpenAddressingEnumerator) + protected + function GetCurrent: PKey; override; + end; - TKeyCollection = class(TDictionaryEnumerable); + // Collections + TValueCollection = class(TDictionaryEnumerable); + + TKeyCollection = class(TDictionaryEnumerable); // bug #24283 - workaround related to lack of DoGetEnumerator function GetEnumerator: TPairEnumerator; reintroduce; private function GetKeys: TKeyCollection; function GetValues: TValueCollection; + function GetPointers: PPointersCollection; inline; private function GetItem(const AKey: TKey): TValue; inline; procedure SetItem(const AKey: TKey; const AValue: TValue); inline; @@ -217,6 +270,7 @@ type property Items[Index: TKey]: TValue read GetItem write SetItem; default; property Keys: TKeyCollection read GetKeys; property Values: TValueCollection read GetValues; + property Ptr: PPointersCollection read GetPointers; procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition); end; @@ -267,6 +321,17 @@ type out AHash: UInt32): SizeInt; override; end; + TOpenAddressingQP = class(TOpenAddressingSH) + private + FPrimaryNumberAsSizeApproximation: SizeInt; + protected + procedure UpdateItemsThreshold(ASize: SizeInt); override; + function FindBucketIndex(constref AItems: TArray; + constref AKey: TKey; out AHash: UInt32): SizeInt; override; overload; + function FindBucketIndexOrTombstone(constref AItems: TArray; + constref AKey: TKey; out AHash: UInt32): SizeInt; override; + end; + TOpenAddressingDH = class(TOpenAddressingTombstones) private type // for workaround Lazarus bug #25613 _TItem = record @@ -285,12 +350,21 @@ type constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer); override; overload; constructor Create(const AComparer: IEqualityComparer); reintroduce; overload; constructor Create(ACollection: TEnumerable; const AComparer: IEqualityComparer); override; overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers; const AComparer: IEqualityComparer); override; overload; + {$ENDIF} public // bug #26181 (redundancy of constructors) constructor Create(ACapacity: SizeInt); override; overload; constructor Create(ACollection: TEnumerable); override; overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers); override; overload; + {$ENDIF} constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer); virtual; overload; constructor Create(const AComparer: IExtendedEqualityComparer); overload; constructor Create(ACollection: TEnumerable; const AComparer: IExtendedEqualityComparer); virtual; overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers; const AComparer: IExtendedEqualityComparer); virtual; overload; + {$ENDIF} end; TDeamortizedDArrayCuckooMapEnumerator = class abstract(TCustomDictionaryEnumerator) @@ -308,6 +382,32 @@ type constructor Create(ADictionary: TCustomDictionary); end; + TDeamortizedDArrayPointersEnumerator = class abstract(TEnumerator) + private var // FItems must be declared as first field and FQueue as second + FItems: ^TItemsDArray; + FQueue: TQueueDictionary; + FIndex: SizeInt; + FMainIndex: SizeInt; + protected + function DoMoveNext: boolean; override; + function DoGetCurrent: PDictionaryPair; override; + function GetCurrent: PDictionaryPair; virtual; + public + constructor Create(var AItems; AQueue: TQueueDictionary; ACount: SizeInt); + end; + + TDeamortizedDArrayPointersCollection = record + private type + PArray = ^TItemsDArray; + function Items: PArray; inline; + function GetCount: SizeInt; inline; + function GetQueue: TQueueDictionary; inline; + public + function GetEnumerator: TPointersEnumerator; + function ToArray: TArray; + property Count: SizeInt read GetCount; + end; + // more info : // http://arxiv.org/abs/0903.0391 @@ -323,7 +423,7 @@ type end; TValueForQueue = TItem; - TQueueDictionary = class(TOpenAddressingLP) + TQueueDictionary = class(TOpenAddressingLP) private type // for workaround Lazarus bug #25613 _TItem = record Hash: UInt32; @@ -344,27 +444,31 @@ type end; // cycle-detection mechanism class - TCDM = class(TOpenAddressingSH); + TCDM = class(TOpenAddressingSH); TItemsArray = array of TItem; TItemsDArray = array[0..Pred(TCuckooCfg.D)] of TItemsArray; + TPointersEnumerator = class(TDeamortizedDArrayPointersEnumerator); + TPointersCollection = TDeamortizedDArrayPointersCollection; + public type + PPointersCollection = ^TPointersCollection; private var + FItems: TItemsDArray; FQueue: TQueueDictionary; // probably can be optimized - hash TItem give information from TItem.Hash for cuckoo ... // currently is kept in "TQueueDictionary = class(TOpenAddressingSH" FCDM: TCDM; // cycle-detection mechanism FItemsThreshold: SizeInt; - FItems: TItemsDArray; // sadly there is bug #24848 for class var ... {class} var CUCKOO_SIGN, CUCKOO_INDEX_SIZE, CUCKOO_HASH_SIGN: UInt32; // CUCKOO_MAX_ITEMS_LENGTH: <- to do : calc max length for items based on CUCKOO sign // maybe some CDM bloom filter? - procedure UpdateItemsThreshold(ASize: SizeInt); override; procedure Resize(ANewSize: SizeInt); procedure Rehash(ASizePow2: SizeInt); - function PrepareAddingItem: SizeInt; + procedure PrepareAddingItem; protected + procedure UpdateItemsThreshold(ASize: SizeInt); override; function Lookup(constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; inline; overload; function Lookup(constref AItems: TItemsDArray; constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; virtual; overload; public @@ -380,28 +484,39 @@ type function GetCurrent: TValue; override; end; + TPValueEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator) + protected + function GetCurrent: PValue; override; + end; + TKeyEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator) protected function GetCurrent: TKey; override; end; - // Collections - TValueCollection = class(TDictionaryEnumerable); + TPKeyEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator) + protected + function GetCurrent: PKey; override; + end; - TKeyCollection = class(TDictionaryEnumerable); + // Collections + TValueCollection = class(TDictionaryEnumerable); + + TKeyCollection = class(TDictionaryEnumerable); // bug #24283 - workaround related to lack of DoGetEnumerator function GetEnumerator: TPairEnumerator; reintroduce; private function GetKeys: TKeyCollection; function GetValues: TValueCollection; + function GetPointers: PPointersCollection; inline; private function GetItem(const AKey: TKey): TValue; inline; procedure SetItem(const AKey: TKey; const AValue: TValue); overload; inline; procedure SetItem(constref AValue: TValue; const AHashListOrIndex: PUInt32; ALookupResult: SizeInt); overload; procedure AddItem(constref AItems: TItemsDArray; constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload; - procedure DoAdd(constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload; inline; + procedure DoAdd(const AKey: TKey; const AValue: TValue; const AHashList: PUInt32); overload; inline; function DoRemove(const AHashListOrIndex: PUInt32; ALookupResult: SizeInt; ACollectionNotification: TCollectionNotification): TValue; @@ -417,15 +532,24 @@ type constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer); override; overload; constructor Create(const AComparer: IEqualityComparer); reintroduce; overload; constructor Create(ACollection: TEnumerable; const AComparer: IEqualityComparer); override; overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers; const AComparer: IEqualityComparer); override; overload; + {$ENDIF} public // TODO: function TryFlushQueue(ACount: SizeInt): SizeInt; constructor Create; override; overload; constructor Create(ACapacity: SizeInt); override; overload; constructor Create(ACollection: TEnumerable); override; overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers); override; overload; + {$ENDIF} constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer); virtual; overload; constructor Create(const AComparer: IExtendedEqualityComparer); overload; constructor Create(ACollection: TEnumerable; const AComparer: IExtendedEqualityComparer); virtual; overload; + {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} + constructor Create(ACollection: TEnumerableWithPointers; const AComparer: IExtendedEqualityComparer); virtual; overload; + {$ENDIF} destructor Destroy; override; procedure Add(constref APair: TPair); override; overload; @@ -443,6 +567,7 @@ type property Items[Index: TKey]: TValue read GetItem write SetItem; default; property Keys: TKeyCollection read GetKeys; property Values: TValueCollection read GetValues; + property Ptr: PPointersCollection read GetPointers; property QueueCount: SizeInt read GetQueueCount; procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition); @@ -486,17 +611,17 @@ type // useful generics overloads TOpenAddressingLP = class(TOpenAddressingLP); - TOpenAddressingLP = class(TOpenAddressingLP); + TOpenAddressingLP = class(TOpenAddressingLP); TObjectOpenAddressingLP = class(TObjectOpenAddressingLP); - TObjectOpenAddressingLP = class(TObjectOpenAddressingLP); + TObjectOpenAddressingLP = class(TObjectOpenAddressingLP); // Linear Probing with Tombstones (LPT) TOpenAddressingLPT = class(TOpenAddressingSH); - TOpenAddressingLPT = class(TOpenAddressingSH); + TOpenAddressingLPT = class(TOpenAddressingSH); - TOpenAddressingQP = class(TOpenAddressingSH); - TOpenAddressingQP = class(TOpenAddressingSH); + TOpenAddressingQP = class(TOpenAddressingQP); + TOpenAddressingQP = class(TOpenAddressingQP); TOpenAddressingDH = class(TOpenAddressingDH); TOpenAddressingDH = class(TOpenAddressingDH); @@ -528,6 +653,3 @@ type THashMap = class(TCuckooD4); TObjectHashMap = class(TObjectCuckooD4); - -var - EmptyRecord: TEmptyRecord;