6079 lines
195 KiB
PHP
6079 lines
195 KiB
PHP
{*******************************************************************************}
|
|
{ }
|
|
{ Library: Fundamentals 5.00 }
|
|
{ File name: flcDataStructs.pas }
|
|
{ File version: 5.44 }
|
|
{ Description: Data structures }
|
|
{ }
|
|
{ Copyright: Copyright (c) 1999-2020, David J Butler }
|
|
{ All rights reserved. }
|
|
{ Redistribution and use in source and binary forms, with }
|
|
{ or without modification, are permitted provided that }
|
|
{ the following conditions are met: }
|
|
{ Redistributions of source code must retain the above }
|
|
{ copyright notice, this list of conditions and the }
|
|
{ following disclaimer. }
|
|
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
|
|
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
|
|
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
|
|
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
|
|
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
|
|
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
|
|
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
|
|
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
|
|
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
|
|
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
|
|
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
|
|
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
|
|
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
|
|
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
|
|
{ POSSIBILITY OF SUCH DAMAGE. }
|
|
{ }
|
|
{ Github: https://github.com/fundamentalslib }
|
|
{ E-mail: fundamentals.library at gmail.com }
|
|
{ }
|
|
{ Description: }
|
|
{ }
|
|
{ This unit implements classes for the following commonly used data }
|
|
{ structures: }
|
|
{ + Arrays }
|
|
{ + Dictionaries }
|
|
{ + Sparse Arrays }
|
|
{ + Linked Lists }
|
|
{ }
|
|
{ ARRAYS }
|
|
{ }
|
|
{ Arrays are ordered collections where items are indexed by consecutive }
|
|
{ integer values. }
|
|
{ }
|
|
{ This unit implements array classes for each of the following types: }
|
|
{ + Int32 }
|
|
{ + LongInt }
|
|
{ + Word32 }
|
|
{ + LongWord }
|
|
{ + Int64 }
|
|
{ + Single }
|
|
{ + Double }
|
|
{ + Extended }
|
|
{ + Pointer }
|
|
{ + AnsiString }
|
|
{ + RawByteString }
|
|
{ + String }
|
|
{ + Object }
|
|
{ }
|
|
{ DICTIONARIES }
|
|
{ }
|
|
{ Dictionaries are associative arrays where the key value is a string. }
|
|
{ }
|
|
{ Associative arrays, also referred to as mappings, are unordered }
|
|
{ collections where an arbitrary key can be used to index a value. }
|
|
{ }
|
|
{ This unit implements dictionary classes for each of the following types: }
|
|
{ + Integer }
|
|
{ + Cardinal }
|
|
{ + Int64 }
|
|
{ + Single }
|
|
{ + Double }
|
|
{ + Extended }
|
|
{ + Pointer }
|
|
{ + AnsiString }
|
|
{ + RawByteString }
|
|
{ + String }
|
|
{ + TObject }
|
|
{ + IInterface }
|
|
{ }
|
|
{ For example, the class TIntegerDictionary is used where the key is an }
|
|
{ arbitrary string and the value an integer. }
|
|
{ }
|
|
{ Ages := TIntegerDictionary.Create; }
|
|
{ Ages['John'] := 29; }
|
|
{ Ages['Tori'] := 35; }
|
|
{ if Ages.HasKey['John'] then }
|
|
{ Ages.Delete('John'); }
|
|
{ Ages.Free; }
|
|
{ }
|
|
{ SPARSE ARRAYS }
|
|
{ }
|
|
{ Sparse arrays are associative arrays where the index value is an }
|
|
{ arbitrary integer. }
|
|
{ }
|
|
{ Associative arrays, also referred to as mappings, are unordered }
|
|
{ collections where an arbitrary key can be used to index a value. }
|
|
{ }
|
|
{ This unit implements sparse arrays that can hold the following values: }
|
|
{ + String }
|
|
{ + Int64 }
|
|
{ + Extended }
|
|
{ + TObject }
|
|
{ }
|
|
{ For example, the class TSparseStringArray is used where the key is an }
|
|
{ arbitrary integer and the value a string. }
|
|
{ }
|
|
{ Names := TSparseStringArray.Create; }
|
|
{ Names[123] := 'John'; }
|
|
{ Names[999] := 'Tori'; }
|
|
{ if Names.HasItem(123) then }
|
|
{ Names.Delete(123); }
|
|
{ Names.Free; }
|
|
{ }
|
|
{ Revision history: }
|
|
{ }
|
|
{ 1999/11/12 0.01 Split cTypes from cDataStruct and cHolder. }
|
|
{ 1999/11/14 0.02 Added AListType. }
|
|
{ 2000/02/08 1.03 Initial version. AArray, TArray and TStreamArray. }
|
|
{ 2000/06/07 1.04 Base classes (AIntegerArray, ASet). }
|
|
{ 2000/06/08 1.05 Added AObjectArray. }
|
|
{ 2000/06/03 1.06 Added AArray, AIntegerArray, AExtendedArray, }
|
|
{ AStringArray and ABitArray (formerly ASet) with some }
|
|
{ implementations. }
|
|
{ 2000/06/06 1.07 TFlatBitArray implementation. }
|
|
{ Added AInt64Array. }
|
|
{ 2000/06/08 1.08 Added TObjectArray. }
|
|
{ 2000/06/10 1.09 Added linked lists. }
|
|
{ 2000/06/14 1.10 Converted cDataStructs to template. }
|
|
{ 2000/06/16 1.11 Added ADictionary. }
|
|
{ 2000/07/07 1.12 Added ATypeDictionary. }
|
|
{ 2001/01/19 1.13 Added THashedStringDictionary. }
|
|
{ 2001/04/13 1.14 Added TObjectDictionary. }
|
|
{ 2001/07/15 1.15 Changed memory arrays to pre-allocate when growing. }
|
|
{ 2001/08/20 2.16 Merged cTypes and cDataStructs to allow object }
|
|
{ interface implementation in base classes. }
|
|
{ 2002/05/15 3.17 Created cArrays unit from cDataStructs. }
|
|
{ Refactored for Fundamentals 3. }
|
|
{ 2002/09/30 3.18 Moved stream array classes to unit cStreamArrays. }
|
|
{ 2002/12/17 3.19 Added THashedStringArray. }
|
|
{ 2003/03/08 3.20 Renamed Add methods to Append. }
|
|
{ 2003/05/26 3.21 Added Remove methods to object array. }
|
|
{ 2003/07/27 3.22 Initial version (sparse object array). }
|
|
{ 2003/09/11 3.23 Added TInterfaceArray. }
|
|
{ 2004/01/02 3.24 Bug fixed in TStringArray.SetAsString by Eb. }
|
|
{ 2004/01/18 3.25 Added TWideStringArray. }
|
|
{ 2004/03/31 3.26 Added sparse String, WideString and Int64 arrays. }
|
|
{ 2004/07/24 3.27 Fixed bug in Sort with duplicate values. Thanks to Eb }
|
|
{ and others for reporting it. }
|
|
{ 2004/08/01 3.28 Added AArray.RemoveDuplicates. }
|
|
{ 2005/01/27 3.29 Added sparse Extended array. }
|
|
{ 2006/05/10 3.30 Fixed bug in TDoublyLinkedList.DeleteList as reported }
|
|
{ by Malinovsky Vladimir. }
|
|
{ 2007/09/27 4.31 Merged into single unit for Fundamentals 4. }
|
|
{ 2009/09/23 4.32 Fixed bug in TDoublyLinkedList.InsertBefore/InsertAfter }
|
|
{ reported by Steffen Thorkildsen. }
|
|
{ 2011/08/27 4.33 Fixed bugs in THashedAnsiStringArray reported by }
|
|
{ H Visli. }
|
|
{ 2012/04/11 4.34 Unicode string changes. }
|
|
{ 2012/09/01 4.35 Unicode string changes. }
|
|
{ 2015/03/13 4.36 RawByteString support. }
|
|
{ 2016/01/16 5.37 Revised for Fundamentals 5. }
|
|
{ 2018/07/17 5.38 Int32/Word32 arrays. }
|
|
{ 2018/08/12 5.39 String type changes. }
|
|
{ 2019/04/02 5.40 Integer/Cardinal array changes. }
|
|
{ 2020/03/22 5.41 Rename parameters to avoid conflict with properties. }
|
|
{ 2020/03/22 5.42 Remove dependency on flcBits32. }
|
|
{ 2020/03/31 5.43 LongWord changes in bit array. Integer array changes. }
|
|
{ 2020/06/02 5.44 UInt64 changes. }
|
|
{ }
|
|
{ Supported compilers: }
|
|
{ }
|
|
{ Delphi 2010-10.4 Win32/Win64 5.44 2020/06/02 }
|
|
{ Delphi 10.2-10.4 Linux64 5.44 2020/06/02 }
|
|
{ FreePascal 3.0.4 Win64 5.44 2020/06/02 }
|
|
{ }
|
|
{*******************************************************************************}
|
|
|
|
{$INCLUDE ..\flcInclude.inc}
|
|
|
|
{$IFDEF FREEPASCAL}
|
|
{$WARNINGS OFF}
|
|
{$HINTS OFF}
|
|
{$ENDIF}
|
|
|
|
unit flcDataStructs;
|
|
|
|
interface
|
|
|
|
uses
|
|
{ System }
|
|
SysUtils,
|
|
|
|
{ Fundamentals }
|
|
flcStdTypes,
|
|
flcUtils;
|
|
|
|
|
|
|
|
{ }
|
|
{ A note on the class naming convention used in this unit: }
|
|
{ }
|
|
{ Classes with the A-prefix are abstract base classes. They define the }
|
|
{ interface for the type and must never be instanciated. Implementation }
|
|
{ classes follow the standard naming convention of using the T-prefix. }
|
|
{ }
|
|
|
|
|
|
|
|
{ }
|
|
{ TYPE BASE CLASS }
|
|
{ }
|
|
|
|
|
|
|
|
{ }
|
|
{ AType }
|
|
{ Abstract base class for data structures. }
|
|
{ }
|
|
{ Provides an interface for commonly used data operations such as }
|
|
{ assigning, comparing and duplicating. }
|
|
{ }
|
|
{ Duplicate creates a new instance of the object (using CreateInstance) and }
|
|
{ then copies the content (using Assign). Implementations do not have to }
|
|
{ override Duplicate if both CreateInstance and Assign are implemented. }
|
|
{ Assign's default implementation calls the protected AssignTo. }
|
|
{ }
|
|
{ Clear sets an instance's content (value) to an empty/zero state. This }
|
|
{ state should be similar to the state of a new instance created using }
|
|
{ CreateInstance. }
|
|
{ }
|
|
{ IsEqual compares the content of instances. After a call to Assign, an }
|
|
{ equivalent call to IsEqual should return True. }
|
|
{ }
|
|
{ Compare is the ranking function used by sorting and searching. }
|
|
{ }
|
|
{ HashValue returns a 'random' number, based on the content (value). }
|
|
{ }
|
|
{ AsString is the default string type representation of the content. }
|
|
{ }
|
|
type
|
|
EType = class(Exception);
|
|
AType = class
|
|
protected
|
|
procedure RaiseTypeError(const Msg: String; const ErrorClass: ExceptClass = nil); virtual;
|
|
|
|
procedure Init; virtual;
|
|
procedure AssignTo(const Dest: TObject); virtual;
|
|
|
|
function GetAsString: String; virtual;
|
|
procedure SetAsString(const S: String); virtual;
|
|
|
|
function GetAsUTF8String: RawByteString; virtual;
|
|
procedure SetAsUTF8String(const S: RawByteString); virtual;
|
|
|
|
function GetAsUnicodeString: UnicodeString; virtual;
|
|
procedure SetAsUnicodeString(const S: UnicodeString); virtual;
|
|
|
|
public
|
|
constructor Create;
|
|
class function CreateInstance: AType; virtual;
|
|
|
|
function Duplicate: TObject; virtual;
|
|
procedure Assign(const Source: TObject); overload; virtual;
|
|
|
|
procedure Clear; virtual;
|
|
function IsEmpty: Boolean; virtual;
|
|
function IsEqual(const V: TObject): Boolean; virtual;
|
|
function Compare(const V: TObject): TCompareResult; virtual;
|
|
function HashValue: Word32; virtual;
|
|
|
|
property AsString: String read GetAsString write SetAsString;
|
|
property AsUTF8String: RawByteString read GetAsUTF8String write SetAsUTF8String;
|
|
property AsUnicodeString: UnicodeString read GetAsUnicodeString write SetAsUnicodeString;
|
|
end;
|
|
TypeClass = class of AType;
|
|
ATypeArray = Array of AType;
|
|
TypeClassArray = Array of TypeClass;
|
|
|
|
|
|
|
|
{ }
|
|
{ AType helper functions }
|
|
{ }
|
|
function TypeDuplicate(const V: TObject): TObject;
|
|
procedure TypeAssign(const A, B: TObject);
|
|
procedure TypeClear(const V: TObject);
|
|
function TypeIsEqual(const A, B: TObject): Boolean;
|
|
function TypeCompare(const A, B: TObject): TCompareResult;
|
|
function TypeHashValue(const A: TObject): Word32;
|
|
function TypeGetAsString(const V: TObject): String;
|
|
procedure TypeSetAsString(const V: TObject; const S: String);
|
|
function TypeGetAsUTF8String(const V: TObject): RawByteString;
|
|
procedure TypeSetAsUTF8String(const V: TObject; const S: RawByteString);
|
|
function TypeGetAsUnicodeString(const V: TObject): UnicodeString;
|
|
procedure TypeSetAsUnicodeString(const V: TObject; const S: UnicodeString);
|
|
|
|
|
|
|
|
{ }
|
|
{ ARRAY BASE CLASSES }
|
|
{ }
|
|
|
|
|
|
|
|
{ }
|
|
{ AArray }
|
|
{ Base class for an array. }
|
|
{ }
|
|
type
|
|
AArray = class(AType)
|
|
protected
|
|
procedure RaiseIndexError(const Idx: Integer); virtual;
|
|
|
|
function GetAsString: String; override;
|
|
procedure SetAsString(const S: String); override;
|
|
|
|
function GetCount: Integer; virtual; abstract;
|
|
procedure SetCount(const NewCount: Integer); virtual; abstract;
|
|
|
|
function GetItemAsString(const Idx: Integer): String; virtual;
|
|
procedure SetItemAsString(const Idx: Integer; const Value: String); virtual;
|
|
|
|
public
|
|
{ AType }
|
|
procedure Clear; override;
|
|
|
|
{ AArray }
|
|
property Count: Integer read GetCount write SetCount;
|
|
property ItemAsString[const Idx: Integer]: String read GetItemAsString write SetItemAsString;
|
|
|
|
function CompareItems(const Idx1, Idx2: Integer): TCompareResult; virtual; abstract;
|
|
procedure ExchangeItems(const Idx1, Idx2: Integer); virtual; abstract;
|
|
procedure Sort; virtual;
|
|
procedure ReverseOrder; virtual;
|
|
procedure RemoveDuplicates(const IsSortedAscending: Boolean); virtual;
|
|
|
|
function DuplicateRange(const LoIdx, HiIdx: Integer): AArray; virtual; abstract;
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); virtual; abstract;
|
|
procedure Insert(const Idx: Integer; const ACount: Integer = 1); virtual; abstract;
|
|
function AppendArray(const V: AArray): Integer; overload; virtual; abstract;
|
|
end;
|
|
EArray = class(EType);
|
|
ArrayClass = class of AArray;
|
|
|
|
|
|
|
|
{%DEFINE ATypeArray}
|
|
{ }
|
|
{-A%1%Array }
|
|
{- Base class for an array of %1%s. }
|
|
{ }
|
|
type
|
|
A%1%Array = class(AArray)
|
|
protected
|
|
function GetItem(const Idx: Integer): %3%; virtual; abstract;
|
|
procedure SetItem(const Idx: Integer; const Value: %3%); virtual; abstract;{%IF 5}
|
|
|
|
function GetItemAsString(const Idx: Integer): String; override;
|
|
procedure SetItemAsString(const Idx: Integer; const Value: String); override;{%ENDIF}
|
|
|
|
function GetRange(const LoIdx, HiIdx: Integer): %1%Array; virtual;
|
|
procedure SetRange(const LoIdx, HiIdx: Integer; const V: %1%Array); virtual;{%IF 4}
|
|
|
|
function GetAsString: String; override;
|
|
procedure SetAsString(const S: String); override;{%ENDIF}
|
|
|
|
public
|
|
{ AType }
|
|
procedure Assign(const Source: TObject); override;
|
|
function IsEqual(const V: TObject): Boolean; override;
|
|
|
|
{ AArray }
|
|
procedure ExchangeItems(const Idx1, Idx2: Integer); override;{%IF 2}
|
|
function CompareItems(const Idx1, Idx2: Integer): TCompareResult; override;{%ENDIF}
|
|
function AppendArray(const V: AArray): Integer; overload; override;
|
|
function DuplicateRange(const LoIdx, HiIdx: Integer): AArray; override;
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); override;
|
|
procedure Insert(const Idx: Integer; const ACount: Integer = 1); override;
|
|
|
|
{-A%1%Array interface }
|
|
property Item[const Idx: Integer]: %3% read GetItem write SetItem; default;
|
|
property Range[const LoIdx, HiIdx: Integer]: %1%Array read GetRange write SetRange;
|
|
procedure Fill(const Idx, ACount: Integer; const Value: %3%); virtual;
|
|
function AppendItem(const Value: %3%): Integer; virtual;
|
|
function AppendArray(const V: %1%Array): Integer; overload; virtual;
|
|
function PosNext(const Find: %3%; const PrevPos: Integer = -1{%IF 2};
|
|
const IsSortedAscending: Boolean = False{%ENDIF}): Integer;
|
|
end;
|
|
E%1%Array = class(EArray);
|
|
|
|
|
|
{%ENDDEF}
|
|
{%TEMPLATE ATypeArray 'Int32' 'B' 'Int32' '' 'I' }
|
|
{%TEMPLATE ATypeArray 'Int64' 'B' 'Int64' '' 'I' }
|
|
{%TEMPLATE ATypeArray 'LongInt' 'B' 'LongInt' '' 'I' }
|
|
{ }
|
|
{ AIntegerArray }
|
|
{ }
|
|
type
|
|
AIntegerArray = AInt32Array;
|
|
EIntegerArray = EInt32Array;
|
|
|
|
|
|
|
|
{%TEMPLATE ATypeArray 'Word32' 'B' 'Word32' '' 'I' }
|
|
{%TEMPLATE ATypeArray 'Word64' 'B' 'Word64' '' 'I' }
|
|
{%TEMPLATE ATypeArray 'LongWord' 'B' 'LongWord' '' 'I' }
|
|
{ }
|
|
{ ACardinalArray }
|
|
{ }
|
|
type
|
|
ACardinalArray = AWord32Array;
|
|
ECardinalArray = EWord32Array;
|
|
|
|
|
|
|
|
{%TEMPLATE ATypeArray 'Single' 'B' 'Single' '' 'I' }
|
|
{%TEMPLATE ATypeArray 'Double' 'B' 'Double' '' 'I' }
|
|
{%TEMPLATE ATypeArray 'Extended' 'B' 'Extended' '' 'I' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeArray 'AnsiString' 'B' 'AnsiString' '' 'I' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeArray 'RawByteString' 'B' 'RawByteString' '' 'I' }
|
|
type
|
|
AUTF8StringArray = ARawByteStringArray;
|
|
EUTF8StringArray = ERawByteStringArray;
|
|
|
|
|
|
|
|
{%TEMPLATE ATypeArray 'UnicodeString' 'B' 'UnicodeString' '' 'I' }
|
|
{%TEMPLATE ATypeArray 'String' 'B' 'String' '' '' }
|
|
{%TEMPLATE ATypeArray 'Pointer' 'B' 'Pointer' '' 'I' }
|
|
{%TEMPLATE ATypeArray 'Interface' 'B' 'IInterface' '' '' }
|
|
{ }
|
|
{ AObjectArray }
|
|
{ Base class for an array of objects. }
|
|
{ }
|
|
type
|
|
EObjectArray = class(EArray);
|
|
AObjectArray = class(AArray)
|
|
protected
|
|
function GetItem(const Idx: Integer): TObject; virtual; abstract;
|
|
procedure SetItem(const Idx: Integer; const Value: TObject); virtual; abstract;
|
|
function GetRange(const LoIdx, HiIdx: Integer): ObjectArray; virtual;
|
|
procedure SetRange(const LoIdx, HiIdx: Integer; const V: ObjectArray); virtual;
|
|
function GetAsString: String; override;
|
|
function GetIsItemOwner: Boolean; virtual; abstract;
|
|
procedure SetIsItemOwner(const AIsItemOwner: Boolean); virtual; abstract;
|
|
|
|
public
|
|
{ AType }
|
|
procedure Clear; override;
|
|
procedure Assign(const Source: TObject); override;
|
|
function IsEqual(const V: TObject): Boolean; override;
|
|
function Compare(const V: TObject): TCompareResult; override;
|
|
|
|
{ AArray }
|
|
procedure ExchangeItems(const Idx1, Idx2: Integer); override;
|
|
function CompareItems(const Idx1, Idx2: Integer): TCompareResult; override;
|
|
function AppendArray(const V: AArray): Integer; overload; override;
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); override;
|
|
|
|
{ AObjectArray interface }
|
|
property Item[const Idx: Integer]: TObject read GetItem write SetItem; default;
|
|
property Range[const LoIdx, HiIdx: Integer]: ObjectArray read GetRange write SetRange;
|
|
function AppendItem(const Value: TObject): Integer; virtual;
|
|
function AppendArray(const V: ObjectArray): Integer; overload; virtual;
|
|
|
|
function PosNext(const Find: TObject; const PrevPos: Integer): Integer; overload;
|
|
function PosNext(var AItem: TObject; const AClassType: TClass; const PrevPos: Integer = -1): Integer; overload;
|
|
function PosNext(var AItem: TObject; const AClassName: String; const PrevPos: Integer = -1): Integer; overload;
|
|
function Find(const AClassType: TClass; const ACount: Integer = 1): TObject; overload;
|
|
function Find(const AClassName: String; const ACount: Integer = 1): TObject; overload;
|
|
function FindAll(const AClassType: TClass): ObjectArray; overload;
|
|
function FindAll(const AClassName: String): ObjectArray; overload;
|
|
function CountItems(const AClassType: TClass): Integer; overload;
|
|
function CountItems(const AClassName: String): Integer; overload;
|
|
function DeleteValue(const Value: TObject): Boolean;
|
|
function DeleteAll(const Value: TObject): Integer;
|
|
|
|
property IsItemOwner: Boolean read GetIsItemOwner write SetIsItemOwner;
|
|
procedure ReleaseItems; virtual; abstract;
|
|
procedure FreeItems; virtual; abstract;
|
|
function ReleaseItem(const Idx: Integer): TObject; virtual; abstract;
|
|
function ReleaseValue(const Value: TObject): Boolean;
|
|
function RemoveItem(const Idx: Integer): TObject;
|
|
function RemoveValue(const Value: TObject): Boolean;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ABitArray }
|
|
{ Base class for bit array implementations. }
|
|
{ Bits are defined as False at initialization. }
|
|
{ FindRange finds Count consecutive bits that are equal to Value. It }
|
|
{ returns the index of the leftmost bit or -1 if not found. }
|
|
{ }
|
|
type
|
|
EBitArray = class(EArray);
|
|
ABitArray = class(AArray)
|
|
protected
|
|
function GetBit(const Idx: Integer): Boolean; virtual; abstract;
|
|
procedure SetBit(const Idx: Integer; const Value: Boolean); virtual; abstract;
|
|
function GetRangeL(const Idx: Integer): Word32; virtual;
|
|
procedure SetRangeL(const Idx: Integer; const Value: Word32); virtual;
|
|
|
|
public
|
|
{ AType }
|
|
procedure Assign(const Source: TObject); override;
|
|
function IsEqual(const V: TObject): Boolean; override;
|
|
|
|
{ AArray }
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); override;
|
|
procedure Insert(const Idx: Integer; const ACount: Integer = 1); override;
|
|
function AppendArray(const V: AArray): Integer; override;
|
|
procedure ExchangeItems(const Idx1, Idx2: Integer); override;
|
|
function CompareItems(const Idx1, Idx2: Integer): TCompareResult; override;
|
|
function DuplicateRange(const LoIdx, HiIdx: Integer): AArray; override;
|
|
|
|
{ ABitArray interface }
|
|
property Bit[const Idx: Integer]: Boolean read GetBit write SetBit; default;
|
|
property RangeL[const Idx: Integer]: Word32 read GetRangeL write SetRangeL;
|
|
function IsRange(const LoIdx, HiIdx: Integer; const Value: Boolean): Boolean; virtual;
|
|
procedure Fill(const Idx, ACount: Integer; const Value: Boolean); virtual;
|
|
function AppendItem(const Value: Boolean): Integer; virtual;
|
|
procedure Invert; virtual;
|
|
|
|
function Find(const Value: Boolean = False;
|
|
const Start: Integer = 0): Integer; virtual;
|
|
function FindRange(const Value: Boolean = False;
|
|
const Start: Integer = 0;
|
|
const ACount: Integer = 1): Integer; virtual;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ARRAY IMPLEMENTATIONS }
|
|
{ }
|
|
|
|
|
|
|
|
{%DEFINE AArrayDynArray}
|
|
{ }
|
|
{-T%1%Array }
|
|
{- A%1%Array implemented using a dynamic array. }
|
|
{ }
|
|
type
|
|
T%1%Array = class(A%1%Array)
|
|
protected
|
|
FData : %1%Array;
|
|
FCapacity : Integer;
|
|
FCount : Integer;
|
|
|
|
{ ACollection }
|
|
function GetCount: Integer; override;
|
|
procedure SetCount(const NewCount: Integer); override;
|
|
|
|
{ A%1%Array }
|
|
function GetItem(const Idx: Integer): %2%; override;
|
|
procedure SetItem(const Idx: Integer; const Value: %2%); override;
|
|
function GetRange(const LoIdx, HiIdx: Integer): %1%Array; override;
|
|
procedure SetRange(const LoIdx, HiIdx: Integer; const V: %1%Array); override;
|
|
procedure SetData(const AData: %1%Array); virtual;
|
|
|
|
public
|
|
constructor Create(const V: %1%Array = nil); overload;
|
|
|
|
{ AType }
|
|
procedure Assign(const Source: TObject); overload; override;
|
|
|
|
{ AArray }
|
|
procedure ExchangeItems(const Idx1, Idx2: Integer); override;
|
|
function DuplicateRange(const LoIdx, HiIdx: Integer): AArray; override;
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); override;
|
|
procedure Insert(const Idx: Integer; const ACount: Integer = 1); override;
|
|
|
|
{ A%1%Array }
|
|
procedure Assign(const V: %1%Array); overload;
|
|
procedure Assign(const V: Array of %2%); overload;
|
|
function AppendItem(const Value: %2%): Integer; override;
|
|
|
|
{ T%1%Array }
|
|
property Data: %1%Array read FData write SetData;
|
|
property Count: Integer read FCount write SetCount;
|
|
end;
|
|
|
|
|
|
{%ENDDEF}
|
|
{%TEMPLATE AArrayDynArray 'Int32' 'Int32' }
|
|
{%TEMPLATE AArrayDynArray 'Int64' 'Int64' }
|
|
{%TEMPLATE AArrayDynArray 'LongInt' 'LongInt' }
|
|
{ }
|
|
{ TIntegerArray }
|
|
{ }
|
|
type
|
|
TIntegerArray = TInt32Array;
|
|
|
|
|
|
|
|
{ }
|
|
{ TNativeIntArray }
|
|
{ }
|
|
{$IFDEF CPU_32}
|
|
type
|
|
TNativeIntArray = TInt32Array;
|
|
{$ELSE}{$IFDEF CPU_64}
|
|
type
|
|
TNativeIntArray = TInt64Array;
|
|
{$ENDIF}{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ TIntArray }
|
|
{ }
|
|
type
|
|
TIntArray = TInt64Array;
|
|
|
|
|
|
|
|
{%TEMPLATE AArrayDynArray 'Word32' 'Word32'}
|
|
{%TEMPLATE AArrayDynArray 'Word64' 'Word64'}
|
|
{%TEMPLATE AArrayDynArray 'LongWord' 'LongWord'}
|
|
{ }
|
|
{ TCardinalArray }
|
|
{ }
|
|
type
|
|
TCardinalArray = TWord32Array;
|
|
|
|
|
|
|
|
{ }
|
|
{ TUInt32Array }
|
|
{ }
|
|
type
|
|
TUInt32Array = Word32Array;
|
|
|
|
|
|
|
|
{ }
|
|
{ TUInt64Array }
|
|
{ }
|
|
type
|
|
TUInt64Array = Word64Array;
|
|
|
|
|
|
|
|
{ }
|
|
{ TNativeUIntArray }
|
|
{ }
|
|
{$IFDEF CPU_32}
|
|
type
|
|
TNativeUIntArray = TUInt32Array;
|
|
{$ELSE}{$IFDEF CPU_64}
|
|
type
|
|
TNativeUIntArray = TUInt64Array;
|
|
{$ENDIF}{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ TNativeWordArray }
|
|
{ }
|
|
type
|
|
TNativeWordArray = TNativeUIntArray;
|
|
|
|
|
|
|
|
{ }
|
|
{ TUIntArray }
|
|
{ }
|
|
type
|
|
TUIntArray = TUInt64Array;
|
|
|
|
|
|
|
|
{%TEMPLATE AArrayDynArray 'Single' 'Single' }
|
|
{%TEMPLATE AArrayDynArray 'Double' 'Double' }
|
|
{%TEMPLATE AArrayDynArray 'Extended' 'Extended' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE AArrayDynArray 'AnsiString' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE AArrayDynArray 'RawByteString' 'RawByteString' }
|
|
{ }
|
|
{ TUTF8StringArray }
|
|
{ }
|
|
type
|
|
TUTF8StringArray = TRawByteStringArray;
|
|
|
|
|
|
|
|
{%TEMPLATE AArrayDynArray 'UnicodeString' 'UnicodeString' }
|
|
{%TEMPLATE AArrayDynArray 'String' 'String' }
|
|
{%TEMPLATE AArrayDynArray 'Pointer' 'Pointer' }
|
|
{%TEMPLATE AArrayDynArray 'Interface' 'IInterface' }
|
|
{ }
|
|
{ TObjectArray }
|
|
{ AObjectArray implemented using a dynamic array. }
|
|
{ }
|
|
type
|
|
TObjectArray = class(AObjectArray)
|
|
protected
|
|
FData : ObjectArray;
|
|
FCapacity : Integer;
|
|
FCount : Integer;
|
|
FIsItemOwner : Boolean;
|
|
|
|
procedure Init; override;
|
|
procedure SetData(const AData: ObjectArray); virtual;
|
|
|
|
{ AArray }
|
|
function GetCount: Integer; override;
|
|
procedure SetCount(const NewCount: Integer); override;
|
|
|
|
{ AObjectArray }
|
|
function GetItem(const Idx: Integer): TObject; override;
|
|
procedure SetItem(const Idx: Integer; const Value: TObject); override;
|
|
function GetRange(const LoIdx, HiIdx: Integer): ObjectArray; override;
|
|
function GetIsItemOwner: Boolean; override;
|
|
procedure SetIsItemOwner(const AIsItemOwner: Boolean); override;
|
|
|
|
public
|
|
{ TObjectArray interface }
|
|
constructor Create(const V: ObjectArray = nil;
|
|
const AIsItemOwner: Boolean = False); reintroduce; overload;
|
|
destructor Destroy; override;
|
|
|
|
property Data: ObjectArray read FData write SetData;
|
|
property Count: Integer read FCount write SetCount;
|
|
property IsItemOwner: Boolean read FIsItemOwner write FIsItemOwner;
|
|
procedure FreeItems; override;
|
|
procedure ReleaseItems; override;
|
|
function ReleaseItem(const Idx: Integer): TObject; override;
|
|
|
|
{ AArray }
|
|
function DuplicateRange(const LoIdx, HiIdx: Integer): AArray; override;
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); override;
|
|
procedure Insert(const Idx: Integer; const ACount: Integer = 1); override;
|
|
|
|
{ AObjectArray }
|
|
function AppendItem(const Value: TObject): Integer; override;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ TBitArray }
|
|
{ ABitArray implemented using a dynamic array. }
|
|
{ }
|
|
type
|
|
TBitArray = class(ABitArray)
|
|
protected
|
|
FData : Word32Array;
|
|
FCount : Integer;
|
|
|
|
{ AArray }
|
|
function GetCount: Integer; override;
|
|
procedure SetCount(const NewCount: Integer); override;
|
|
|
|
{ ABitArray }
|
|
function GetBit(const Idx: Integer): Boolean; override;
|
|
procedure SetBit(const Idx: Integer; const Value: Boolean); override;
|
|
function GetRangeL(const Idx: Integer): Word32; override;
|
|
procedure SetRangeL(const Idx: Integer; const Value: Word32); override;
|
|
|
|
public
|
|
{ ABitArray }
|
|
procedure Fill(const LoIdx, HiIdx: Integer; const Value: Boolean); override;
|
|
function IsRange(const LoIdx, HiIdx: Integer; const Value: Boolean): Boolean; override;
|
|
end;
|
|
|
|
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
{ }
|
|
{ THashedAnsiStringArray }
|
|
{ AAnsiStringArray that maintains a hash lookup table of array values. }
|
|
{ }
|
|
type
|
|
THashedAnsiStringArray = class(TAnsiStringArray)
|
|
protected
|
|
FLookup : Array of IntegerArray;
|
|
FCaseSensitive : Boolean;
|
|
|
|
function LocateItemHash(const Value: AnsiString;
|
|
var LookupList, LookupIdx: Integer): Boolean;
|
|
procedure Rehash;
|
|
|
|
procedure Init; override;
|
|
procedure SetItem(const Idx: Integer; const Value: AnsiString); override;
|
|
procedure SetData(const AData: AnsiStringArray); override;
|
|
|
|
public
|
|
constructor Create(const ACaseSensitive: Boolean = True);
|
|
|
|
procedure Assign(const Source: TObject); override;
|
|
procedure Clear; override;
|
|
|
|
procedure ExchangeItems(const Idx1, Idx2: Integer); override;
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); override;
|
|
procedure Insert(const Idx: Integer; const ACount: Integer = 1); override;
|
|
function AppendItem(const Value: AnsiString): Integer; override;
|
|
|
|
function PosNext(const Find: AnsiString; const PrevPos: Integer = -1): Integer;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ THashedRawByteStringArray }
|
|
{ ARawByteStringArray that maintains a hash lookup table of array values. }
|
|
{ }
|
|
type
|
|
THashedRawByteStringArray = class(TRawByteStringArray)
|
|
protected
|
|
FLookup : Array of IntegerArray;
|
|
FCaseSensitive : Boolean;
|
|
|
|
function LocateItemHash(const Value: RawByteString;
|
|
var LookupList, LookupIdx: Integer): Boolean;
|
|
procedure Rehash;
|
|
|
|
procedure Init; override;
|
|
procedure SetItem(const Idx: Integer; const Value: RawByteString); override;
|
|
procedure SetData(const AData: RawByteStringArray); override;
|
|
|
|
public
|
|
constructor Create(const ACaseSensitive: Boolean = True);
|
|
|
|
procedure Assign(const Source: TObject); override;
|
|
procedure Clear; override;
|
|
|
|
procedure ExchangeItems(const Idx1, Idx2: Integer); override;
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); override;
|
|
procedure Insert(const Idx: Integer; const ACount: Integer = 1); override;
|
|
function AppendItem(const Value: RawByteString): Integer; override;
|
|
|
|
function PosNext(const Find: RawByteString; const PrevPos: Integer = -1): Integer;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ THashedUnicodeStringArray }
|
|
{ AUnicodeStringArray that maintains a hash lookup table of array values. }
|
|
{ }
|
|
type
|
|
THashedUnicodeStringArray = class(TUnicodeStringArray)
|
|
protected
|
|
FLookup : Array of IntegerArray;
|
|
FCaseSensitive : Boolean;
|
|
|
|
function LocateItemHash(const Value: UnicodeString;
|
|
var LookupList, LookupIdx: Integer): Boolean;
|
|
procedure Rehash;
|
|
|
|
procedure Init; override;
|
|
procedure SetItem(const Idx: Integer; const Value: UnicodeString); override;
|
|
procedure SetData(const AData: UnicodeStringArray); override;
|
|
|
|
public
|
|
constructor Create(const ACaseSensitive: Boolean = True);
|
|
|
|
procedure Assign(const Source: TObject); override;
|
|
procedure Clear; override;
|
|
|
|
procedure ExchangeItems(const Idx1, Idx2: Integer); override;
|
|
procedure Delete(const Idx: Integer; const ACount: Integer = 1); override;
|
|
procedure Insert(const Idx: Integer; const ACount: Integer = 1); override;
|
|
function AppendItem(const Value: UnicodeString): Integer; override;
|
|
|
|
function PosNext(const Find: UnicodeString; const PrevPos: Integer = -1): Integer;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ DICTIONARY BASE CLASSES }
|
|
{ }
|
|
|
|
|
|
|
|
{ }
|
|
{ ADictionary }
|
|
{ }
|
|
type
|
|
TDictionaryDuplicatesAction = (
|
|
ddError, // raises an exception on duplicate keys
|
|
ddAccept, // allow duplicate keys
|
|
ddIgnore); // silently discard duplicates
|
|
|
|
ADictionaryBase = class(AType)
|
|
protected
|
|
function GetAsString: String; override;
|
|
|
|
function GetAddOnSet: Boolean; virtual; abstract;
|
|
procedure SetAddOnSet(const AAddOnSet: Boolean); virtual; abstract;
|
|
function GetDuplicatesAction: TDictionaryDuplicatesAction; virtual; abstract;
|
|
procedure SetDuplicatesAction(const Value: TDictionaryDuplicatesAction); virtual; abstract;
|
|
|
|
function GetKeyStrByIndex(const Idx: Integer): String; virtual; abstract;
|
|
function GetItemStrByIndex(const Idx: Integer): String; virtual;
|
|
|
|
public
|
|
{ ADictionaryBase }
|
|
property AddOnSet: Boolean read GetAddOnSet write SetAddOnSet;
|
|
property DuplicatesAction: TDictionaryDuplicatesAction
|
|
read GetDuplicatesAction write SetDuplicatesAction;
|
|
|
|
function Count: Integer; virtual; abstract;
|
|
end;
|
|
|
|
EDictionary = class(EType);
|
|
|
|
|
|
|
|
{%DEFINE ATypeDictionaryBase}
|
|
{ }
|
|
{-ADictionary%1% }
|
|
{- Base class for a dictionary with %2% keys. }
|
|
{ }
|
|
type
|
|
ADictionary%1% = class(ADictionaryBase)
|
|
protected
|
|
procedure RaiseKeyNotFoundError(const Key: %2%);
|
|
procedure RaiseDuplicateKeyError(const Key: %2%);
|
|
|
|
function GetKeysCaseSensitive: Boolean; virtual; abstract;
|
|
|
|
public
|
|
{ ADictionary }
|
|
procedure Delete(const Key: %2%); virtual; abstract;
|
|
function HasKey(const Key: %2%): Boolean; virtual; abstract;
|
|
procedure Rename(const Key, NewKey: %2%); virtual; abstract;
|
|
|
|
function GetKeyByIndex(const Idx: Integer): %2%; virtual; abstract;
|
|
function GetKeyStrByIndex(const Idx: Integer): String; override;
|
|
procedure DeleteItemByIndex(const Idx: Integer); virtual; abstract;
|
|
|
|
property KeysCaseSensitive: Boolean read GetKeysCaseSensitive;
|
|
end;
|
|
|
|
|
|
{%ENDDEF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryBase 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryBase 'B' 'RawByteString' }
|
|
{%TEMPLATE ATypeDictionaryBase 'U' 'UnicodeString' }
|
|
{%TEMPLATE ATypeDictionaryBase '' 'String' }
|
|
{%DEFINE ATypeDictionary}
|
|
{ }
|
|
{-A%1%Dictionary%5% }
|
|
{- A Dictionary with %1% values and %6% keys. }
|
|
{ }
|
|
type
|
|
A%1%Dictionary%5% = class(ADictionary%5%)
|
|
protected
|
|
function GetAsString: String; override;{%IF 7}
|
|
|
|
function GetItemStrByIndex(const Idx: Integer): String; override;{%ENDIF}
|
|
|
|
function GetItem(const Key: %6%): %2%; virtual;
|
|
procedure SetItem(const Key: %6%; const Value: %2%); virtual; abstract;{%IF 8}
|
|
|
|
function GetIsItemOwner: Boolean; virtual; abstract;
|
|
procedure SetIsItemOwner(const AIsItemOwner: Boolean); virtual; abstract;{%ENDIF}
|
|
|
|
public
|
|
{ AType }
|
|
procedure Assign(const Source: TObject); override;{%IF 4}
|
|
procedure StreamOut(const Writer: AWriterEx); override;
|
|
procedure StreamIn(const Reader: AReaderEx); override;{%ENDIF}{%IF 8}
|
|
procedure Clear; override;{%ENDIF}
|
|
|
|
{ A%1%Dictionary }
|
|
property Item[const Key: %6%]: %2% read GetItem write SetItem; default;
|
|
procedure Add(const Key: %6%; const Value: %2%); virtual; abstract;
|
|
|
|
function GetItemByIndex(const Idx: Integer): %2%; virtual; abstract;
|
|
function LocateItem(const Key: %6%; var Value: %2%): Integer; virtual; abstract;
|
|
function LocateNext(const Key: %6%; const Idx: Integer;
|
|
var Value: %2%): Integer; virtual; abstract;{%IF 3}
|
|
|
|
function GetItemLength(const Key: %6%): Integer; virtual;
|
|
function GetTotalLength: Int64; virtual;{%ENDIF}{%IF 8}
|
|
|
|
property IsItemOwner: Boolean read GetIsItemOwner write SetIsItemOwner;
|
|
function ReleaseItem(const Key: %6%): TObject; virtual; abstract;
|
|
procedure ReleaseItems; virtual; abstract;
|
|
procedure FreeItems; virtual; abstract;{%ENDIF}
|
|
end;
|
|
E%1%Dictionary%5% = class(EDictionary);
|
|
|
|
|
|
{%ENDDEF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'LongInt' 'LongInt' '' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'LongInt' 'LongInt' '' '' 'B' 'RawByteString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'LongInt' 'LongInt' '' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'LongInt' 'LongInt' '' '' '' 'String' 'I' '' }
|
|
{ }
|
|
{ AIntegerDictionary }
|
|
{ }
|
|
type
|
|
{$IFDEF SupportAnsiString}
|
|
AIntegerDictionaryA = ALongIntDictionaryA;
|
|
{$ENDIF}
|
|
AIntegerDictionaryB = ALongIntDictionaryB;
|
|
AIntegerDictionaryU = ALongIntDictionaryU;
|
|
AIntegerDictionary = ALongIntDictionary;
|
|
|
|
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'LongWord' 'LongWord' '' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'LongWord' 'LongWord' '' '' 'B' 'RawByteString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'LongWord' 'LongWord' '' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'LongWord' 'LongWord' '' '' '' 'String' 'I' '' }
|
|
{ }
|
|
{ ACardinalDictionary }
|
|
{ }
|
|
type
|
|
{$IFDEF SupportAnsiString}
|
|
ACardinalDictionaryA = ALongWordDictionaryA;
|
|
{$ENDIF}
|
|
ACardinalDictionaryB = ALongWordDictionaryB;
|
|
ACardinalDictionaryU = ALongWordDictionaryU;
|
|
ACardinalDictionary = ALongWordDictionary;
|
|
|
|
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'Int64' 'Int64' '' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'Int64' 'Int64' '' '' 'B' 'RawByteString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Int64' 'Int64' '' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Int64' 'Int64' '' '' '' 'String' 'I' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'Single' 'Single' '' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'Single' 'Single' '' '' 'B' 'RawByteString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Single' 'Single' '' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Single' 'Single' '' '' '' 'String' 'I' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'Double' 'Double' '' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'Double' 'Double' '' '' 'B' 'RawByteString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Double' 'Double' '' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Double' 'Double' '' '' '' 'String' 'I' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'Extended' 'Extended' '' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'Extended' 'Extended' '' '' 'B' 'RawByteString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Extended' 'Extended' '' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Extended' 'Extended' '' '' '' 'String' 'I' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'AnsiString' 'AnsiString' 'L' '' 'A' 'AnsiString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'AnsiString' 'AnsiString' 'L' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'AnsiString' 'AnsiString' 'L' '' '' 'String' 'I' '' }
|
|
{$ENDIF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'RawByteString' 'RawByteString' 'L' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'RawByteString' 'RawByteString' 'L' '' 'B' 'RawByteString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'RawByteString' 'RawByteString' 'L' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'RawByteString' 'RawByteString' 'L' '' '' 'String' 'I' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'UnicodeString' 'UnicodeString' 'L' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'UnicodeString' 'UnicodeString' 'L' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'UnicodeString' 'UnicodeString' 'L' '' '' 'String' 'I' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'String' 'String' 'L' '' 'A' 'AnsiString' '' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'String' 'String' 'L' '' 'U' 'UnicodeString' '' '' }
|
|
{%TEMPLATE ATypeDictionary 'String' 'String' 'L' '' '' 'String' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'Pointer' 'Pointer' '' '' 'A' 'AnsiString' 'I' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'Pointer' 'Pointer' '' '' 'B' 'RawByteString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Pointer' 'Pointer' '' '' 'U' 'UnicodeString' 'I' '' }
|
|
{%TEMPLATE ATypeDictionary 'Pointer' 'Pointer' '' '' '' 'String' 'I' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'Interface' 'IInterface' '' '' 'A' 'AnsiString' '' '' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'Interface' 'IInterface' '' '' 'U' 'UnicodeString' '' '' }
|
|
{%TEMPLATE ATypeDictionary 'Interface' 'IInterface' '' '' '' 'String' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionary 'Object' 'TObject' '' '' 'A' 'AnsiString' 'I' 'O' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionary 'Object' 'TObject' '' '' 'B' 'RawByteString' 'I' 'O' }
|
|
{%TEMPLATE ATypeDictionary 'Object' 'TObject' '' '' 'U' 'UnicodeString' 'I' 'O' }
|
|
{%TEMPLATE ATypeDictionary 'Object' 'TObject' '' '' '' 'String' 'I' 'O' }
|
|
|
|
|
|
|
|
{ }
|
|
{ DICTIONARY IMPLEMENTATIONS }
|
|
{ }
|
|
|
|
|
|
|
|
{%DEFINE ADictionaryAArray}
|
|
{ }
|
|
{-T%1%Dictionary }
|
|
{- Implements A%1%Dictionary using arrays. }
|
|
{- A 'chained-hash' lookup table is used for quick access. }
|
|
{ }
|
|
type
|
|
TGeneral%1%Dictionary%4% = class(A%1%Dictionary%4%)
|
|
protected
|
|
FKeys : T%5%Array;
|
|
FValues : T%1%Array;
|
|
FLookup : Array of IntegerArray;
|
|
FHashSize : Integer;
|
|
FCaseSensitive : Boolean;
|
|
FAddOnSet : Boolean;
|
|
FDuplicatesAction : TDictionaryDuplicatesAction;
|
|
|
|
function LocateKey(const Key: %5%; var LookupIdx: Word32;
|
|
const ErrorIfNotFound: Boolean): Integer; virtual;
|
|
procedure DeleteByIndex(const Idx: Integer; const Hash: Integer = -1);
|
|
procedure Rehash;
|
|
function GetHashTableSize: Integer;
|
|
procedure RaiseIndexError;
|
|
|
|
{ ADictionary }
|
|
function GetKeysCaseSensitive: Boolean; override;
|
|
function GetAddOnSet: Boolean; override;
|
|
procedure SetAddOnSet(const AAddOnSet: Boolean); override;
|
|
function GetDuplicatesAction: TDictionaryDuplicatesAction; override;
|
|
procedure SetDuplicatesAction(const ADuplicatesAction: TDictionaryDuplicatesAction); override;
|
|
|
|
{ A%1%Dictionary }{%IF 3}
|
|
function GetIsItemOwner: Boolean; override;
|
|
procedure SetIsItemOwner(const AIsItemOwner: Boolean); override;
|
|
{%ENDIF}
|
|
procedure SetItem(const Key: %5%; const Value: %2%); override;
|
|
|
|
public
|
|
{ TGeneral%1%Dictionary }
|
|
constructor Create;
|
|
constructor CreateEx(
|
|
const AKeys: T%5%Array = nil;
|
|
const AValues: T%1%Array = nil;{%IF 3}
|
|
const AIsItemOwner: Boolean = False;{%ENDIF}
|
|
const AKeysCaseSensitive: Boolean = True;
|
|
const AAddOnSet: Boolean = True;
|
|
const ADuplicatesAction: TDictionaryDuplicatesAction = ddAccept);
|
|
destructor Destroy; override;
|
|
|
|
property Keys: T%5%Array read FKeys;
|
|
property Values: T%1%Array read FValues;
|
|
property HashTableSize: Integer read GetHashTableSize;
|
|
|
|
{ AType }
|
|
procedure Clear; override;
|
|
|
|
{ ADictionary }
|
|
procedure Delete(const Key: %5%); override;
|
|
function HasKey(const Key: %5%): Boolean; override;
|
|
procedure Rename(const Key: %5%; const NewKey: %5%); override;
|
|
function Count: Integer; override;
|
|
function GetKeyByIndex(const Idx: Integer): %5%; override;
|
|
procedure DeleteItemByIndex(const Idx: Integer); override;
|
|
|
|
{ A%1%Dictionary }
|
|
procedure Add(const Key: %5%; const Value: %2%); override;
|
|
function GetItemByIndex(const Idx: Integer): %2%; override;
|
|
procedure SetItemByIndex(const Idx: Integer; const Value: %2%);
|
|
function LocateItem(const Key: %5%; var Value: %2%): Integer; override;
|
|
function LocateNext(const Key: %5%; const Idx: Integer;
|
|
var Value: %2%): Integer; override;{%IF 3}
|
|
|
|
function ReleaseItem(const Key: %5%): TObject; override;
|
|
procedure ReleaseItems; override;
|
|
procedure FreeItems; override;{%ENDIF}
|
|
end;
|
|
|
|
T%1%Dictionary%4% = class(TGeneral%1%Dictionary%4%)
|
|
protected
|
|
function GetItem(const Key: %5%): %2%; override;
|
|
function LocateKey(const Key: %5%; var LookupIdx: Word32;
|
|
const ErrorIfNotFound: Boolean): Integer; override;
|
|
|
|
public
|
|
constructor CreateEx(
|
|
const AKeys: T%5%Array = nil;
|
|
const AValues: T%1%Array = nil;{%IF 3}
|
|
const AIsItemOwner: Boolean = False;{%ENDIF}
|
|
const AKeysCaseSensitive: Boolean = True;
|
|
const AAddOnSet: Boolean = True;
|
|
const ADuplicatesAction: TDictionaryDuplicatesAction = ddAccept);
|
|
|
|
function LocateItem(const Key: %5%; var Value: %2%): Integer; override;
|
|
end;
|
|
|
|
|
|
{%ENDDEF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'LongInt' 'LongInt' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'LongInt' 'LongInt' '' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'LongInt' 'LongInt' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'LongInt' 'LongInt' '' '' 'String' }
|
|
{ }
|
|
{ TIntegerDictionary }
|
|
{ }
|
|
type
|
|
{$IFDEF SupportAnsiString}
|
|
TGeneralIntegerDictionaryA = TGeneralLongIntDictionaryA;
|
|
{$ENDIF}
|
|
TGeneralIntegerDictionary = TGeneralLongIntDictionary;
|
|
{$IFDEF SupportAnsiString}
|
|
TIntegerDictionaryA = TLongIntDictionaryA;
|
|
{$ENDIF}
|
|
TIntegerDictionary = TLongIntDictionary;
|
|
|
|
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'LongWord' 'LongWord' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'LongWord' 'LongWord' '' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'LongWord' 'LongWord' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'LongWord' 'LongWord' '' '' 'String' }
|
|
{ }
|
|
{ TCardinalDictionary }
|
|
{ }
|
|
type
|
|
{$IFDEF SupportAnsiString}
|
|
TGeneralCardinalDictionaryA = TGeneralLongWordDictionaryA;
|
|
{$ENDIF}
|
|
TGeneralCardinalDictionaryU = TGeneralLongWordDictionaryU;
|
|
TGeneralCardinalDictionary = TGeneralLongWordDictionary;
|
|
{$IFDEF SupportAnsiString}
|
|
TCardinalDictionaryA = TLongWordDictionaryA;
|
|
{$ENDIF}
|
|
TCardinalDictionaryU = TLongWordDictionaryU;
|
|
TCardinalDictionary = TLongWordDictionary;
|
|
|
|
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'Int64' 'Int64' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'Int64' 'Int64' '' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'Int64' 'Int64' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'Int64' 'Int64' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'Single' 'Single' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'Single' 'Single' '' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'Single' 'Single' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'Single' 'Single' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'Double' 'Double' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'Double' 'Double' '' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'Double' 'Double' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'Double' 'Double' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'Extended' 'Extended' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'Extended' 'Extended' '' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'Extended' 'Extended' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'Extended' 'Extended' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'AnsiString' 'AnsiString' '' 'A' 'AnsiString' }
|
|
{%TEMPLATE ADictionaryAArray 'AnsiString' 'AnsiString' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'AnsiString' 'AnsiString' '' '' 'String' }
|
|
{$ENDIF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'RawByteString' 'RawByteString' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'RawByteString' 'RawByteString' '' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'RawByteString' 'RawByteString' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'RawByteString' 'RawByteString' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'UnicodeString' 'UnicodeString' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'UnicodeString' 'UnicodeString' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'UnicodeString' 'UnicodeString' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'String' 'String' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'String' 'String' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'String' 'String' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'Pointer' 'Pointer' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'Pointer' 'Pointer' '' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'Pointer' 'Pointer' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'Pointer' 'Pointer' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'Interface' 'IInterface' '' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'Interface' 'IInterface' '' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'Interface' 'IInterface' '' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryAArray 'Object' 'TObject' 'O' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryAArray 'Object' 'TObject' 'O' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryAArray 'Object' 'TObject' 'O' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryAArray 'Object' 'TObject' 'O' '' 'String' }
|
|
{ }
|
|
{ Dictionary functions }
|
|
{ }
|
|
const
|
|
DictionaryAverageHashChainSize = 4;
|
|
|
|
function DictionaryRehashSize(const Count: Integer): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ SPARSE ARRAY BASE CLASSES }
|
|
{ }
|
|
|
|
|
|
|
|
{ }
|
|
{ ASparseArray }
|
|
{ Sparse array base class. }
|
|
{ }
|
|
type
|
|
ASparseArray = class(AType)
|
|
protected
|
|
procedure IndexError;
|
|
function GetCount: Integer; virtual; abstract;
|
|
|
|
public
|
|
property Count: Integer read GetCount;
|
|
function IsEmpty: Boolean; override;
|
|
procedure Delete(const Idx: Integer); virtual; abstract;
|
|
function HasItem(const Idx: Integer): Boolean; virtual; abstract;
|
|
end;
|
|
ESparseArray = class(EType);
|
|
|
|
|
|
|
|
{ }
|
|
{ SPARSE ARRAY IMPLEMENTATIONS }
|
|
{ }
|
|
|
|
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
{ }
|
|
{ TSparseAnsiStringArray }
|
|
{ Sparse array that holds String values. }
|
|
{ }
|
|
type
|
|
TSparseAnsiStringRecord = record
|
|
Idx : Integer;
|
|
Value : AnsiString;
|
|
end;
|
|
PSparseAnsiStringRecord = ^TSparseAnsiStringRecord;
|
|
TSparseAnsiStringRecordArray = Array of TSparseAnsiStringRecord;
|
|
TSparseAnsiStringArrayHashList = Array of TSparseAnsiStringRecordArray;
|
|
|
|
TSparseAnsiStringArray = class(ASparseArray)
|
|
private
|
|
FHashList : TSparseAnsiStringArrayHashList;
|
|
FHashSize : Integer;
|
|
FCount : Integer;
|
|
|
|
protected
|
|
function LocateItemRecord(const Idx: Integer;
|
|
var LookupIdx, ChainIdx: Integer): PSparseAnsiStringRecord;
|
|
procedure Rehash;
|
|
|
|
function GetCount: Integer; override;
|
|
function GetItem(const Idx: Integer): AnsiString;
|
|
procedure SetItem(const Idx: Integer; const Value: AnsiString);
|
|
|
|
public
|
|
procedure Assign(const Source: TObject); override;
|
|
function IsEqual(const V: TObject): Boolean; override;
|
|
|
|
property Item[const Idx: Integer]: AnsiString read GetItem write SetItem; default;
|
|
function LocateItem(const Idx: Integer; var Value: AnsiString): Boolean;
|
|
|
|
property Count: Integer read FCount;
|
|
function IsEmpty: Boolean; override;
|
|
procedure Clear; override;
|
|
|
|
procedure Delete(const Idx: Integer); override;
|
|
|
|
function HasItem(const Idx: Integer): Boolean; override;
|
|
function FindFirst(var Idx: Integer; var Value: AnsiString): Boolean;
|
|
function FindNext(var Idx: Integer; var Value: AnsiString): Boolean;
|
|
end;
|
|
ESparseAnsiStringArray = class(ESparseArray);
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ TSparseInt64Array }
|
|
{ Sparse array that holds Int64 values. }
|
|
{ }
|
|
type
|
|
TSparseInt64Record = record
|
|
Idx : Integer;
|
|
Value : Int64;
|
|
end;
|
|
PSparseInt64Record = ^TSparseInt64Record;
|
|
TSparseInt64RecordArray = Array of TSparseInt64Record;
|
|
TSparseInt64ArrayHashList = Array of TSparseInt64RecordArray;
|
|
|
|
TSparseInt64Array = class(ASparseArray)
|
|
private
|
|
FHashList : TSparseInt64ArrayHashList;
|
|
FHashSize : Integer;
|
|
FCount : Integer;
|
|
|
|
protected
|
|
function LocateItemRecord(const Idx: Integer;
|
|
var LookupIdx, ChainIdx: Integer): PSparseInt64Record;
|
|
procedure Rehash;
|
|
|
|
function GetCount: Integer; override;
|
|
function GetItem(const Idx: Integer): Int64;
|
|
procedure SetItem(const Idx: Integer; const Value: Int64);
|
|
|
|
public
|
|
procedure Assign(const Source: TObject); override;
|
|
function IsEqual(const V: TObject): Boolean; override;
|
|
|
|
property Item[const Idx: Integer]: Int64 read GetItem write SetItem; default;
|
|
function LocateItem(const Idx: Integer; var Value: Int64): Boolean;
|
|
|
|
property Count: Integer read FCount;
|
|
function IsEmpty: Boolean; override;
|
|
procedure Clear; override;
|
|
|
|
procedure Delete(const Idx: Integer); override;
|
|
|
|
function HasItem(const Idx: Integer): Boolean; override;
|
|
function FindFirst(var Idx: Integer; var Value: Int64): Boolean;
|
|
function FindNext(var Idx: Integer; var Value: Int64): Boolean;
|
|
end;
|
|
ESparseInt64Array = class(ESparseArray);
|
|
|
|
|
|
|
|
{ }
|
|
{ TSparseExtendedArray }
|
|
{ Sparse array that holds Extended values. }
|
|
{ }
|
|
type
|
|
TSparseExtendedRecord = record
|
|
Idx : Integer;
|
|
Value : Extended;
|
|
end;
|
|
PSparseExtendedRecord = ^TSparseExtendedRecord;
|
|
TSparseExtendedRecordArray = Array of TSparseExtendedRecord;
|
|
TSparseExtendedArrayHashList = Array of TSparseExtendedRecordArray;
|
|
|
|
TSparseExtendedArray = class(ASparseArray)
|
|
private
|
|
FHashList : TSparseExtendedArrayHashList;
|
|
FHashSize : Integer;
|
|
FCount : Integer;
|
|
|
|
protected
|
|
function LocateItemRecord(const Idx: Integer;
|
|
var LookupIdx, ChainIdx: Integer): PSparseExtendedRecord;
|
|
procedure Rehash;
|
|
|
|
function GetCount: Integer; override;
|
|
function GetItem(const Idx: Integer): Extended;
|
|
procedure SetItem(const Idx: Integer; const Value: Extended);
|
|
|
|
public
|
|
procedure Assign(const Source: TObject); override;
|
|
function IsEqual(const V: TObject): Boolean; override;
|
|
|
|
property Item[const Idx: Integer]: Extended read GetItem write SetItem; default;
|
|
function LocateItem(const Idx: Integer; var Value: Extended): Boolean;
|
|
|
|
property Count: Integer read FCount;
|
|
function IsEmpty: Boolean; override;
|
|
procedure Clear; override;
|
|
|
|
procedure Delete(const Idx: Integer); override;
|
|
|
|
function HasItem(const Idx: Integer): Boolean; override;
|
|
function FindFirst(var Idx: Integer; var Value: Extended): Boolean;
|
|
function FindNext(var Idx: Integer; var Value: Extended): Boolean;
|
|
end;
|
|
ESparseExtendedArray = class(ESparseArray);
|
|
|
|
|
|
|
|
{ }
|
|
{ TSparseObjectArray }
|
|
{ Sparse array that holds TObject values. }
|
|
{ }
|
|
type
|
|
TSparseObjectRecord = record
|
|
Idx : Integer;
|
|
Value : TObject;
|
|
end;
|
|
PSparseObjectRecord = ^TSparseObjectRecord;
|
|
TSparseObjectRecordArray = Array of TSparseObjectRecord;
|
|
TSparseObjectArrayHashList = Array of TSparseObjectRecordArray;
|
|
|
|
TSparseObjectArray = class(ASparseArray)
|
|
private
|
|
FHashList : TSparseObjectArrayHashList;
|
|
FHashSize : Integer;
|
|
FCount : Integer;
|
|
FIsItemOwner : Boolean;
|
|
|
|
protected
|
|
procedure Init; override;
|
|
|
|
function LocateItemRecord(const Idx: Integer;
|
|
var LookupIdx, ChainIdx: Integer): PSparseObjectRecord;
|
|
procedure Rehash;
|
|
|
|
function GetCount: Integer; override;
|
|
function GetItem(const Idx: Integer): TObject;
|
|
procedure SetItem(const Idx: Integer; const Value: TObject);
|
|
|
|
public
|
|
constructor Create(const AIsItemOwner: Boolean = False);
|
|
destructor Destroy; override;
|
|
|
|
procedure Assign(const Source: TObject); override;
|
|
function IsEqual(const V: TObject): Boolean; override;
|
|
|
|
property IsItemOwner: Boolean read FIsItemOwner write FIsItemOwner;
|
|
property Item[const Idx: Integer]: TObject read GetItem write SetItem; default;
|
|
function LocateItem(const Idx: Integer; var Value: TObject): Boolean;
|
|
|
|
property Count: Integer read FCount;
|
|
function IsEmpty: Boolean; override;
|
|
procedure Clear; override;
|
|
|
|
procedure Delete(const Idx: Integer); override;
|
|
function ReleaseItem(const Idx: Integer): TObject;
|
|
|
|
function HasItem(const Idx: Integer): Boolean; override;
|
|
function FindFirst(var Idx: Integer; var Value: TObject): Boolean;
|
|
function FindNext(var Idx: Integer; var Value: TObject): Boolean;
|
|
end;
|
|
ESparseObjectArray = class(ESparseArray);
|
|
|
|
|
|
|
|
{ }
|
|
{ Linked lists }
|
|
{ }
|
|
type
|
|
TDoublyLinkedItem = class
|
|
protected
|
|
FNext : TDoublyLinkedItem;
|
|
FPrev : TDoublyLinkedItem;
|
|
|
|
public
|
|
destructor DestroyList;
|
|
|
|
property Next: TDoublyLinkedItem read FNext write FNext;
|
|
property Prev: TDoublyLinkedItem read FPrev write FPrev;
|
|
|
|
function HasNext: Boolean;
|
|
function HasPrev: Boolean;
|
|
function Last: TDoublyLinkedItem;
|
|
function First: TDoublyLinkedItem;
|
|
function Count: Integer;
|
|
|
|
procedure Remove;
|
|
function RemoveNext: TDoublyLinkedItem;
|
|
procedure DeleteNext;
|
|
function RemovePrev: TDoublyLinkedItem;
|
|
procedure DeletePrev;
|
|
procedure InsertAfter(const Item: TDoublyLinkedItem);
|
|
procedure InsertBefore(const Item: TDoublyLinkedItem);
|
|
procedure Delete;
|
|
end;
|
|
|
|
{%DEFINE LinkedList}
|
|
TDoublyLinked%1% = class(TDoublyLinkedItem)
|
|
public
|
|
Value : %2%;
|
|
|
|
constructor Create(const V: %2%);
|
|
|
|
procedure InsertAfter(const V: %2%); overload;
|
|
procedure InsertBefore(const V: %2%); overload;
|
|
procedure InsertFirst(const V: %2%);
|
|
procedure Append(const V: %2%);
|
|
function FindNext(const Find: %2%): TDoublyLinked%1%;
|
|
function FindPrev(const Find: %2%): TDoublyLinked%1%;
|
|
end;
|
|
{%ENDDEF}
|
|
{%TEMPLATE LinkedList 'Integer' 'Integer'}
|
|
{%TEMPLATE LinkedList 'Extended' 'Extended'}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE LinkedList 'String' 'AnsiString'}
|
|
{$ENDIF}
|
|
{%TEMPLATE LinkedList 'Object' 'TObject'}
|
|
|
|
{%DEFINE OpenArrayAsLinkedList}
|
|
function As%1%Linked%2%List(const V: Array of %3%): T%1%Linked%2%;{%ENDDEF}
|
|
{%TEMPLATE OpenArrayAsLinkedList 'Doubly' 'Integer' 'Integer' }
|
|
{%TEMPLATE OpenArrayAsLinkedList 'Doubly' 'Extended' 'Extended' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE OpenArrayAsLinkedList 'Doubly' 'String' 'AnsiString'}
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ TDoublyLinkedList }
|
|
{ }
|
|
type
|
|
TDoublyLinkedList = class
|
|
protected
|
|
FFirst : TDoublyLinkedItem;
|
|
FLast : TDoublyLinkedItem;
|
|
FCount : Integer;
|
|
|
|
public
|
|
destructor Destroy; override;
|
|
|
|
property First: TDoublyLinkedItem read FFirst;
|
|
property Last: TDoublyLinkedItem read FLast;
|
|
function IsEmpty: Boolean;
|
|
property Count: Integer read FCount;
|
|
|
|
procedure Remove(const Item: TDoublyLinkedItem);
|
|
function RemoveFirst: TDoublyLinkedItem;
|
|
function RemoveLast: TDoublyLinkedItem;
|
|
|
|
procedure Delete(const Item: TDoublyLinkedItem);
|
|
procedure DeleteFirst;
|
|
procedure DeleteLast;
|
|
procedure DeleteList;
|
|
|
|
procedure Append(const Item: TDoublyLinkedItem);
|
|
procedure InsertFront(const Item: TDoublyLinkedItem);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Self testing code }
|
|
{ }
|
|
{$IFDEF DEBUG}
|
|
{$IFDEF TEST}
|
|
procedure Test;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{ Fundamentals }
|
|
flcDynArrays,
|
|
flcUTF,
|
|
flcStrings;
|
|
|
|
|
|
|
|
{ }
|
|
{ AType }
|
|
{ }
|
|
constructor AType.Create;
|
|
begin
|
|
inherited Create;
|
|
Init;
|
|
end;
|
|
|
|
procedure AType.Init;
|
|
begin
|
|
end;
|
|
|
|
procedure AType.RaiseTypeError(const Msg: String; const ErrorClass: ExceptClass);
|
|
begin
|
|
if Assigned(ErrorClass) then
|
|
raise ErrorClass.Create(Msg)
|
|
else
|
|
raise EType.Create(Msg);
|
|
end;
|
|
|
|
class function AType.CreateInstance: AType;
|
|
begin
|
|
Result := AType(TypeClass(self).Create);
|
|
end;
|
|
|
|
procedure AType.Clear;
|
|
begin
|
|
raise EType.CreateFmt('Method %s.Clear not implemented', [ClassName]);
|
|
end;
|
|
|
|
function AType.IsEmpty: Boolean;
|
|
begin
|
|
raise EType.CreateFmt('Method %s.IsEmpty not implemented', [ClassName]);
|
|
end;
|
|
|
|
function AType.Duplicate: TObject;
|
|
begin
|
|
try
|
|
Result := CreateInstance;
|
|
try
|
|
AType(Result).Assign(self);
|
|
except
|
|
Result.Free;
|
|
raise;
|
|
end;
|
|
except
|
|
on E : Exception do
|
|
raise EType.CreateFmt('%s cannot duplicate: %s', [ClassName, E.Message]);
|
|
end;
|
|
end;
|
|
|
|
procedure AType.Assign(const Source: TObject);
|
|
var R : Boolean;
|
|
begin
|
|
if Source is AType then
|
|
try
|
|
AType(Source).AssignTo(self);
|
|
R := True;
|
|
except
|
|
R := False;
|
|
end
|
|
else
|
|
R := False;
|
|
if not R then
|
|
raise EType.CreateFmt('%s cannot assign from %s', [ClassName, ObjectClassName(Source)]);
|
|
end;
|
|
|
|
procedure AType.AssignTo(const Dest: TObject);
|
|
begin
|
|
raise EType.CreateFmt('%s cannot assign to %s', [ClassName, ObjectClassName(Dest)]);
|
|
end;
|
|
|
|
function AType.IsEqual(const V: TObject): Boolean;
|
|
begin
|
|
raise EType.CreateFmt('%s cannot compare with %s', [ClassName, ObjectClassName(V)]);
|
|
end;
|
|
|
|
function AType.Compare(const V: TObject): TCompareResult;
|
|
begin
|
|
raise EType.CreateFmt('%s cannot compare with %s', [ClassName, ObjectClassName(V)]);
|
|
end;
|
|
|
|
function AType.HashValue: Word32;
|
|
begin
|
|
try
|
|
Result := HashStr(GetAsString, 1, -1, True);
|
|
except
|
|
on E : Exception do
|
|
raise EType.CreateFmt('Hash error: %s', [E.Message]);
|
|
end;
|
|
end;
|
|
|
|
function AType.GetAsString: String;
|
|
begin
|
|
raise EType.CreateFmt('Method %s.GetAsString not implemented', [ClassName]);
|
|
end;
|
|
|
|
function AType.GetAsUTF8String: RawByteString;
|
|
begin
|
|
{$IFDEF StringIsUnicode}
|
|
Result := StringToUTF8String(GetAsString);
|
|
{$ELSE}
|
|
Result := GetAsString;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function AType.GetAsUnicodeString: UnicodeString;
|
|
begin
|
|
{$IFDEF StringIsUnicode}
|
|
Result := GetAsString;
|
|
{$ELSE}
|
|
Result := UTF8Decode(GetAsString);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure AType.SetAsUTF8String(const S: RawByteString);
|
|
begin
|
|
raise EType.CreateFmt('Method %s.SetAsUTF8String not implemented', [ClassName]);
|
|
end;
|
|
|
|
procedure AType.SetAsUnicodeString(const S: UnicodeString);
|
|
begin
|
|
raise EType.CreateFmt('Method %s.SetAsUnicodeString not implemented', [ClassName]);
|
|
end;
|
|
|
|
procedure AType.SetAsString(const S: String);
|
|
begin
|
|
raise EType.CreateFmt('Method %s.SetAsString not implemented', [ClassName]);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ AType helper functions }
|
|
{ }
|
|
function TypeGetAsString(const V: TObject): String;
|
|
begin
|
|
if V is AType then
|
|
Result := AType(V).GetAsString
|
|
else
|
|
raise EType.CreateFmt('%s cannot convert to string', [ObjectClassName(V)]);
|
|
end;
|
|
|
|
procedure TypeSetAsString(const V: TObject; const S: String);
|
|
begin
|
|
if V is AType then
|
|
AType(V).SetAsString(S)
|
|
else
|
|
raise EType.CreateFmt('%s cannot set as string', [ObjectClassName(V)]);
|
|
end;
|
|
|
|
function TypeGetAsUTF8String(const V: TObject): RawByteString;
|
|
begin
|
|
if V is AType then
|
|
Result := AType(V).GetAsUTF8String
|
|
else
|
|
raise EType.CreateFmt('%s cannot convert to utf-8 string', [ObjectClassName(V)]);
|
|
end;
|
|
|
|
procedure TypeSetAsUTF8String(const V: TObject; const S: RawByteString);
|
|
begin
|
|
if V is AType then
|
|
AType(V).SetAsUTF8String(S)
|
|
else
|
|
raise EType.CreateFmt('%s cannot set as utf-8 string', [ObjectClassName(V)]);
|
|
end;
|
|
|
|
function TypeGetAsUnicodeString(const V: TObject): UnicodeString;
|
|
begin
|
|
if V is AType then
|
|
Result := AType(V).GetAsUnicodeString
|
|
else
|
|
raise EType.CreateFmt('%s cannot convert to utf-16 string', [ObjectClassName(V)]);
|
|
end;
|
|
|
|
procedure TypeSetAsUnicodeString(const V: TObject; const S: UnicodeString);
|
|
begin
|
|
if V is AType then
|
|
AType(V).SetAsUnicodeString(S)
|
|
else
|
|
raise EType.CreateFmt('%s cannot set as utf-16 string', [ObjectClassName(V)]);
|
|
end;
|
|
|
|
function TypeDuplicate(const V: TObject): TObject;
|
|
begin
|
|
if V is AType then
|
|
Result := AType(V).Duplicate else
|
|
if not Assigned(V) then
|
|
Result := nil
|
|
else
|
|
raise EType.CreateFmt('%s cannot duplicate', [ObjectClassName(V)]);
|
|
end;
|
|
|
|
procedure TypeClear(const V: TObject);
|
|
begin
|
|
if V is AType then
|
|
AType(V).Clear else
|
|
if Assigned(V) then
|
|
raise EType.CreateFmt('%s cannot clear', [ObjectClassName(V)]);
|
|
end;
|
|
|
|
function TypeIsEqual(const A, B: TObject): Boolean;
|
|
begin
|
|
if A = B then
|
|
Result := True else
|
|
if not Assigned(A) or not Assigned(B) then
|
|
Result := False else
|
|
if A is AType then
|
|
Result := AType(A).IsEqual(B) else
|
|
if B is AType then
|
|
Result := AType(B).IsEqual(A)
|
|
else
|
|
raise EType.CreateFmt('%s and %s cannot compare',
|
|
[ObjectClassName(A), ObjectClassName(B)]);
|
|
end;
|
|
|
|
function TypeCompare(const A, B: TObject): TCompareResult;
|
|
begin
|
|
if A = B then
|
|
Result := crEqual else
|
|
if A is AType then
|
|
Result := AType(A).Compare(B) else
|
|
if B is AType then
|
|
Result := InverseCompareResult(AType(B).Compare(A))
|
|
else
|
|
Result := crUndefined;
|
|
end;
|
|
|
|
procedure TypeAssign(const A, B: TObject);
|
|
begin
|
|
if A = B then
|
|
exit else
|
|
if A is AType then
|
|
AType(A).Assign(B) else
|
|
if B is AType then
|
|
AType(B).AssignTo(A)
|
|
else
|
|
raise EType.CreateFmt('%s cannot assign %s',
|
|
[ObjectClassName(A), ObjectClassName(B)]);
|
|
end;
|
|
|
|
function TypeHashValue(const A: TObject): Word32;
|
|
begin
|
|
if not Assigned(A) then
|
|
Result := 0 else
|
|
if A is AType then
|
|
Result := AType(A).HashValue
|
|
else
|
|
raise EType.CreateFmt('%s cannot calculate hash value', [A.ClassName]);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ }
|
|
{ TYPE BASE CLASSES }
|
|
{ }
|
|
{ }
|
|
|
|
|
|
|
|
{ }
|
|
{ AArray }
|
|
{ }
|
|
procedure AArray.RaiseIndexError(const Idx: Integer);
|
|
begin
|
|
raise EArray.Create(
|
|
'Array index out of bounds'
|
|
{$IFDEF DEBUG} + ': ' + IntToStr(Idx) + '/' + IntToStr(GetCount){$ENDIF}
|
|
);
|
|
end;
|
|
|
|
function AArray.GetItemAsString(const Idx: Integer): String;
|
|
begin
|
|
raise EArray.CreateFmt('%s.GetItemAsString not implemented', [ClassName]);
|
|
end;
|
|
|
|
procedure AArray.SetItemAsString(const Idx: Integer; const Value: String);
|
|
begin
|
|
raise EArray.CreateFmt('%s.SetItemAsString not implemented', [ClassName]);
|
|
end;
|
|
|
|
procedure AArray.Clear;
|
|
begin
|
|
Count := 0;
|
|
end;
|
|
|
|
procedure AArray.Sort;
|
|
|
|
procedure QuickSort(L, R: Integer);
|
|
var I, J : Integer;
|
|
M : Integer;
|
|
begin
|
|
repeat
|
|
I := L;
|
|
J := R;
|
|
M := (L + R) shr 1;
|
|
repeat
|
|
while CompareItems(I, M) = crLess do
|
|
Inc(I);
|
|
while CompareItems(J, M) = crGreater do
|
|
Dec(J);
|
|
if I <= J then
|
|
begin
|
|
ExchangeItems(I, J);
|
|
if M = I then
|
|
M := J else
|
|
if M = J then
|
|
M := I;
|
|
Inc(I);
|
|
Dec(J);
|
|
end;
|
|
until I > J;
|
|
if L < J then
|
|
QuickSort(L, J);
|
|
L := I;
|
|
until I >= R;
|
|
end;
|
|
|
|
var I : Integer;
|
|
begin
|
|
I := Count;
|
|
if I > 0 then
|
|
QuickSort(0, I - 1);
|
|
end;
|
|
|
|
procedure AArray.ReverseOrder;
|
|
var I, L : Integer;
|
|
begin
|
|
L := Count;
|
|
for I := 1 to L div 2 do
|
|
ExchangeItems(I - 1, L - I);
|
|
end;
|
|
|
|
function AArray.GetAsString: String;
|
|
var I, L : Integer;
|
|
begin
|
|
L := Count;
|
|
if L = 0 then
|
|
begin
|
|
Result := '';
|
|
exit;
|
|
end;
|
|
Result := GetItemAsString(0);
|
|
for I := 1 to L - 1 do
|
|
Result := Result + ',' + GetItemAsString(I);
|
|
end;
|
|
|
|
procedure AArray.SetAsString(const S: String);
|
|
var F, G, L, C : Integer;
|
|
begin
|
|
L := Length(S);
|
|
if L = 0 then
|
|
begin
|
|
Count := 0;
|
|
exit;
|
|
end;
|
|
L := 0;
|
|
F := 1;
|
|
C := Length(S);
|
|
while F < C do
|
|
begin
|
|
G := 0;
|
|
while (F + G <= C) and (S[F + G] <> ',') do
|
|
Inc(G);
|
|
Inc(L);
|
|
Count := L;
|
|
SetItemAsString(L - 1, Copy(S, F, G));
|
|
Inc(F, G + 1);
|
|
end;
|
|
end;
|
|
|
|
procedure AArray.RemoveDuplicates(const IsSortedAscending: Boolean);
|
|
var I, C, J, L : Integer;
|
|
begin
|
|
L := GetCount;
|
|
if L = 0 then
|
|
exit;
|
|
if IsSortedAscending then
|
|
begin
|
|
J := 0;
|
|
repeat
|
|
I := J + 1;
|
|
while (I < L) and (CompareItems(I, J) = crEqual) do
|
|
Inc(I);
|
|
C := I - J;
|
|
if C > 1 then
|
|
begin
|
|
Delete(J + 1, C - 1);
|
|
Dec(L, C - 1);
|
|
Inc(J);
|
|
end
|
|
else
|
|
J := I;
|
|
until J >= L;
|
|
end else
|
|
begin
|
|
J := 0;
|
|
while J < L - 1 do
|
|
begin
|
|
I := J + 1;
|
|
while I <= L - 1 do
|
|
if CompareItems(J, I) = crEqual then
|
|
begin
|
|
Delete(I, 1);
|
|
Dec(L);
|
|
end else
|
|
Inc(I);
|
|
Inc(J);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
{%DEFINE ATypeArrayImpl}
|
|
|
|
{ }
|
|
{-A%1%Array }
|
|
{ }
|
|
procedure A%1%Array.ExchangeItems(const Idx1, Idx2: Integer);
|
|
var I : %2%;
|
|
begin
|
|
I := Item[Idx1];
|
|
Item[Idx1] := Item[Idx2];
|
|
Item[Idx2] := I;
|
|
end;
|
|
|
|
function A%1%Array.AppendItem(const Value: %2%): Integer;
|
|
begin
|
|
Result := Count;
|
|
Count := Result + 1;
|
|
Item[Result] := Value;
|
|
end;
|
|
|
|
function A%1%Array.GetRange(const LoIdx, HiIdx: Integer): %1%Array;
|
|
var I, L, H, C : Integer;
|
|
begin
|
|
L := MaxInt(0, LoIdx);
|
|
H := MinInt(Count - 1, HiIdx);
|
|
C := H - L + 1;
|
|
SetLength(Result, C);
|
|
for I := 0 to C - 1 do
|
|
Result[I] := Item[L + I];
|
|
end;
|
|
|
|
function A%1%Array.DuplicateRange(const LoIdx, HiIdx: Integer): AArray;
|
|
var I, L, H, C : Integer;
|
|
begin
|
|
Result := A%1%Array(CreateInstance);
|
|
L := MaxInt(0, LoIdx);
|
|
H := MinInt(Count - 1, HiIdx);
|
|
C := H - L + 1;
|
|
A%1%Array(Result).Count := C;
|
|
for I := 0 to C - 1 do
|
|
A%1%Array(Result)[I] := Item[L + I];
|
|
end;
|
|
|
|
procedure A%1%Array.SetRange(const LoIdx, HiIdx: Integer; const V: %1%Array);
|
|
var I, L, H, C : Integer;
|
|
begin
|
|
L := MaxInt(0, LoIdx);
|
|
H := MinInt(Count - 1, HiIdx);
|
|
C := MinInt(Length(V), H - L + 1);
|
|
for I := 0 to C - 1 do
|
|
Item[L + I] := V[I];
|
|
end;
|
|
|
|
procedure A%1%Array.Fill(const Idx, ACount: Integer; const Value: %2%);
|
|
var I : Integer;
|
|
begin
|
|
for I := Idx to Idx + ACount - 1 do
|
|
Item[I] := Value;
|
|
end;
|
|
|
|
function A%1%Array.AppendArray(const V: %1%Array): Integer;
|
|
begin
|
|
Result := Count;
|
|
Count := Result + Length(V);
|
|
Range[Result, Count - 1] := V;
|
|
end;
|
|
|
|
function A%1%Array.CompareItems(const Idx1, Idx2: Integer): TCompareResult;
|
|
var I, J : %2%;
|
|
begin
|
|
I := Item[Idx1];
|
|
J := Item[Idx2];
|
|
if %7%I{%IF 7}){%ENDIF} < %7%J{%IF 7}){%ENDIF} then
|
|
Result := crLess else
|
|
if %7%I{%IF 7}){%ENDIF} > %7%J{%IF 7}){%ENDIF} then
|
|
Result := crGreater else
|
|
Result := crEqual;
|
|
end;
|
|
|
|
function A%1%Array.PosNext(const Find: %2%;
|
|
const PrevPos: Integer; const IsSortedAscending: Boolean): Integer;
|
|
var I, L, H : Integer;
|
|
D : %2%;
|
|
begin
|
|
if IsSortedAscending then // binary search
|
|
begin
|
|
if MaxInt(PrevPos + 1, 0) = 0 then // find first
|
|
begin
|
|
L := 0;
|
|
H := Count - 1;
|
|
repeat
|
|
I := (L + H) div 2;
|
|
D := Item[I];
|
|
if D = Find then
|
|
begin
|
|
while (I > 0) and (Item[I - 1] = Find) do
|
|
Dec(I);
|
|
Result := I;
|
|
exit;
|
|
end else
|
|
if %7%D{%IF 7}){%ENDIF} > %7%Find{%IF 7}){%ENDIF} then
|
|
H := I - 1 else
|
|
L := I + 1;
|
|
until L > H;
|
|
Result := -1;
|
|
end else // find next
|
|
if PrevPos >= Count - 1 then
|
|
Result := -1 else
|
|
if Item[PrevPos + 1] = Find then
|
|
Result := PrevPos + 1 else
|
|
Result := -1;
|
|
end else // linear search
|
|
begin
|
|
for I := MaxInt(PrevPos + 1, 0) to Count - 1 do
|
|
if Item[I] = Find then
|
|
begin
|
|
Result := I;
|
|
exit;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
{%IF 8}function A%1%Array.GetItemAsString(const Idx: Integer): String;
|
|
begin
|
|
Result := %8%(GetItem(Idx));
|
|
end;
|
|
{%ENDIF}
|
|
{%IF 9}procedure A%1%Array.SetItemAsString(const Idx: Integer; const Value: String);
|
|
begin
|
|
SetItem(Idx, %9%(Value));
|
|
end;
|
|
{%ENDIF}
|
|
procedure A%1%Array.Assign(const Source: TObject);
|
|
var I, L : Integer;
|
|
begin
|
|
if Source is A%1%Array then
|
|
begin
|
|
L := A%1%Array(Source).Count;
|
|
Count := L;
|
|
for I := 0 to L - 1 do
|
|
Item[I] := A%1%Array(Source).Item[I];
|
|
end else{%IF 6}
|
|
if Source is A%6%Array then
|
|
begin
|
|
L := A%6%Array(Source).Count;
|
|
Count := L;
|
|
for I := 0 to L - 1 do
|
|
Item[I] := A%6%Array(Source).Item[I];
|
|
end else{%ENDIF}
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
function A%1%Array.IsEqual(const V: TObject): Boolean;
|
|
var I, L : Integer;
|
|
begin
|
|
if V is A%1%Array then
|
|
begin
|
|
L := A%1%Array(V).Count;
|
|
Result := L = Count;
|
|
if not Result then
|
|
exit;
|
|
for I := 0 to L - 1 do
|
|
if Item[I] <> A%1%Array(V).Item[I] then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
end else
|
|
Result := inherited IsEqual(V);
|
|
end;
|
|
|
|
function A%1%Array.AppendArray(const V: AArray): Integer;
|
|
var I, L : Integer;
|
|
begin
|
|
Result := Count;
|
|
if V is A%1%Array then
|
|
begin
|
|
L := V.Count;
|
|
Count := Result + L;
|
|
for I := 0 to L - 1 do
|
|
Item[Result + I] := A%1%Array(V)[I];
|
|
end
|
|
else
|
|
raise E%1%Array.CreateFmt('%s can not append %s', [ClassName, ObjectClassName(V)]);
|
|
end;
|
|
|
|
procedure A%1%Array.Delete(const Idx: Integer; const ACount: Integer);
|
|
var I, C, J, L : Integer;
|
|
begin
|
|
J := MaxInt(Idx, 0);
|
|
C := GetCount;
|
|
L := MinInt(ACount, C - J);
|
|
if L > 0 then
|
|
begin
|
|
for I := J to J + C - 1 do
|
|
SetItem(I, GetItem(I + ACount));
|
|
SetCount(C - L);
|
|
end;
|
|
end;
|
|
|
|
procedure A%1%Array.Insert(const Idx: Integer; const ACount: Integer);
|
|
var I, C, J, L : Integer;
|
|
begin
|
|
if ACount <= 0 then
|
|
exit;
|
|
C := GetCount;
|
|
SetCount(C + ACount);
|
|
J := MinInt(MaxInt(Idx, 0), C);
|
|
L := C - J;
|
|
for I := C - 1 downto C - L do
|
|
SetItem(I + ACount, GetItem(I));
|
|
end;
|
|
{%ENDDEF}
|
|
{%TEMPLATE ATypeArrayImpl 'Int32' 'Int32' '' '' '0' '' '' 'IntToStr' 'StrToInt' }
|
|
{%TEMPLATE ATypeArrayImpl 'Int64' 'Int64' '' '' '0' 'LongInt' '' 'IntToStr' 'StrToInt64' }
|
|
{%TEMPLATE ATypeArrayImpl 'LongInt' 'LongInt' '' '' '0' '' '' 'IntToStr' 'StrToInt' }
|
|
{%TEMPLATE ATypeArrayImpl 'Word32' 'Word32' '' '' '0' '' '' 'IntToStr' 'StrToInt64' }
|
|
{%TEMPLATE ATypeArrayImpl 'Word64' 'Word64' '' '' '0' '' '' 'UIntToString' 'StringToUInt64' }
|
|
{%TEMPLATE ATypeArrayImpl 'LongWord' 'LongWord' '' '' '0' '' '' 'IntToStr' 'StrToInt64' }
|
|
{%TEMPLATE ATypeArrayImpl 'Single' 'Single' '' '' '0.0' 'Int64' '' 'FloatToStr' 'StrToFloat' }
|
|
{%TEMPLATE ATypeArrayImpl 'Double' 'Double' '' '' '0.0' 'Int64' '' 'FloatToStr' 'StrToFloat' }
|
|
{%TEMPLATE ATypeArrayImpl 'Extended' 'Extended' '' '' '0.0' 'Int64' '' 'FloatToStr' 'StrToFloat' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeArrayImpl 'AnsiString' 'AnsiString' '' '' '''''' '' '' 'ToStringA' 'ToAnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeArrayImpl 'RawByteString' 'RawByteString' '' '' '''''' '' '' 'ToStringB' 'ToRawByteString' }
|
|
{%TEMPLATE ATypeArrayImpl 'UnicodeString' 'UnicodeString' '' '' '''''' '' '' 'ToStringU' 'ToUnicodeString' }
|
|
{%TEMPLATE ATypeArrayImpl 'String' 'String' '' '' '''''' '' '' '' '' }
|
|
{%TEMPLATE ATypeArrayImpl 'Pointer' 'Pointer' '' '' 'nil' '' 'NativeInt(' 'PointerToStr' 'StrToPointer' }
|
|
{%TEMPLATE ATypeArrayImpl 'Interface' 'IInterface' '' '' 'nil' '' 'NativeInt(' '' '' }
|
|
|
|
{ }
|
|
{ AObjectArray }
|
|
{ }
|
|
procedure AObjectArray.Clear;
|
|
begin
|
|
if IsItemOwner then
|
|
FreeItems
|
|
else
|
|
ReleaseItems;
|
|
end;
|
|
|
|
procedure AObjectArray.Assign(const Source: TObject);
|
|
var I, L : Integer;
|
|
V : TObject;
|
|
begin
|
|
if Source is AObjectArray then
|
|
begin
|
|
FreeItems;
|
|
IsItemOwner := AObjectArray(Source).IsItemOwner;
|
|
L := AObjectArray(Source).Count;
|
|
Count := L;
|
|
if GetIsItemOwner then
|
|
for I := 0 to L - 1 do
|
|
begin
|
|
V := AObjectArray(Source)[I];
|
|
if V is AArray then
|
|
Item[I] := AArray(V).Duplicate else
|
|
Item[I] := V;
|
|
end
|
|
else
|
|
for I := 0 to L - 1 do
|
|
Item[I] := AObjectArray(Source)[I];
|
|
end else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
function AObjectArray.IsEqual(const V: TObject): Boolean;
|
|
var I, L : Integer;
|
|
A, B : TObject;
|
|
begin
|
|
if V is AObjectArray then
|
|
begin
|
|
L := AArray(V).Count;
|
|
if Count <> L then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
for I := 0 to L - 1 do
|
|
begin
|
|
A := Item[I];
|
|
B := AObjectArray(V)[I];
|
|
Result := A = B;
|
|
if not Result then
|
|
exit;
|
|
end;
|
|
Result := True;
|
|
end else
|
|
Result := inherited IsEqual(V);
|
|
end;
|
|
|
|
function AObjectArray.Compare(const V: TObject): TCompareResult;
|
|
var I, C1, C2 : Integer;
|
|
A, B : TObject;
|
|
begin
|
|
if V is AObjectArray then
|
|
begin
|
|
C1 := GetCount;
|
|
C2 := AObjectArray(V).GetCount;
|
|
if C1 < C2 then
|
|
Result := crLess else
|
|
if C1 > C2 then
|
|
Result := crGreater else
|
|
begin
|
|
Result := crEqual;
|
|
for I := 0 to GetCount - 1 do
|
|
begin
|
|
A := Item[I];
|
|
B := AObjectArray(V)[I];
|
|
if A <> B then
|
|
begin
|
|
Result := crUndefined;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end else
|
|
Result := inherited Compare(V);
|
|
end;
|
|
|
|
function AObjectArray.GetRange(const LoIdx, HiIdx: Integer): ObjectArray;
|
|
var I, L, H, C : Integer;
|
|
begin
|
|
L := MaxInt(0, LoIdx);
|
|
H := MinInt(Count - 1, HiIdx);
|
|
C := H - L + 1;
|
|
SetLength(Result, C);
|
|
for I := 0 to C - 1 do
|
|
Result[L + I] := Item[I];
|
|
end;
|
|
|
|
procedure AObjectArray.SetRange(const LoIdx, HiIdx: Integer; const V: ObjectArray);
|
|
var I, L, H, C : Integer;
|
|
begin
|
|
L := MaxInt(0, LoIdx);
|
|
H := MinInt(Count - 1, HiIdx);
|
|
C := MinInt(Length(V), H - L + 1);
|
|
for I := 0 to C - 1 do
|
|
Item[L + I] := V[I];
|
|
end;
|
|
|
|
function AObjectArray.GetAsString: String;
|
|
var I, L : Integer;
|
|
V : TObject;
|
|
begin
|
|
Result := '';
|
|
L := Count;
|
|
for I := 0 to L - 1 do
|
|
begin
|
|
V := Item[I];
|
|
Result := Result + PointerToStr(Pointer(V));
|
|
if I < L - 1 then
|
|
Result := Result + ',';
|
|
end;
|
|
end;
|
|
|
|
procedure AObjectArray.ExchangeItems(const Idx1, Idx2: Integer);
|
|
var I : TObject;
|
|
begin
|
|
I := Item[Idx1];
|
|
Item[Idx1] := Item[Idx2];
|
|
Item[Idx2] := I;
|
|
end;
|
|
|
|
function AObjectArray.AppendItem(const Value: TObject): Integer;
|
|
begin
|
|
Result := Count;
|
|
Count := Result + 1;
|
|
Item[Result] := Value;
|
|
end;
|
|
|
|
function AObjectArray.AppendArray(const V: ObjectArray): Integer;
|
|
begin
|
|
Result := Count;
|
|
Count := Result + Length(V);
|
|
Range[Result, Count - 1] := V;
|
|
end;
|
|
|
|
{$WARNINGS OFF}
|
|
function AObjectArray.AppendArray(const V: AArray): Integer;
|
|
var I, L : Integer;
|
|
begin
|
|
if V is AObjectArray then
|
|
begin
|
|
Result := Count;
|
|
L := V.Count;
|
|
Count := Result + L;
|
|
for I := 0 to L - 1 do
|
|
Item[Result + I] := AObjectArray(V)[I];
|
|
end
|
|
else
|
|
raise EObjectArray.CreateFmt('%s can not append %s', [ClassName, ObjectClassName(V)]);
|
|
end;
|
|
{$IFDEF DEBUG}{$IFNDEF FREEPASCAL}{$WARNINGS ON}{$ENDIF}{$ENDIF}
|
|
|
|
procedure AObjectArray.Delete(const Idx: Integer; const ACount: Integer);
|
|
var I, C, J, L : Integer;
|
|
begin
|
|
J := MaxInt(Idx, 0);
|
|
C := GetCount;
|
|
L := MinInt(ACount, C - J);
|
|
if L > 0 then
|
|
begin
|
|
for I := J to J + C - 1 do
|
|
SetItem(Idx + I, GetItem(Idx + ACount + I));
|
|
SetCount(C - L);
|
|
end;
|
|
end;
|
|
|
|
function AObjectArray.PosNext(const Find: TObject; const PrevPos: Integer): Integer;
|
|
var I : Integer;
|
|
begin
|
|
for I := MaxInt(PrevPos + 1, 0) to Count - 1 do
|
|
if Find = Item[I] then
|
|
begin
|
|
Result := I;
|
|
exit;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
function AObjectArray.PosNext(var AItem: TObject; const AClassType: TClass;
|
|
const PrevPos: Integer): Integer;
|
|
var I : Integer;
|
|
begin
|
|
for I := MaxInt(PrevPos + 1, 0) to Count - 1 do
|
|
begin
|
|
AItem := GetItem(I);
|
|
if AItem.InheritsFrom(AClassType) then
|
|
begin
|
|
Result := I;
|
|
exit;
|
|
end;
|
|
end;
|
|
AItem := nil;
|
|
Result := -1;
|
|
end;
|
|
|
|
function AObjectArray.PosNext(var AItem: TObject; const AClassName: String;
|
|
const PrevPos: Integer): Integer;
|
|
var I : Integer;
|
|
begin
|
|
for I := MaxInt(PrevPos + 1, 0) to Count - 1 do
|
|
begin
|
|
AItem := GetItem(I);
|
|
if Assigned(AItem) and AItem.ClassNameIs(AClassName) then
|
|
begin
|
|
Result := I;
|
|
exit;
|
|
end;
|
|
end;
|
|
AItem := nil;
|
|
Result := -1;
|
|
end;
|
|
|
|
function AObjectArray.Find(const AClassType: TClass; const ACount: Integer): TObject;
|
|
var I, J : Integer;
|
|
begin
|
|
Result := nil;
|
|
I := -1;
|
|
for J := 1 to ACount do
|
|
begin
|
|
I := PosNext(Result, AClassType, I);
|
|
if I = -1 then
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function AObjectArray.Find(const AClassName: String; const ACount: Integer): TObject;
|
|
var I, J : Integer;
|
|
begin
|
|
Result := nil;
|
|
I := -1;
|
|
for J := 1 to ACount do
|
|
begin
|
|
I := PosNext(Result, AClassName, I);
|
|
if I = -1 then
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function AObjectArray.FindAll(const AClassType: TClass): ObjectArray;
|
|
var I : Integer;
|
|
V : TObject;
|
|
begin
|
|
SetLength(Result, 0);
|
|
I := PosNext(V, AClassType);
|
|
while I >= 0 do
|
|
begin
|
|
DynArrayAppend(Result, V);
|
|
I := PosNext(V, AClassType, I);
|
|
end;
|
|
end;
|
|
|
|
function AObjectArray.FindAll(const AClassName: String): ObjectArray;
|
|
var I : Integer;
|
|
V : TObject;
|
|
begin
|
|
SetLength(Result, 0);
|
|
I := PosNext(V, AClassName);
|
|
while I >= 0 do
|
|
begin
|
|
DynArrayAppend(Result, V);
|
|
I := PosNext(V, AClassName, I);
|
|
end;
|
|
end;
|
|
|
|
function AObjectArray.CountItems(const AClassType: TClass): Integer;
|
|
var I : Integer;
|
|
V : TObject;
|
|
begin
|
|
Result := 0;
|
|
I := PosNext(V, AClassType);
|
|
while I >= 0 do
|
|
begin
|
|
Inc(Result);
|
|
I := PosNext(V, AClassType, I);
|
|
end;
|
|
end;
|
|
|
|
function AObjectArray.CountItems(const AClassName: String): Integer;
|
|
var I : Integer;
|
|
V : TObject;
|
|
begin
|
|
Result := 0;
|
|
I := PosNext(V, AClassName);
|
|
while I >= 0 do
|
|
begin
|
|
Inc(Result);
|
|
I := PosNext(V, AClassName, I);
|
|
end;
|
|
end;
|
|
|
|
function AObjectArray.CompareItems(const Idx1, Idx2: Integer): TCompareResult;
|
|
var A, B : TObject;
|
|
begin
|
|
A := Item[Idx1];
|
|
B := Item[Idx2];
|
|
if A = B then
|
|
Result := crEqual else
|
|
Result := crUndefined;
|
|
end;
|
|
|
|
function AObjectArray.DeleteValue(const Value: TObject): Boolean;
|
|
var I : Integer;
|
|
begin
|
|
I := PosNext(Value, -1);
|
|
Result := I >= 0;
|
|
if Result then
|
|
Delete(I, 1);
|
|
end;
|
|
|
|
function AObjectArray.DeleteAll(const Value: TObject): Integer;
|
|
begin
|
|
Result := 0;
|
|
while DeleteValue(Value) do
|
|
Inc(Result);
|
|
end;
|
|
|
|
function AObjectArray.ReleaseValue(const Value: TObject): Boolean;
|
|
var I : Integer;
|
|
begin
|
|
I := PosNext(Value, -1);
|
|
Result := I >= 0;
|
|
if Result then
|
|
ReleaseItem(I);
|
|
end;
|
|
|
|
function AObjectArray.RemoveItem(const Idx: Integer): TObject;
|
|
begin
|
|
Result := ReleaseItem(Idx);
|
|
Delete(Idx, 1);
|
|
end;
|
|
|
|
function AObjectArray.RemoveValue(const Value: TObject): Boolean;
|
|
var I : Integer;
|
|
begin
|
|
I := PosNext(Value, -1);
|
|
Result := I >= 0;
|
|
if Result then
|
|
RemoveItem(I);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ABitArray }
|
|
{ }
|
|
const
|
|
BitMaskTable32: array[0..31] of Word32 =
|
|
($00000001, $00000002, $00000004, $00000008,
|
|
$00000010, $00000020, $00000040, $00000080,
|
|
$00000100, $00000200, $00000400, $00000800,
|
|
$00001000, $00002000, $00004000, $00008000,
|
|
$00010000, $00020000, $00040000, $00080000,
|
|
$00100000, $00200000, $00400000, $00800000,
|
|
$01000000, $02000000, $04000000, $08000000,
|
|
$10000000, $20000000, $40000000, $80000000);
|
|
|
|
function ABitArray.GetRangeL(const Idx: Integer): Word32;
|
|
var I : Integer;
|
|
begin
|
|
Result := 0;
|
|
for I := 0 to 31 do
|
|
if Bit[Idx + I] then
|
|
Result := Result or BitMaskTable32[I];
|
|
end;
|
|
|
|
procedure ABitArray.SetRangeL(const Idx: Integer; const Value: Word32);
|
|
var
|
|
I : Integer;
|
|
C : Word32;
|
|
begin
|
|
C := 1;
|
|
for I := Idx to Idx + 31 do
|
|
begin
|
|
Bit[I] := Value and C <> 0;
|
|
C := C shl 1;
|
|
end;
|
|
end;
|
|
|
|
procedure ABitArray.Fill(const Idx, ACount: Integer; const Value: Boolean);
|
|
var
|
|
I : Integer;
|
|
begin
|
|
for I := Idx to Idx + ACount - 1 do
|
|
Bit[I] := Value;
|
|
end;
|
|
|
|
function ABitArray.IsRange(const LoIdx, HiIdx: Integer; const Value: Boolean): Boolean;
|
|
var
|
|
I : Integer;
|
|
begin
|
|
for I := LoIdx to HiIdx do
|
|
if Bit[I] <> Value then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure ABitArray.Assign(const Source: TObject);
|
|
var
|
|
I, L : Integer;
|
|
begin
|
|
if Source is ABitArray then
|
|
begin
|
|
L := AArray(Source).Count;
|
|
Count := L;
|
|
for I := 0 to L - 1 do
|
|
Bit[I] := ABitArray(Source)[I];
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
function ABitArray.IsEqual(const V: TObject): Boolean;
|
|
var
|
|
I, L : Integer;
|
|
begin
|
|
if V is ABitArray then
|
|
begin
|
|
L := AArray(V).Count;
|
|
if Count <> L then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
for I := 0 to L - 1 do
|
|
if Bit[I] <> ABitArray(V)[I] then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
Result := True;
|
|
end
|
|
else
|
|
Result := inherited IsEqual(V);
|
|
end;
|
|
|
|
procedure ABitArray.ExchangeItems(const Idx1, Idx2: Integer);
|
|
var
|
|
I : Boolean;
|
|
begin
|
|
I := Bit[Idx1];
|
|
Bit[Idx1] := Bit[Idx2];
|
|
Bit[Idx2] := I;
|
|
end;
|
|
|
|
function ABitArray.AppendItem(const Value: Boolean): Integer;
|
|
begin
|
|
Result := Count;
|
|
Count := Result + 1;
|
|
Bit[Result] := Value;
|
|
end;
|
|
|
|
function ABitArray.CompareItems(const Idx1, Idx2: Integer): TCompareResult;
|
|
begin
|
|
Result := flcUtils.Compare(Bit[Idx1], Bit[Idx2]);
|
|
end;
|
|
|
|
procedure ABitArray.Invert;
|
|
var
|
|
I : Integer;
|
|
begin
|
|
for I := 0 to Count - 1 do
|
|
Bit[I] := not Bit[I];
|
|
end;
|
|
|
|
function ABitArray.Find(const Value: Boolean; const Start: Integer): Integer;
|
|
var
|
|
I, C : Integer;
|
|
begin
|
|
if Start < 0 then
|
|
I := 0
|
|
else
|
|
I := Start;
|
|
C := Count;
|
|
while I < C do
|
|
if Bit[I] = Value then
|
|
begin
|
|
Result := I;
|
|
exit;
|
|
end
|
|
else
|
|
Inc(I);
|
|
Result := -1;
|
|
end;
|
|
|
|
function ABitArray.FindRange(const Value: Boolean; const Start: Integer;
|
|
const ACount: Integer): Integer;
|
|
var
|
|
I, C, F : Integer;
|
|
begin
|
|
if ACount <= 0 then
|
|
begin
|
|
Result := -1;
|
|
exit;
|
|
end;
|
|
if Start < 0 then
|
|
I := 0
|
|
else
|
|
I := Start;
|
|
C := self.Count;
|
|
F := 0;
|
|
while I + F < C do
|
|
if Bit[I + F] = Value then
|
|
begin
|
|
Inc(F);
|
|
if F = ACount then
|
|
begin
|
|
Result := I;
|
|
exit;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Inc(I, F + 1);
|
|
F := 0;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
procedure ABitArray.Delete(const Idx: Integer; const ACount: Integer);
|
|
var
|
|
I, C : Integer;
|
|
begin
|
|
C := GetCount;
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx + ACount > C) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
for I := Idx + ACount to C - 1 do
|
|
SetBit(I - ACount, GetBit(I));
|
|
SetCount(C - ACount);
|
|
end;
|
|
|
|
procedure ABitArray.Insert(const Idx: Integer; const ACount: Integer);
|
|
var
|
|
I, C : Integer;
|
|
begin
|
|
C := GetCount;
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx > C) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
SetCount(C + ACount);
|
|
for I := Idx to C - 1 do
|
|
SetBit(I + ACount, GetBit(I));
|
|
Fill(Idx, Idx + ACount - 1, False);
|
|
end;
|
|
|
|
function ABitArray.DuplicateRange(const LoIdx, HiIdx: Integer): AArray;
|
|
var
|
|
I, C : Integer;
|
|
begin
|
|
C := GetCount;
|
|
{$IFOPT R+}
|
|
if (LoIdx < 0) or (LoIdx > HiIdx) or (HiIdx >= C) then
|
|
RaiseIndexError(HiIdx);
|
|
{$ENDIF}
|
|
Result := ABitArray(CreateInstance);
|
|
C := HiIdx - LoIdx + 1;
|
|
Result.Count := C;
|
|
for I := 0 to C - 1 do
|
|
ABitArray(Result)[I] := GetBit(LoIdx + I);
|
|
end;
|
|
|
|
function ABitArray.AppendArray(const V: AArray): Integer;
|
|
var
|
|
I, C : Integer;
|
|
begin
|
|
if V is ABitArray then
|
|
begin
|
|
Result := Count;
|
|
C := ABitArray(V).Count;
|
|
if C = 0 then
|
|
exit;
|
|
SetCount(Result + C);
|
|
for I := 0 to C - 1 do
|
|
SetBit(Result + I, ABitArray(V).GetBit(I));
|
|
end
|
|
else
|
|
raise EBitArray.CreateFmt('%s can not append %s', [ClassName, ObjectClassName(V)]);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ARRAY IMPLEMENTATIONS }
|
|
{ }
|
|
{ }
|
|
{%DEFINE AArrayDynArrayImpl}
|
|
{ }
|
|
{-T%1%Array }
|
|
{ }
|
|
function T%1%Array.GetItem(const Idx: Integer): %3%;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
Result := FData[Idx];
|
|
end;
|
|
|
|
procedure T%1%Array.SetItem(const Idx: Integer; const Value: %3%);
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
FData[Idx] := Value;
|
|
end;
|
|
|
|
procedure T%1%Array.ExchangeItems(const Idx1, Idx2: Integer);
|
|
var I : %3%;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx1 < 0) or (Idx1 >= FCount) then
|
|
RaiseIndexError(Idx1);
|
|
if (Idx2 < 0) or (Idx2 >= FCount) then
|
|
RaiseIndexError(Idx2);
|
|
{$ENDIF}
|
|
I := FData[Idx1];
|
|
FData[Idx1] := FData[Idx2];
|
|
FData[Idx2] := I;
|
|
end;
|
|
|
|
function T%1%Array.GetCount: Integer;
|
|
begin
|
|
Result := FCount;
|
|
end;
|
|
|
|
{ Memory allocation strategy to reduce memory copies: }
|
|
{ * For first allocation: allocate the exact size. }
|
|
{ * For change to < 16: allocate 16 entries. }
|
|
{ * For growing to >= 16: pre-allocate 1/8th of NewCount. }
|
|
{ * For shrinking blocks: shrink actual allocation when Count is less }
|
|
{ than half of the allocated size. }
|
|
procedure T%1%Array.SetCount(const NewCount: Integer);
|
|
var L, N : Integer;
|
|
begin
|
|
N := NewCount;
|
|
if FCount = N then
|
|
exit;
|
|
FCount := N;
|
|
L := FCapacity;
|
|
if L > 0 then
|
|
if N < 16 then // pre-allocate first 16 entries
|
|
N := 16 else
|
|
if N > L then
|
|
N := N + N shr 3 else // pre-allocate 1/8th extra if growing
|
|
if N > L shr 1 then // only reduce capacity if size is at least half
|
|
exit;
|
|
if N <> L then
|
|
begin
|
|
SetLength{%IF 2}AndZero{%ENDIF}(FData, N);
|
|
FCapacity := N;
|
|
end;
|
|
end;
|
|
|
|
function T%1%Array.AppendItem(const Value: %3%): Integer;
|
|
begin
|
|
Result := FCount;
|
|
if Result >= FCapacity then
|
|
SetCount(Result + 1)
|
|
else
|
|
FCount := Result + 1;
|
|
FData[Result] := Value;
|
|
end;
|
|
|
|
procedure T%1%Array.Delete(const Idx: Integer; const ACount: Integer = 1);
|
|
var N : Integer;
|
|
begin
|
|
N := DynArrayRemove%4%(FData, Idx, ACount);
|
|
Dec(FCapacity, N);
|
|
Dec(FCount, N);
|
|
end;
|
|
|
|
procedure T%1%Array.Insert(const Idx: Integer; const ACount: Integer = 1);
|
|
var I : Integer;
|
|
begin
|
|
I := DynArrayInsert%4%(FData, Idx, ACount);
|
|
if I >= 0 then
|
|
begin
|
|
Inc(FCapacity, ACount);
|
|
Inc(FCount, ACount);
|
|
end;
|
|
end;
|
|
|
|
function T%1%Array.GetRange(const LoIdx, HiIdx: Integer): %1%Array;
|
|
var L, H : Integer;
|
|
begin
|
|
L := MaxInt(0, LoIdx);
|
|
H := MinInt(HiIdx, FCount);
|
|
if H >= L then
|
|
Result := Copy(FData, L, H - L + 1) else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure T%1%Array.SetRange(const LoIdx, HiIdx: Integer; const V: %1%Array);
|
|
var L, H, C : Integer;
|
|
begin
|
|
L := MaxInt(0, LoIdx);
|
|
H := MinInt(HiIdx, FCount);
|
|
C := MaxInt(MinInt(Length(V), H - L + 1), 0);
|
|
if C > 0 then
|
|
Move(V[0], FData[L], C * Sizeof(%3%));
|
|
end;
|
|
|
|
constructor T%1%Array.Create(const V: %1%Array);
|
|
begin
|
|
inherited Create;
|
|
SetData(V);
|
|
end;
|
|
|
|
procedure T%1%Array.SetData(const AData: %1%Array);
|
|
begin
|
|
FData := AData;
|
|
FCount := Length(FData);
|
|
FCapacity := FCount;
|
|
end;
|
|
|
|
function T%1%Array.DuplicateRange(const LoIdx, HiIdx: Integer): AArray;
|
|
var L, H, C : Integer;
|
|
begin
|
|
L := MaxInt(0, LoIdx);
|
|
H := MinInt(HiIdx, FCount);
|
|
C := MaxInt(0, H - L + 1);
|
|
Result := CreateInstance as T%1%Array;
|
|
T%1%Array(Result).FCount := C;
|
|
if C > 0 then
|
|
T%1%Array(Result).FData := Copy(FData, L, C);
|
|
end;
|
|
|
|
procedure T%1%Array.Assign(const V: %1%Array);
|
|
begin
|
|
FData := Copy(V);
|
|
FCount := Length(FData);
|
|
FCapacity := FCount;
|
|
end;
|
|
|
|
procedure T%1%Array.Assign(const V: Array of %3%);
|
|
begin
|
|
FData := As%1%Array(V);
|
|
FCount := Length(FData);
|
|
FCapacity := FCount;
|
|
end;
|
|
|
|
procedure T%1%Array.Assign(const Source: TObject);
|
|
begin
|
|
if Source is T%1%Array then
|
|
begin
|
|
FCount := T%1%Array(Source).FCount;
|
|
FData := Copy(T%1%Array(Source).FData, 0, FCount);
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
|
|
{%ENDDEF}
|
|
|
|
{%TEMPLATE AArrayDynArrayImpl 'Int32' 'Z' 'Int32' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'Int64' 'Z' 'Int64' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'LongInt' 'Z' 'LongInt' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'Word32' 'Z' 'Word32' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'Word64' 'Z' 'Word64' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'LongWord' 'Z' 'LongWord' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'Single' 'Z' 'Single' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'Double' 'Z' 'Double' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'Extended' 'Z' 'Extended' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE AArrayDynArrayImpl 'AnsiString' '' 'AnsiString' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE AArrayDynArrayImpl 'RawByteString' '' 'RawByteString' 'B' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'UnicodeString' '' 'UnicodeString' 'U' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'String' '' 'String' '' }
|
|
{%TEMPLATE AArrayDynArrayImpl 'Pointer' 'Z' 'Pointer' '' }
|
|
{ }
|
|
{ TObjectArray }
|
|
{ }
|
|
constructor TObjectArray.Create(const V: ObjectArray; const AIsItemOwner: Boolean);
|
|
begin
|
|
inherited Create;
|
|
FData := V;
|
|
FIsItemOwner := AIsItemOwner;
|
|
FCount := Length(FData);
|
|
FCapacity := FCount;
|
|
end;
|
|
|
|
destructor TObjectArray.Destroy;
|
|
begin
|
|
if FIsItemOwner then
|
|
FreeItems;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TObjectArray.Init;
|
|
begin
|
|
inherited Init;
|
|
FIsItemOwner := False;
|
|
end;
|
|
|
|
procedure TObjectArray.FreeItems;
|
|
begin
|
|
FreeObjectArray(FData);
|
|
FData := nil;
|
|
FCapacity := 0;
|
|
FCount := 0;
|
|
end;
|
|
|
|
procedure TObjectArray.ReleaseItems;
|
|
begin
|
|
FData := nil;
|
|
FCapacity := 0;
|
|
FCount := 0;
|
|
end;
|
|
|
|
function TObjectArray.GetIsItemOwner: Boolean;
|
|
begin
|
|
Result := FIsItemOwner;
|
|
end;
|
|
|
|
procedure TObjectArray.SetIsItemOwner(const AIsItemOwner: Boolean);
|
|
begin
|
|
FIsItemOwner := AIsItemOwner;
|
|
end;
|
|
|
|
function TObjectArray.GetCount: Integer;
|
|
begin
|
|
Result := FCount;
|
|
end;
|
|
|
|
procedure TObjectArray.SetCount(const NewCount: Integer);
|
|
var L, N : Integer;
|
|
begin
|
|
N := NewCount;
|
|
if N = FCount then
|
|
exit;
|
|
if (N < FCount) and FIsItemOwner then
|
|
FreeObjectArray(FData, N, FCount - 1);
|
|
FCount := N;
|
|
L := FCapacity;
|
|
if L > 0 then
|
|
if N < 16 then
|
|
N := 16 else
|
|
if N > L then
|
|
N := N + N shr 3 else
|
|
if N > L shr 1 then
|
|
exit;
|
|
if N <> L then
|
|
begin
|
|
SetLengthAndZero(FData, N);
|
|
FCapacity := N;
|
|
end;
|
|
end;
|
|
|
|
function TObjectArray.GetItem(const Idx: Integer): TObject;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
Result := FData[Idx];
|
|
end;
|
|
|
|
procedure TObjectArray.SetItem(const Idx: Integer; const Value: TObject);
|
|
var P : ^TObject;
|
|
V : TObject;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
P := Pointer(FData);
|
|
Inc(P, Idx);
|
|
if FIsItemOwner then
|
|
begin
|
|
V := P^;
|
|
if V = Value then
|
|
exit;
|
|
V.Free;
|
|
end;
|
|
P^ := Value;
|
|
end;
|
|
|
|
function TObjectArray.AppendItem(const Value: TObject): Integer;
|
|
begin
|
|
Result := FCount;
|
|
if Result >= FCapacity then
|
|
SetCount(Result + 1)
|
|
else
|
|
FCount := Result + 1;
|
|
FData[Result] := Value;
|
|
end;
|
|
|
|
function TObjectArray.ReleaseItem(const Idx: Integer): TObject;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
Result := FData[Idx];
|
|
if Assigned(Result) and FIsItemOwner then
|
|
FData[Idx] := nil;
|
|
end;
|
|
|
|
function TObjectArray.GetRange(const LoIdx, HiIdx: Integer): ObjectArray;
|
|
begin
|
|
Result := Copy(FData, LoIdx, MinInt(HiIdx, FCount - 1) - LoIdx + 1);
|
|
end;
|
|
|
|
procedure TObjectArray.SetData(const AData: ObjectArray);
|
|
begin
|
|
if FIsItemOwner then
|
|
FreeItems;
|
|
FData := AData;
|
|
FCount := Length(FData);
|
|
FCapacity := FCount;
|
|
end;
|
|
|
|
function TObjectArray.DuplicateRange(const LoIdx, HiIdx: Integer): AArray;
|
|
var I : Integer;
|
|
V : TObject;
|
|
begin
|
|
Result := CreateInstance as TObjectArray;
|
|
for I := LoIdx to MinInt(HiIdx, FCount - 1) do
|
|
begin
|
|
V := FData[I];
|
|
if V is AType then
|
|
V := AType(V).Duplicate;
|
|
TObjectArray(Result).AppendItem(V);
|
|
end;
|
|
end;
|
|
|
|
procedure TObjectArray.Delete(const Idx: Integer; const ACount: Integer = 1);
|
|
var N : Integer;
|
|
begin
|
|
N := DynArrayRemove(FData, Idx, ACount, FIsItemOwner);
|
|
Dec(FCapacity, N);
|
|
Dec(FCount, N);
|
|
end;
|
|
|
|
procedure TObjectArray.Insert(const Idx: Integer; const ACount: Integer = 1);
|
|
var I : Integer;
|
|
begin
|
|
I := DynArrayInsert(FData, Idx, ACount);
|
|
if I >= 0 then
|
|
begin
|
|
Inc(FCapacity, ACount);
|
|
Inc(FCount, ACount);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
{%TEMPLATE AArrayDynArrayImpl 'Interface' '' 'IInterface' '' }
|
|
{ }
|
|
{ TBitArray }
|
|
{ }
|
|
function Word32IsBitSet(const A: Word32; const B: Integer): Boolean;
|
|
begin
|
|
if (B < 0) or (B > 31) then
|
|
Result := False
|
|
else
|
|
Result := (A and (1 shl B) <> 0);
|
|
end;
|
|
|
|
function Word32SetBitF(const A: Word32; const B: Integer): Word32;
|
|
begin
|
|
if (B < 0) or (B > 31) then
|
|
Result := A
|
|
else
|
|
Result := A or (1 shl B);
|
|
end;
|
|
|
|
function Word32ClearBitF(const A: Word32; const B: Integer): Word32;
|
|
begin
|
|
if (B < 0) or (B > 31) then
|
|
Result := A
|
|
else
|
|
Result := A and not (1 shl B);
|
|
end;
|
|
|
|
function LowBitMask32(const HighBitIndex: Word32): Word32;
|
|
begin
|
|
if HighBitIndex >= 32 then
|
|
Result := 0
|
|
else
|
|
Result := BitMaskTable32[HighBitIndex] - 1;
|
|
end;
|
|
|
|
function HighBitMask32(const LowBitIndex: Word32): Word32;
|
|
begin
|
|
if LowBitIndex >= 32 then
|
|
Result := 0
|
|
else
|
|
Result := not BitMaskTable32[LowBitIndex] + 1;
|
|
end;
|
|
|
|
function RangeBitMask32(const LowBitIndex, HighBitIndex: Word32): Word32;
|
|
begin
|
|
if (LowBitIndex >= 32) and (HighBitIndex >= 32) then
|
|
begin
|
|
Result := 0;
|
|
exit;
|
|
end;
|
|
Result := $FFFFFFFF;
|
|
if LowBitIndex > 0 then
|
|
Result := Result xor (BitMaskTable32[LowBitIndex] - 1);
|
|
if HighBitIndex < 31 then
|
|
Result := Result xor (not BitMaskTable32[HighBitIndex + 1] + 1);
|
|
end;
|
|
|
|
const
|
|
TrueWord32 : Word32 = $FFFFFFFF;
|
|
FalseWord32 : Word32 = $00000000;
|
|
|
|
function TBitArray.GetBit(const Idx: Integer): Boolean;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
Result := Word32IsBitSet(FData[Idx shr 5], Idx and 31);
|
|
end;
|
|
|
|
procedure TBitArray.SetBit(const Idx: Integer; const Value: Boolean);
|
|
var
|
|
L : PWord32;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
L := @FData[Idx shr 5];
|
|
if Value then
|
|
L^ := Word32SetBitF(L^, Idx and 31)
|
|
else
|
|
L^ := Word32ClearBitF(L^, Idx and 31);
|
|
end;
|
|
|
|
function TBitArray.GetCount: Integer;
|
|
begin
|
|
Result := FCount;
|
|
end;
|
|
|
|
procedure TBitArray.SetCount(const NewCount: Integer);
|
|
begin
|
|
if NewCount = FCount then
|
|
exit;
|
|
SetLengthAndZero(FData, (NewCount + 31) div 32);
|
|
FCount := NewCount;
|
|
end;
|
|
|
|
function TBitArray.GetRangeL(const Idx: Integer): Word32;
|
|
var
|
|
F : Byte;
|
|
I : Integer;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
F := Idx and 31;
|
|
I := Idx shr 5;
|
|
if F = 0 then
|
|
Result := FData[I]
|
|
else
|
|
begin
|
|
Result := FData[I] shr F;
|
|
if I + 1 < Length(FData) then
|
|
Result := Result or (FData[I + 1] shl (32 - F));
|
|
end;
|
|
end;
|
|
|
|
procedure TBitArray.SetRangeL(const Idx: Integer; const Value: Word32);
|
|
var
|
|
F : Byte;
|
|
I : Integer;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
F := Idx and 31;
|
|
I := Idx shr 5;
|
|
if F = 0 then
|
|
FData[I] := Value
|
|
else
|
|
begin
|
|
FData[I] := (FData[I] and LowBitMask32(F))
|
|
or (Value shl F);
|
|
if I + 1 < Length(FData) then
|
|
FData[I + 1] := (FData[I + 1] and HighBitMask32(F))
|
|
or (Value shr (32 - F));
|
|
end;
|
|
end;
|
|
|
|
function TBitArray.IsRange(const LoIdx, HiIdx: Integer; const Value: Boolean): Boolean;
|
|
var B, I : Word32;
|
|
IL, IH : Integer;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (LoIdx < 0) or (LoIdx > HiIdx) or (HiIdx >= FCount) then
|
|
RaiseIndexError(HiIdx);
|
|
{$ENDIF}
|
|
// Check bits in FData[IL]
|
|
IL := LoIdx shr 5;
|
|
IH := HiIdx shr 5;
|
|
B := HighBitMask32(LoIdx and 31);
|
|
I := FData[IL];
|
|
if Value then
|
|
Result := I or B = I
|
|
else
|
|
Result := I and not B = I;
|
|
if not Result or (IL = IH) then
|
|
exit;
|
|
// Check bits in FData[IH]
|
|
B := LowBitMask32(HiIdx and 31);
|
|
I := FData[IH];
|
|
if Value then
|
|
Result := I or B = I
|
|
else
|
|
Result := I and not B = I;
|
|
if not Result or (IH = IL + 1) then
|
|
exit;
|
|
// Check bits in FStore[IL + 1..IR - 1]
|
|
for I := IL + 1 to IH - 1 do
|
|
if (Value and (FData[I] <> TrueWord32)) or
|
|
(not Value and (FData[I] <> FalseWord32)) then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TBitArray.Fill(const LoIdx, HiIdx: Integer; const Value: Boolean);
|
|
var
|
|
B, I : Word32;
|
|
IL, IH : Integer;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (LoIdx < 0) or (LoIdx > HiIdx) or (HiIdx >= FCount) then
|
|
RaiseIndexError(HiIdx);
|
|
{$ENDIF}
|
|
IL := LoIdx shr 5;
|
|
IH := HiIdx shr 5;
|
|
// Set bits in FData[IL]
|
|
if IH = IL then
|
|
B := RangeBitMask32(LoIdx and 31, HiIdx and 31) else
|
|
B := HighBitMask32(LoIdx and 31);
|
|
I := FData[IL];
|
|
if Value then
|
|
FData[IL] := I or B
|
|
else
|
|
FData[IL] := I and not B;
|
|
if IH = IL then
|
|
exit;
|
|
// Set bits in FData[IH]
|
|
B := LowBitMask32(HiIdx and 31);
|
|
I := FData[IH];
|
|
if Value then
|
|
FData[IH] := I or B
|
|
else
|
|
FData[IH] := I and not B;
|
|
if IH = IL + 1 then
|
|
exit;
|
|
// Set bits in FData[IL + 1..IR - 1]
|
|
for I := IL + 1 to IH - 1 do
|
|
if Value then
|
|
FData[I] := TrueWord32
|
|
else
|
|
FData[I] := FalseWord32;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Hashed Array helper function }
|
|
{ }
|
|
const
|
|
ArrayAverageHashChainSize = 4;
|
|
|
|
function ArrayRehashSize(const Count: Integer): Integer;
|
|
var L : Integer;
|
|
begin
|
|
L := Count div ArrayAverageHashChainSize; // Number of slots
|
|
if L <= 16 then // Rehash in powers of 16
|
|
Result := 16 else
|
|
if L <= 256 then
|
|
Result := 256 else
|
|
if L <= 4096 then
|
|
Result := 4096 else
|
|
if L <= 65536 then
|
|
Result := 65536 else
|
|
if L <= 1048576 then
|
|
Result := 1048576 else
|
|
if L <= 16777216 then
|
|
Result := 16777216 else
|
|
Result := 268435456;
|
|
end;
|
|
|
|
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
{ }
|
|
{ THashedAnsiStringArray }
|
|
{ }
|
|
constructor THashedAnsiStringArray.Create(const ACaseSensitive: Boolean);
|
|
begin
|
|
inherited Create(nil);
|
|
FCaseSensitive := ACaseSensitive;
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.Init;
|
|
begin
|
|
inherited Init;
|
|
FCaseSensitive := True;
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.Assign(const Source: TObject);
|
|
begin
|
|
if Source is THashedAnsiStringArray then
|
|
begin
|
|
// Assign array data
|
|
inherited Assign(Source);
|
|
// Assign hash lookup
|
|
FLookup := Copy(THashedAnsiStringArray(Source).FLookup);
|
|
FCaseSensitive := THashedAnsiStringArray(Source).FCaseSensitive;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.Clear;
|
|
begin
|
|
inherited Clear;
|
|
Rehash;
|
|
end;
|
|
|
|
function THashedAnsiStringArray.LocateItemHash(const Value: AnsiString;
|
|
var LookupList, LookupIdx: Integer): Boolean;
|
|
var I: Integer;
|
|
begin
|
|
// Hash value
|
|
LookupList := HashStrA(Value, 1, -1, FCaseSensitive, Length(FLookup));
|
|
// Locate value in hash lookup
|
|
for I := 0 to Length(FLookup[LookupList]) - 1 do
|
|
if StrEqualA(Value, FData[FLookup[LookupList][I]], FCaseSensitive) then
|
|
begin
|
|
LookupIdx := I;
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
// Not found
|
|
LookupIdx := -1;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.Rehash;
|
|
var I, C, L : Integer;
|
|
begin
|
|
C := FCount;
|
|
L := ArrayRehashSize(C);
|
|
FLookup := nil;
|
|
SetLength(FLookup, L);
|
|
for I := 0 to C - 1 do
|
|
DynArrayAppend(FLookup[HashStrA(FData[I], 1, -1, FCaseSensitive, L)], I);
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.ExchangeItems(const Idx1, Idx2: Integer);
|
|
var L1, L2, I1, I2: Integer;
|
|
begin
|
|
// Swap lookup
|
|
if LocateItemHash(FData[Idx1], L1, I1) and
|
|
LocateItemHash(FData[Idx2], L2, I2) then
|
|
Swap(FLookup[L1][I1], FLookup[L2][I2]);
|
|
// Swap array items
|
|
inherited ExchangeItems(Idx1, Idx2);
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.Delete(const Idx: Integer; const ACount: Integer);
|
|
var I, L, V : Integer;
|
|
P : PInteger;
|
|
begin
|
|
// Delete lookup
|
|
for I := MaxInt(0, Idx) to MinInt(FCount, Idx + ACount - 1) do
|
|
if LocateItemHash(FData[I], L, V) then
|
|
DynArrayRemove(FLookup[L], V, 1);
|
|
// Delete array
|
|
inherited Delete(Idx, ACount);
|
|
// Reindex
|
|
for I := 0 to Length(FLookup) - 1 do
|
|
for V := 0 to Length(FLookup[I]) - 1 do
|
|
begin
|
|
P := @FLookup[I][V];
|
|
if P^ >= Idx then
|
|
Dec(P^);
|
|
end;
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.Insert(const Idx: Integer; const ACount: Integer);
|
|
begin
|
|
// Insert array
|
|
inherited Insert(Idx, ACount);
|
|
// Rebuild hash table
|
|
Rehash;
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.SetData(const AData: AnsiStringArray);
|
|
begin
|
|
inherited SetData(AData);
|
|
Rehash;
|
|
end;
|
|
|
|
procedure THashedAnsiStringArray.SetItem(const Idx: Integer; const Value: AnsiString);
|
|
var S : AnsiString;
|
|
I, J : Integer;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
// Remove old hash
|
|
S := FData[Idx];
|
|
if LocateItemHash(S, I, J) then
|
|
DynArrayRemove(FLookup[I], J, 1);
|
|
// Set array value
|
|
FData[Idx] := Value;
|
|
// Add new hash
|
|
DynArrayAppend(FLookup[HashStrA(Value, 1, -1, FCaseSensitive, Length(FLookup))], Idx);
|
|
end;
|
|
|
|
function THashedAnsiStringArray.AppendItem(const Value: AnsiString): Integer;
|
|
var L : Integer;
|
|
begin
|
|
// add to array
|
|
Result := Count;
|
|
Count := Result + 1;
|
|
FData[Result] := Value;
|
|
// add lookup
|
|
L := Length(FLookup);
|
|
DynArrayAppend(FLookup[HashStrA(Value, 1, -1, FCaseSensitive, L)], Result);
|
|
if (Result + 1) div ArrayAverageHashChainSize > L then
|
|
Rehash;
|
|
end;
|
|
|
|
function THashedAnsiStringArray.PosNext(const Find: AnsiString; const PrevPos: Integer): Integer;
|
|
var I, J, F, L, P : Integer;
|
|
begin
|
|
// locate first
|
|
if not LocateItemHash(Find, I, J) then
|
|
begin
|
|
Result := -1;
|
|
exit;
|
|
end;
|
|
if PrevPos < 0 then
|
|
begin
|
|
Result := FLookup[I][J];
|
|
exit;
|
|
end;
|
|
// locate previous
|
|
L := Length(FLookup[I]);
|
|
P := -1;
|
|
for F := J to L - 1 do
|
|
if FLookup[I][F] = PrevPos then
|
|
begin
|
|
P := F;
|
|
break;
|
|
end;
|
|
if P = -1 then
|
|
begin
|
|
Result := 1;
|
|
exit;
|
|
end;
|
|
// locate next
|
|
for F := P + 1 to L - 1 do
|
|
begin
|
|
Result := FLookup[I][F];
|
|
if StrEqualA(Find, FData[Result], FCaseSensitive) then
|
|
// found
|
|
exit;
|
|
end;
|
|
// not found
|
|
Result := 1;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ THashedRawByteStringArray }
|
|
{ }
|
|
constructor THashedRawByteStringArray.Create(const ACaseSensitive: Boolean);
|
|
begin
|
|
inherited Create(nil);
|
|
FCaseSensitive := ACaseSensitive;
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.Init;
|
|
begin
|
|
inherited Init;
|
|
FCaseSensitive := True;
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.Assign(const Source: TObject);
|
|
begin
|
|
if Source is THashedRawByteStringArray then
|
|
begin
|
|
// Assign array data
|
|
inherited Assign(Source);
|
|
// Assign hash lookup
|
|
FLookup := Copy(THashedRawByteStringArray(Source).FLookup);
|
|
FCaseSensitive := THashedRawByteStringArray(Source).FCaseSensitive;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.Clear;
|
|
begin
|
|
inherited Clear;
|
|
Rehash;
|
|
end;
|
|
|
|
function THashedRawByteStringArray.LocateItemHash(const Value: RawByteString;
|
|
var LookupList, LookupIdx: Integer): Boolean;
|
|
var I: Integer;
|
|
begin
|
|
// Hash value
|
|
LookupList := HashStrB(Value, 1, -1, FCaseSensitive, Length(FLookup));
|
|
// Locate value in hash lookup
|
|
for I := 0 to Length(FLookup[LookupList]) - 1 do
|
|
if StrEqualB(Value, FData[FLookup[LookupList][I]], FCaseSensitive) then
|
|
begin
|
|
LookupIdx := I;
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
// Not found
|
|
LookupIdx := -1;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.Rehash;
|
|
var I, C, L : Integer;
|
|
begin
|
|
C := FCount;
|
|
L := ArrayRehashSize(C);
|
|
FLookup := nil;
|
|
SetLength(FLookup, L);
|
|
for I := 0 to C - 1 do
|
|
DynArrayAppend(FLookup[HashStrB(FData[I], 1, -1, FCaseSensitive, L)], I);
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.ExchangeItems(const Idx1, Idx2: Integer);
|
|
var L1, L2, I1, I2: Integer;
|
|
begin
|
|
// Swap lookup
|
|
if LocateItemHash(FData[Idx1], L1, I1) and
|
|
LocateItemHash(FData[Idx2], L2, I2) then
|
|
SwapInt(FLookup[L1][I1], FLookup[L2][I2]);
|
|
// Swap array items
|
|
inherited ExchangeItems(Idx1, Idx2);
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.Delete(const Idx: Integer; const ACount: Integer);
|
|
var I, L, V : Integer;
|
|
P : PInteger;
|
|
begin
|
|
// Delete lookup
|
|
for I := MaxInt(0, Idx) to MinInt(FCount, Idx + ACount - 1) do
|
|
if LocateItemHash(FData[I], L, V) then
|
|
DynArrayRemove(FLookup[L], V, 1);
|
|
// Delete array
|
|
inherited Delete(Idx, ACount);
|
|
// Reindex
|
|
for I := 0 to Length(FLookup) - 1 do
|
|
for V := 0 to Length(FLookup[I]) - 1 do
|
|
begin
|
|
P := @FLookup[I][V];
|
|
if P^ >= Idx then
|
|
Dec(P^);
|
|
end;
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.Insert(const Idx: Integer; const ACount: Integer);
|
|
begin
|
|
// Insert array
|
|
inherited Insert(Idx, ACount);
|
|
// Rebuild hash table
|
|
Rehash;
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.SetData(const AData: RawByteStringArray);
|
|
begin
|
|
inherited SetData(AData);
|
|
Rehash;
|
|
end;
|
|
|
|
procedure THashedRawByteStringArray.SetItem(const Idx: Integer; const Value: RawByteString);
|
|
var S : RawByteString;
|
|
I, J : Integer;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
// Remove old hash
|
|
S := FData[Idx];
|
|
if LocateItemHash(S, I, J) then
|
|
DynArrayRemove(FLookup[I], J, 1);
|
|
// Set array value
|
|
FData[Idx] := Value;
|
|
// Add new hash
|
|
DynArrayAppend(FLookup[HashStrB(Value, 1, -1, FCaseSensitive, Length(FLookup))], Idx);
|
|
end;
|
|
|
|
function THashedRawByteStringArray.AppendItem(const Value: RawByteString): Integer;
|
|
var L : Integer;
|
|
begin
|
|
// add to array
|
|
Result := Count;
|
|
Count := Result + 1;
|
|
FData[Result] := Value;
|
|
// add lookup
|
|
L := Length(FLookup);
|
|
DynArrayAppend(FLookup[HashStrB(Value, 1, -1, FCaseSensitive, L)], Result);
|
|
if (Result + 1) div ArrayAverageHashChainSize > L then
|
|
Rehash;
|
|
end;
|
|
|
|
function THashedRawByteStringArray.PosNext(const Find: RawByteString; const PrevPos: Integer): Integer;
|
|
var I, J, F, L, P : Integer;
|
|
begin
|
|
// locate first
|
|
if not LocateItemHash(Find, I, J) then
|
|
begin
|
|
Result := -1;
|
|
exit;
|
|
end;
|
|
if PrevPos < 0 then
|
|
begin
|
|
Result := FLookup[I][J];
|
|
exit;
|
|
end;
|
|
// locate previous
|
|
L := Length(FLookup[I]);
|
|
P := -1;
|
|
for F := J to L - 1 do
|
|
if FLookup[I][F] = PrevPos then
|
|
begin
|
|
P := F;
|
|
break;
|
|
end;
|
|
if P = -1 then
|
|
begin
|
|
Result := 1;
|
|
exit;
|
|
end;
|
|
// locate next
|
|
for F := P + 1 to L - 1 do
|
|
begin
|
|
Result := FLookup[I][F];
|
|
if StrEqualB(Find, FData[Result], FCaseSensitive) then
|
|
// found
|
|
exit;
|
|
end;
|
|
// not found
|
|
Result := 1;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ THashedUnicodeStringArray }
|
|
{ }
|
|
constructor THashedUnicodeStringArray.Create(const ACaseSensitive: Boolean);
|
|
begin
|
|
inherited Create(nil);
|
|
FCaseSensitive := ACaseSensitive;
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.Init;
|
|
begin
|
|
inherited Init;
|
|
FCaseSensitive := True;
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.Assign(const Source: TObject);
|
|
begin
|
|
if Source is THashedUnicodeStringArray then
|
|
begin
|
|
// Assign array data
|
|
inherited Assign(Source);
|
|
// Assign hash lookup
|
|
FLookup := Copy(THashedUnicodeStringArray(Source).FLookup);
|
|
FCaseSensitive := THashedUnicodeStringArray(Source).FCaseSensitive;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.Clear;
|
|
begin
|
|
inherited Clear;
|
|
Rehash;
|
|
end;
|
|
|
|
function THashedUnicodeStringArray.LocateItemHash(const Value: UnicodeString;
|
|
var LookupList, LookupIdx: Integer): Boolean;
|
|
var I: Integer;
|
|
begin
|
|
// Hash value
|
|
LookupList := HashStrU(Value, 1, -1, FCaseSensitive, Length(FLookup));
|
|
// Locate value in hash lookup
|
|
for I := 0 to Length(FLookup[LookupList]) - 1 do
|
|
if StrEqualU(Value, FData[FLookup[LookupList][I]], FCaseSensitive) then
|
|
begin
|
|
LookupIdx := I;
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
// Not found
|
|
LookupIdx := -1;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.Rehash;
|
|
var I, C, L : Integer;
|
|
begin
|
|
C := FCount;
|
|
L := ArrayRehashSize(C);
|
|
FLookup := nil;
|
|
SetLength(FLookup, L);
|
|
for I := 0 to C - 1 do
|
|
DynArrayAppend(FLookup[HashStrU(FData[I], 1, -1, FCaseSensitive, L)], I);
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.ExchangeItems(const Idx1, Idx2: Integer);
|
|
var L1, L2, I1, I2: Integer;
|
|
begin
|
|
// Swap lookup
|
|
if LocateItemHash(FData[Idx1], L1, I1) and
|
|
LocateItemHash(FData[Idx2], L2, I2) then
|
|
SwapInt(FLookup[L1][I1], FLookup[L2][I2]);
|
|
// Swap array items
|
|
inherited ExchangeItems(Idx1, Idx2);
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.Delete(const Idx: Integer; const ACount: Integer);
|
|
var I, L, V : Integer;
|
|
P : PInteger;
|
|
begin
|
|
// Delete lookup
|
|
for I := MaxInt(0, Idx) to MinInt(FCount, Idx + ACount - 1) do
|
|
if LocateItemHash(FData[I], L, V) then
|
|
DynArrayRemove(FLookup[L], V, 1);
|
|
// Delete array
|
|
inherited Delete(Idx, ACount);
|
|
// Reindex
|
|
for I := 0 to Length(FLookup) - 1 do
|
|
for V := 0 to Length(FLookup[I]) - 1 do
|
|
begin
|
|
P := @FLookup[I][V];
|
|
if P^ >= Idx then
|
|
Dec(P^);
|
|
end;
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.Insert(const Idx: Integer; const ACount: Integer);
|
|
begin
|
|
// Insert array
|
|
inherited Insert(Idx, ACount);
|
|
// Rebuild hash table
|
|
Rehash;
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.SetData(const AData: UnicodeStringArray);
|
|
begin
|
|
inherited SetData(AData);
|
|
Rehash;
|
|
end;
|
|
|
|
procedure THashedUnicodeStringArray.SetItem(const Idx: Integer; const Value: UnicodeString);
|
|
var S : UnicodeString;
|
|
I, J : Integer;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FCount) then
|
|
RaiseIndexError(Idx);
|
|
{$ENDIF}
|
|
// Remove old hash
|
|
S := FData[Idx];
|
|
if LocateItemHash(S, I, J) then
|
|
DynArrayRemove(FLookup[I], J, 1);
|
|
// Set array value
|
|
FData[Idx] := Value;
|
|
// Add new hash
|
|
DynArrayAppend(FLookup[HashStrU(Value, 1, -1, FCaseSensitive, Length(FLookup))], Idx);
|
|
end;
|
|
|
|
function THashedUnicodeStringArray.AppendItem(const Value: UnicodeString): Integer;
|
|
var L : Integer;
|
|
begin
|
|
// add to array
|
|
Result := Count;
|
|
Count := Result + 1;
|
|
FData[Result] := Value;
|
|
// add lookup
|
|
L := Length(FLookup);
|
|
DynArrayAppend(FLookup[HashStrU(Value, 1, -1, FCaseSensitive, L)], Result);
|
|
if (Result + 1) div ArrayAverageHashChainSize > L then
|
|
Rehash;
|
|
end;
|
|
|
|
function THashedUnicodeStringArray.PosNext(const Find: UnicodeString; const PrevPos: Integer): Integer;
|
|
var I, J, F, L, P : Integer;
|
|
begin
|
|
// locate first
|
|
if not LocateItemHash(Find, I, J) then
|
|
begin
|
|
Result := -1;
|
|
exit;
|
|
end;
|
|
if PrevPos < 0 then
|
|
begin
|
|
Result := FLookup[I][J];
|
|
exit;
|
|
end;
|
|
// locate previous
|
|
L := Length(FLookup[I]);
|
|
P := -1;
|
|
for F := J to L - 1 do
|
|
if FLookup[I][F] = PrevPos then
|
|
begin
|
|
P := F;
|
|
break;
|
|
end;
|
|
if P = -1 then
|
|
begin
|
|
Result := 1;
|
|
exit;
|
|
end;
|
|
// locate next
|
|
for F := P + 1 to L - 1 do
|
|
begin
|
|
Result := FLookup[I][F];
|
|
if StrEqualU(Find, FData[Result], FCaseSensitive) then
|
|
// found
|
|
exit;
|
|
end;
|
|
// not found
|
|
Result := 1;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ DICTIONARY BASE CLASSES }
|
|
{ }
|
|
|
|
|
|
|
|
{ }
|
|
{ ADictionaryBase }
|
|
{ }
|
|
function ADictionaryBase.GetItemStrByIndex(const Idx: Integer): String;
|
|
begin
|
|
raise EDictionary.CreateFmt('Method %s.GetItemStrByIndex not implemented', [ClassName]);
|
|
end;
|
|
|
|
function ADictionaryBase.GetAsString: String;
|
|
var I, L : Integer;
|
|
begin
|
|
L := Count - 1;
|
|
for I := 0 to L do
|
|
begin
|
|
Result := Result + GetKeyStrByIndex(I) + ':' + GetItemStrByIndex(I);
|
|
if I < L then
|
|
Result := Result + ',';
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
{ }
|
|
{ ADictionaryA }
|
|
{ }
|
|
procedure ADictionaryA.RaiseKeyNotFoundError(const Key: AnsiString);
|
|
begin
|
|
raise EDictionary.CreateFmt('Key not found: %s', [Key]);
|
|
end;
|
|
|
|
procedure ADictionaryA.RaiseDuplicateKeyError(const Key: AnsiString);
|
|
begin
|
|
raise EDictionary.CreateFmt('Duplicate key: %s', [Key]);
|
|
end;
|
|
|
|
function ADictionaryA.GetKeyStrByIndex(const Idx: Integer): String;
|
|
begin
|
|
Result := ToStringA(GetKeyByIndex(Idx));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ ADictionaryB }
|
|
{ }
|
|
procedure ADictionaryB.RaiseKeyNotFoundError(const Key: RawByteString);
|
|
begin
|
|
raise EDictionary.CreateFmt('Key not found: %s', [Key]);
|
|
end;
|
|
|
|
procedure ADictionaryB.RaiseDuplicateKeyError(const Key: RawByteString);
|
|
begin
|
|
raise EDictionary.CreateFmt('Duplicate key: %s', [Key]);
|
|
end;
|
|
|
|
function ADictionaryB.GetKeyStrByIndex(const Idx: Integer): String;
|
|
begin
|
|
Result := ToStringB(GetKeyByIndex(Idx));
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ADictionaryU }
|
|
{ }
|
|
procedure ADictionaryU.RaiseKeyNotFoundError(const Key: UnicodeString);
|
|
begin
|
|
raise EDictionary.CreateFmt('Key not found: %s', [Key]);
|
|
end;
|
|
|
|
procedure ADictionaryU.RaiseDuplicateKeyError(const Key: UnicodeString);
|
|
begin
|
|
raise EDictionary.CreateFmt('Duplicate key: %s', [Key]);
|
|
end;
|
|
|
|
function ADictionaryU.GetKeyStrByIndex(const Idx: Integer): String;
|
|
begin
|
|
Result := ToStringU(GetKeyByIndex(Idx));
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ADictionary }
|
|
{ }
|
|
procedure ADictionary.RaiseKeyNotFoundError(const Key: String);
|
|
begin
|
|
raise EDictionary.CreateFmt('Key not found: %s', [Key]);
|
|
end;
|
|
|
|
procedure ADictionary.RaiseDuplicateKeyError(const Key: String);
|
|
begin
|
|
raise EDictionary.CreateFmt('Duplicate key: %s', [Key]);
|
|
end;
|
|
|
|
function ADictionary.GetKeyStrByIndex(const Idx: Integer): String;
|
|
begin
|
|
Result := GetKeyByIndex(Idx);
|
|
end;
|
|
|
|
|
|
|
|
{%DEFINE ATypeDictionaryImpl}
|
|
{ }
|
|
{-A%1%Dictionary%9% }
|
|
{ }
|
|
function A%1%Dictionary%9%.GetItem(const Key: %3%): %2%;
|
|
begin
|
|
if LocateItem(Key, Result) < 0 then
|
|
RaiseKeyNotFoundError(Key);
|
|
end;
|
|
{%IF 4}
|
|
function A%1%Dictionary%9%.GetItemStrByIndex(const Idx: Integer): String;
|
|
begin
|
|
Result := %4%(GetItemByIndex(Idx));
|
|
end;
|
|
{%ENDIF}
|
|
procedure A%1%Dictionary%9%.Assign(const Source: TObject);
|
|
var I : Integer;
|
|
begin
|
|
if Source is A%1%Dictionary%9% then
|
|
begin
|
|
Clear;
|
|
for I := 0 to A%1%Dictionary%9%(Source).Count - 1 do
|
|
Add(A%1%Dictionary%9%(Source).GetKeyByIndex(I),
|
|
A%1%Dictionary%9%(Source).GetItemByIndex(I));
|
|
end else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
function A%1%Dictionary%9%.GetAsString: String;
|
|
var I, L : Integer;
|
|
begin
|
|
L := Count - 1;
|
|
for I := 0 to L do
|
|
begin
|
|
Result := Result + GetKeyStrByIndex(I) + ':' + GetItemStrByIndex(I);
|
|
if I < L then
|
|
Result := Result + ',';
|
|
end;
|
|
end;
|
|
{%IF 7}
|
|
function A%1%Dictionary%9%.GetItemLength(const Key: %3%): Integer;
|
|
begin
|
|
Result := Length(GetItem(Key));
|
|
end;
|
|
|
|
function A%1%Dictionary%9%.GetTotalLength: Int64;
|
|
var I : Integer;
|
|
begin
|
|
Result := 0;
|
|
for I := 0 to Count - 1 do
|
|
Inc(Result, Length(GetItemByIndex(I)));
|
|
end;
|
|
{%ENDIF}{%IF 5}
|
|
procedure A%1%Dictionary%9%.Clear;
|
|
begin
|
|
if IsItemOwner then
|
|
FreeItems else
|
|
ReleaseItems;
|
|
end;
|
|
{%ENDIF}
|
|
|
|
{%ENDDEF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'LongInt' 'LongInt' 'AnsiString' 'IntToStr' '' '0' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'LongInt' 'LongInt' 'RawByteString' 'IntToStr' '' '0' '' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'LongInt' 'LongInt' 'UnicodeString' 'IntToStr' '' '0' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'LongInt' 'LongInt' 'String' 'IntToStr' '' '0' '' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'LongWord' 'LongWord' 'AnsiString' 'IntToStr' '' '0' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'LongWord' 'LongWord' 'RawByteString' 'IntToStr' '' '0' '' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'LongWord' 'LongWord' 'UnicodeString' 'IntToStr' '' '0' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'LongWord' 'LongWord' 'String' 'IntToStr' '' '0' '' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Int64' 'Int64' 'AnsiString' 'IntToStr' '' '0' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Int64' 'Int64' 'RawByteString' 'IntToStr' '' '0' '' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Int64' 'Int64' 'UnicodeString' 'IntToStr' '' '0' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Int64' 'Int64' 'String' 'IntToStr' '' '0' '' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Single' 'Single' 'AnsiString' 'FloatToStr' '' '0.0' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Single' 'Single' 'RawByteString' 'FloatToStr' '' '0.0' '' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Single' 'Single' 'UnicodeString' 'FloatToStr' '' '0.0' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Single' 'Single' 'String' 'FloatToStr' '' '0.0' '' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Double' 'Double' 'AnsiString' 'FloatToStr' '' '0.0' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Double' 'Double' 'RawByteString' 'FloatToStr' '' '0.0' '' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Double' 'Double' 'UnicodeString' 'FloatToStr' '' '0.0' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Double' 'Double' 'String' 'FloatToStr' '' '0.0' '' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Extended' 'Extended' 'AnsiString' 'FloatToStr' '' '0.0' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Extended' 'Extended' 'RawByteString' 'FloatToStr' '' '0.0' '' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Extended' 'Extended' 'UnicodeString' 'FloatToStr' '' '0.0' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Extended' 'Extended' 'String' 'FloatToStr' '' '0.0' '' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'AnsiString' 'AnsiString' 'AnsiString' 'ToStringA' '' '''''' 'L' '' 'A' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'AnsiString' 'AnsiString' 'UnicodeString' 'ToStringA' '' '''''' 'L' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'AnsiString' 'AnsiString' 'String' 'ToStringA' '' '''''' 'L' '' '' }
|
|
{$ENDIF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'RawByteString' 'RawByteString' 'AnsiString' 'ToStringB' '' '''''' 'L' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'RawByteString' 'RawByteString' 'RawByteString' 'ToStringB' '' '''''' 'L' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'RawByteString' 'RawByteString' 'UnicodeString' 'ToStringB' '' '''''' 'L' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'RawByteString' 'RawByteString' 'String' 'ToStringB' '' '''''' 'L' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'UnicodeString' 'UnicodeString' 'AnsiString' 'ToStringU' '' '''''' 'L' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'UnicodeString' 'UnicodeString' 'UnicodeString' 'ToStringU' '' '''''' 'L' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'UnicodeString' 'UnicodeString' 'String' 'ToStringU' '' '''''' 'L' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'String' 'String' 'AnsiString' '' '' '''''' 'L' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'String' 'String' 'UnicodeString' '' '' '''''' 'L' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'String' 'String' 'String' '' '' '''''' 'L' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Pointer' 'Pointer' 'AnsiString' 'PointerToStr' '' '' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Pointer' 'Pointer' 'RawByteString' 'PointerToStr' '' '' '' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Pointer' 'Pointer' 'UnicodeString' 'PointerToStr' '' '' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Pointer' 'Pointer' 'String' 'PointerToStr' '' '' '' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Interface' 'IInterface' 'AnsiString' '' '' '' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Interface' 'IInterface' 'UnicodeString' '' '' '' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Interface' 'IInterface' 'String' '' '' '' '' '' '' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Object' 'TObject' 'AnsiString' 'ObjectClassName' 'O' '' '' '' 'A' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ATypeDictionaryImpl 'Object' 'TObject' 'RawByteString' 'ObjectClassName' 'O' '' '' '' 'B' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Object' 'TObject' 'UnicodeString' 'ObjectClassName' 'O' '' '' '' 'U' }
|
|
{%TEMPLATE ATypeDictionaryImpl 'Object' 'TObject' 'String' 'ObjectClassName' 'O' '' '' '' '' }
|
|
{ }
|
|
{ DICTIONARY IMPLEMENTATIONS }
|
|
{ }
|
|
|
|
|
|
|
|
{ Dictionary helper functions }
|
|
function DictionaryRehashSize(const Count: Integer): Integer;
|
|
var L : Integer;
|
|
begin
|
|
L := Count div DictionaryAverageHashChainSize; // Number of "slots"
|
|
if L <= $10 then // Rehash in powers of 16
|
|
Result := $10 else
|
|
if L <= $100 then
|
|
Result := $100 else
|
|
if L <= $1000 then
|
|
Result := $1000 else
|
|
if L <= $10000 then
|
|
Result := $10000 else
|
|
if L <= $100000 then
|
|
Result := $100000 else
|
|
if L <= $1000000 then
|
|
Result := $1000000 else
|
|
Result := $10000000;
|
|
end;
|
|
|
|
{%DEFINE ObjectDictionaryConstructor}
|
|
constructor TGeneralObjectDictionary%2%.CreateEx(
|
|
const AKeys: T%3%Array;
|
|
const AValues: TObjectArray; const AIsItemOwner: Boolean;
|
|
const AKeysCaseSensitive: Boolean; const AAddOnSet: Boolean;
|
|
const ADuplicatesAction: TDictionaryDuplicatesAction);
|
|
var L : Integer;
|
|
begin
|
|
inherited Create;
|
|
if Assigned(AKeys) then
|
|
begin
|
|
FKeys := AKeys;
|
|
L := FKeys.Count;
|
|
end
|
|
else
|
|
begin
|
|
FKeys := T%3%Array.Create;
|
|
L := 0;
|
|
end;
|
|
if Assigned(AValues) then
|
|
FValues := AValues
|
|
else
|
|
FValues := TObjectArray.Create;
|
|
FValues.Count := L;
|
|
FAddOnSet := AAddOnSet;
|
|
FValues.IsItemOwner := AIsItemOwner;
|
|
FCaseSensitive := AKeysCaseSensitive;
|
|
FDuplicatesAction := ADuplicatesAction;
|
|
if L > 0 then
|
|
Rehash;
|
|
end;
|
|
|
|
constructor TObjectDictionary%2%.CreateEx(
|
|
const AKeys: T%3%Array;
|
|
const AValues: TObjectArray; const AIsItemOwner: Boolean;
|
|
const AKeysCaseSensitive: Boolean; const AAddOnSet: Boolean;
|
|
const ADuplicatesAction: TDictionaryDuplicatesAction);
|
|
begin
|
|
inherited CreateEx(AKeys, AValues, AIsItemOwner, AKeysCaseSensitive, AAddOnSet,
|
|
ADuplicatesAction);
|
|
end;
|
|
{%ENDDEF}
|
|
{%DEFINE TTypeDictionaryConstructor}
|
|
constructor TGeneral%1%Dictionary%2%.CreateEx(
|
|
const AKeys: T%3%Array;
|
|
const AValues: T%1%Array; const AKeysCaseSensitive: Boolean;
|
|
const AAddOnSet: Boolean;
|
|
const ADuplicatesAction: TDictionaryDuplicatesAction);
|
|
var L : Integer;
|
|
begin
|
|
inherited Create;
|
|
if Assigned(AKeys) then
|
|
begin
|
|
FKeys := AKeys;
|
|
L := FKeys.Count;
|
|
end
|
|
else
|
|
begin
|
|
FKeys := T%3%Array.Create;
|
|
L := 0;
|
|
end;
|
|
if Assigned(AValues) then
|
|
FValues := AValues
|
|
else
|
|
FValues := T%1%Array.Create;
|
|
FCaseSensitive := AKeysCaseSensitive;
|
|
FValues.Count := L;
|
|
FAddOnSet := AAddOnSet;
|
|
FDuplicatesAction := ADuplicatesAction;
|
|
if L > 0 then
|
|
Rehash;
|
|
end;
|
|
|
|
constructor T%1%Dictionary%2%.CreateEx(
|
|
const AKeys: T%3%Array;
|
|
const AValues: T%1%Array; const AKeysCaseSensitive: Boolean;
|
|
const AAddOnSet: Boolean;
|
|
const ADuplicatesAction: TDictionaryDuplicatesAction);
|
|
begin
|
|
inherited CreateEx(AKeys, AValues, AKeysCaseSensitive, AAddOnSet,
|
|
ADuplicatesAction);
|
|
end;
|
|
{%ENDDEF}
|
|
{%DEFINE ADictionaryByArrayImpl}
|
|
{ }
|
|
{-TGeneral%1%Dictionary%6% }
|
|
{ }
|
|
constructor TGeneral%1%Dictionary%6%.Create;
|
|
begin
|
|
inherited Create;
|
|
FCaseSensitive := True;
|
|
FDuplicatesAction := ddAccept;
|
|
FAddOnSet := True;
|
|
FKeys := T%7%Array.Create;
|
|
FValues := T%1%Array.Create;
|
|
end;
|
|
|
|
{%TEMPLATE %5% '%1%' '%6%' '%7%' }
|
|
destructor TGeneral%1%Dictionary%6%.Destroy;
|
|
begin
|
|
FreeAndNil(FValues);
|
|
FreeAndNil(FKeys);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.GetKeysCaseSensitive: Boolean;
|
|
begin
|
|
Result := FCaseSensitive;
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.GetAddOnSet: Boolean;
|
|
begin
|
|
Result := FAddOnSet;
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.SetAddOnSet(const AAddOnSet: Boolean);
|
|
begin
|
|
FAddOnSet := AAddOnSet;
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.GetHashTableSize: Integer;
|
|
begin
|
|
Result := Length(FLookup);
|
|
end;{%IF 4}
|
|
|
|
function TGeneral%1%Dictionary%6%.GetIsItemOwner: Boolean;
|
|
begin
|
|
Result := FValues.IsItemOwner;
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.SetIsItemOwner(const AIsItemOwner: Boolean);
|
|
begin
|
|
FValues.IsItemOwner := AIsItemOwner;
|
|
end;{%ENDIF}
|
|
|
|
procedure TGeneral%1%Dictionary%6%.Rehash;
|
|
var I, C, L : Integer;
|
|
begin
|
|
C := FKeys.Count;
|
|
L := DictionaryRehashSize(C);
|
|
FLookup := nil;
|
|
SetLength(FLookup, L);
|
|
FHashSize := L;
|
|
Assert(L > 0);
|
|
Dec(L);
|
|
for I := 0 to C - 1 do
|
|
DynArrayAppend(FLookup[HashStr%6%(FKeys[I], 1, -1, FCaseSensitive, 0) and L], I);
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.LocateKey(const Key: %7%; var LookupIdx: Word32;
|
|
const ErrorIfNotFound: Boolean): Integer;
|
|
var H : Word32;
|
|
I, J, L : Integer;
|
|
begin
|
|
L := FHashSize;
|
|
if L > 0 then
|
|
begin
|
|
H := HashStr%6%(Key, 1, -1, FCaseSensitive, 0) and (L - 1);
|
|
LookupIdx := H;
|
|
for I := 0 to Length(FLookup[H]) - 1 do
|
|
begin
|
|
J := FLookup[H, I];
|
|
if StrEqual%6%(Key, FKeys[J], FCaseSensitive) then
|
|
begin
|
|
Result := J;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
if ErrorIfNotFound then
|
|
RaiseKeyNotFoundError(Key);
|
|
Result := -1;
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.Add(const Key: %7%; const Value: %3%);
|
|
var H : Word32;
|
|
L, I : Integer;
|
|
begin
|
|
if FDuplicatesAction in [ddIgnore, ddError] then
|
|
if LocateKey(Key, H, False) >= 0 then
|
|
if FDuplicatesAction = ddIgnore then
|
|
exit
|
|
else
|
|
RaiseDuplicateKeyError(Key);
|
|
L := FHashSize;
|
|
if L = 0 then
|
|
begin
|
|
Rehash;
|
|
L := FHashSize;
|
|
Assert(L > 0);
|
|
end;
|
|
H := Integer(HashStr%6%(Key, 1, -1, FCaseSensitive, 0) and (L - 1));
|
|
I := FKeys.AppendItem(Key);
|
|
DynArrayAppend(FLookup[H], I);
|
|
FValues.AppendItem(Value);
|
|
if (I + 1) div DictionaryAverageHashChainSize > L then
|
|
Rehash;
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.DeleteByIndex(const Idx: Integer; const Hash: Integer);
|
|
var I, J, H : Integer;
|
|
begin
|
|
if Hash = -1 then
|
|
H := HashStr%6%(FKeys[Idx], 1, -1, FCaseSensitive, 0) and (FHashSize - 1)
|
|
else
|
|
H := Hash;
|
|
FKeys.Delete(Idx);
|
|
FValues.Delete(Idx);
|
|
J := DynArrayPosNext(Idx, FLookup[H]);
|
|
Assert(J >= 0, 'Invalid hash value/lookup table');
|
|
DynArrayRemove(FLookup[H], J, 1);
|
|
|
|
for I := 0 to FHashSize - 1 do
|
|
for J := 0 to Length(FLookup[I]) - 1 do
|
|
if FLookup[I][J] > Idx then
|
|
Dec(FLookup[I][J]);
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.Delete(const Key: %7%);
|
|
var I : Integer;
|
|
H : Word32;
|
|
begin
|
|
I := LocateKey(Key, H, True);
|
|
DeleteByIndex(I, H);
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.HasKey(const Key: %7%): Boolean;
|
|
var H : Word32;
|
|
begin
|
|
Result := LocateKey(Key, H, False) >= 0;
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.Rename(const Key, NewKey: %7%);
|
|
var I, J : Integer;
|
|
H : Word32;
|
|
begin
|
|
I := LocateKey(Key, H, True);
|
|
FKeys[I] := NewKey;
|
|
J := DynArrayPosNext(I, FLookup[H]);
|
|
Assert(J >= 0, 'Invalid hash value/lookup table');
|
|
DynArrayRemove(FLookup[H], J, 1);
|
|
DynArrayAppend(FLookup[HashStr%6%(NewKey, 1, -1, FCaseSensitive, 0) and (FHashSize - 1)], I);
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.GetDuplicatesAction: TDictionaryDuplicatesAction;
|
|
begin
|
|
Result := FDuplicatesAction;
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.SetDuplicatesAction(const ADuplicatesAction: TDictionaryDuplicatesAction);
|
|
begin
|
|
FDuplicatesAction := ADuplicatesAction;
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.LocateItem(const Key: %7%; var Value: %3%): Integer;
|
|
var H : Word32;
|
|
begin
|
|
Result := LocateKey(Key, H, False);
|
|
if Result >= 0 then
|
|
Value := FValues[Result]
|
|
else
|
|
Value := %2%;
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.LocateNext(const Key: %7%; const Idx: Integer; var Value: %3%): Integer;
|
|
var L, H, I, J, K : Integer;
|
|
begin
|
|
Result := -1;
|
|
L := FHashSize;
|
|
if L = 0 then
|
|
RaiseKeyNotFoundError(Key);
|
|
H := HashStr%6%(Key, 1, -1, FCaseSensitive, 0) and (L - 1);
|
|
for I := 0 to Length(FLookup[H]) - 1 do
|
|
begin
|
|
J := FLookup[H, I];
|
|
if J = Idx then
|
|
begin
|
|
if not StrEqual%6%(Key, FKeys[J], FCaseSensitive) then
|
|
RaiseKeyNotFoundError(Key);
|
|
for K := I + 1 to Length(FLookup[H]) - 1 do
|
|
begin
|
|
J := FLookup[H, K];
|
|
if StrEqual%6%(Key, FKeys[J], FCaseSensitive) then
|
|
begin
|
|
Value := FValues[J];
|
|
Result := J;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result := -1;
|
|
exit;
|
|
end;
|
|
end;
|
|
RaiseKeyNotFoundError(Key);
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.SetItem(const Key: %7%; const Value: %3%);
|
|
var I : Integer;
|
|
H : Word32;
|
|
begin
|
|
I := LocateKey(Key, H, False);
|
|
if I >= 0 then
|
|
FValues[I] := Value else
|
|
if AddOnSet then
|
|
Add(Key, Value) else
|
|
RaiseKeyNotFoundError(Key);
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.RaiseIndexError;
|
|
begin
|
|
raise EDictionary.Create('Index out of range');
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.Count: Integer;
|
|
begin
|
|
Result := FKeys.Count;
|
|
Assert(FValues.Count = Result, 'Key/Value count mismatch');
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.GetKeyByIndex(const Idx: Integer): %7%;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FKeys.Count) then
|
|
RaiseIndexError;
|
|
{$ENDIF}
|
|
Result := FKeys[Idx];
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.DeleteItemByIndex(const Idx: Integer);
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FValues.Count) then
|
|
RaiseIndexError;
|
|
{$ENDIF}
|
|
DeleteByIndex(Idx, -1);
|
|
end;
|
|
|
|
function TGeneral%1%Dictionary%6%.GetItemByIndex(const Idx: Integer): %3%;
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FValues.Count) then
|
|
RaiseIndexError;
|
|
{$ENDIF}
|
|
Result := FValues[Idx];
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.SetItemByIndex(const Idx: Integer; const Value: %3%);
|
|
begin
|
|
{$IFOPT R+}
|
|
if (Idx < 0) or (Idx >= FValues.Count) then
|
|
RaiseIndexError;
|
|
{$ENDIF}
|
|
FValues[Idx] := Value;
|
|
end;
|
|
{%IF 4}
|
|
function TGeneral%1%Dictionary%6%.ReleaseItem(const Key: %7%): TObject;
|
|
var I : Integer;
|
|
H : Word32;
|
|
begin
|
|
I := LocateKey(Key, H, True);
|
|
Result := FValues.ReleaseItem(I);
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.ReleaseItems;
|
|
begin
|
|
FKeys.Clear;
|
|
FValues.ReleaseItems;
|
|
FHashSize := 0;
|
|
FLookup := nil;
|
|
end;
|
|
|
|
procedure TGeneral%1%Dictionary%6%.FreeItems;
|
|
begin
|
|
FKeys.Clear;
|
|
FValues.FreeItems;
|
|
FHashSize := 0;
|
|
FLookup := nil;
|
|
end;
|
|
{%ENDIF}
|
|
procedure TGeneral%1%Dictionary%6%.Clear;
|
|
begin
|
|
FKeys.Clear;
|
|
FValues.Clear;
|
|
FHashSize := 0;
|
|
FLookup := nil;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{-T%1%Dictionary%6% }
|
|
{ }
|
|
function T%1%Dictionary%6%.LocateKey(const Key: %7%; var LookupIdx: Word32;
|
|
const ErrorIfNotFound: Boolean): Integer;
|
|
var H : Word32;
|
|
I, L : Integer;
|
|
begin
|
|
L := FHashSize;
|
|
if L > 0 then
|
|
begin
|
|
H := HashStr%6%(Key, 1, -1, FCaseSensitive, 0) and (L - 1);
|
|
LookupIdx := H;
|
|
for I := 0 to Length(FLookup[H]) - 1 do
|
|
begin
|
|
Result := FLookup[H][I];
|
|
if StrEqual%6%(Key, T%7%Array(FKeys).Data[Result],
|
|
FCaseSensitive) then
|
|
exit;
|
|
end;
|
|
end;
|
|
if ErrorIfNotFound then
|
|
RaiseKeyNotFoundError(Key);
|
|
Result := -1;
|
|
end;
|
|
|
|
function T%1%Dictionary%6%.GetItem(const Key: %7%): %3%;
|
|
var H : Word32;
|
|
I : Integer;
|
|
begin
|
|
I := LocateKey(Key, H, False);
|
|
if I >= 0 then
|
|
Result := T%1%Array(FValues).Data[I]
|
|
else
|
|
Result := %2%;
|
|
end;
|
|
|
|
function T%1%Dictionary%6%.LocateItem(const Key: %7%; var Value: %3%): Integer;
|
|
var H : Word32;
|
|
begin
|
|
Result := LocateKey(Key, H, False);
|
|
if Result >= 0 then
|
|
Value := T%1%Array(FValues).Data[Result]
|
|
else
|
|
Value := %2%;
|
|
end;
|
|
|
|
|
|
{%ENDDEF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'LongInt' '0' 'LongInt' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'LongInt' '0' 'LongInt' '' 'TTypeDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'LongInt' '0' 'LongInt' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'LongInt' '0' 'LongInt' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'LongWord' '0' 'LongWord' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'LongWord' '0' 'LongWord' '' 'TTypeDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'LongWord' '0' 'LongWord' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'LongWord' '0' 'LongWord' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Int64' '0' 'Int64' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Int64' '0' 'Int64' '' 'TTypeDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Int64' '0' 'Int64' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Int64' '0' 'Int64' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Single' '0.0' 'Single' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Single' '0.0' 'Single' '' 'TTypeDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Single' '0.0' 'Single' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Single' '0.0' 'Single' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Double' '0.0' 'Double' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Double' '0.0' 'Double' '' 'TTypeDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Double' '0.0' 'Double' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Double' '0.0' 'Double' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Extended' '0.0' 'Extended' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Extended' '0.0' 'Extended' '' 'TTypeDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Extended' '0.0' 'Extended' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Extended' '0.0' 'Extended' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'AnsiString' '''''' 'AnsiString' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'AnsiString' '''''' 'AnsiString' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'AnsiString' '''''' 'AnsiString' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$ENDIF}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'RawByteString' '''''' 'RawByteString' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'RawByteString' '''''' 'RawByteString' '' 'TTypeDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'RawByteString' '''''' 'RawByteString' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'RawByteString' '''''' 'RawByteString' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'UnicodeString' '''''' 'UnicodeString' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'UnicodeString' '''''' 'UnicodeString' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'UnicodeString' '''''' 'UnicodeString' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'String' '''''' 'String' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'String' '''''' 'String' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'String' '''''' 'String' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Pointer' 'nil' 'Pointer' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Pointer' 'nil' 'Pointer' '' 'TTypeDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Pointer' 'nil' 'Pointer' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Pointer' 'nil' 'Pointer' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Interface' 'nil' 'IInterface' '' 'TTypeDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Interface' 'nil' 'IInterface' '' 'TTypeDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Interface' 'nil' 'IInterface' '' 'TTypeDictionaryConstructor' '' 'String' }
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Object' 'nil' 'TObject' 'O' 'ObjectDictionaryConstructor' 'A' 'AnsiString' }
|
|
{$ENDIF}
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Object' 'nil' 'TObject' 'O' 'ObjectDictionaryConstructor' 'B' 'RawByteString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Object' 'nil' 'TObject' 'O' 'ObjectDictionaryConstructor' 'U' 'UnicodeString' }
|
|
{%TEMPLATE ADictionaryByArrayImpl 'Object' 'nil' 'TObject' 'O' 'ObjectDictionaryConstructor' '' 'String' }
|
|
{ }
|
|
{ Sparse array functions }
|
|
{ }
|
|
const
|
|
SparseArrayAverageHashChainSize = 4;
|
|
|
|
function SparseArrayRehashSize(const Count: Integer): Integer;
|
|
var L : Integer;
|
|
begin
|
|
L := Count div SparseArrayAverageHashChainSize; // Number of "slots"
|
|
if L <= $10 then // Rehash in powers of 16
|
|
Result := $10 else
|
|
if L <= $100 then
|
|
Result := $100 else
|
|
if L <= $1000 then
|
|
Result := $1000 else
|
|
if L <= $10000 then
|
|
Result := $10000 else
|
|
if L <= $100000 then
|
|
Result := $100000 else
|
|
if L <= $1000000 then
|
|
Result := $1000000
|
|
else
|
|
Result := $10000000;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ ASparseArray }
|
|
{ }
|
|
procedure ASparseArray.IndexError;
|
|
begin
|
|
raise ESparseArray.Create('Index not found');
|
|
end;
|
|
|
|
function ASparseArray.IsEmpty: Boolean;
|
|
begin
|
|
Result := GetCount = 0;
|
|
end;
|
|
|
|
|
|
|
|
{%DEFINE TSparseArrayImpl}
|
|
function TSparse%1%Array.IsEqual(const V: TObject): Boolean;
|
|
var I, J : Integer;
|
|
F, G : Integer;
|
|
P, Q : PSparse%1%Record;
|
|
begin
|
|
if V is TSparse%1%Array then
|
|
begin
|
|
if FCount <> TSparse%1%Array(V).FCount then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
for I := 0 to Length(FHashList) - 1 do
|
|
for J := 0 to Length(FHashList[I]) - 1 do
|
|
begin
|
|
Q := @FHashList[I][J];
|
|
P := TSparse%1%Array(V).LocateItemRecord(Q^.Idx, F, G);
|
|
if not Assigned(P) or (P^.Value <> Q^.Value) then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result := True;
|
|
end
|
|
else
|
|
Result := inherited IsEqual(V);
|
|
end;
|
|
|
|
function TSparse%1%Array.LocateItemRecord(const Idx: Integer;
|
|
var LookupIdx, ChainIdx: Integer): PSparse%1%Record;
|
|
var H, I, J, L : Integer;
|
|
P : TSparse%1%RecordArray;
|
|
begin
|
|
I := FHashSize;
|
|
if (I = 0) or (FCount = 0) then
|
|
begin
|
|
LookupIdx := -1;
|
|
ChainIdx := -1;
|
|
Result := nil;
|
|
exit;
|
|
end;
|
|
H := Integer(HashInteger(Idx) and (I - 1));
|
|
LookupIdx := H;
|
|
P := FHashList[H];
|
|
L := Length(P);
|
|
if L > 0 then
|
|
begin
|
|
Result := @P[0];
|
|
J := Idx;
|
|
for I := 0 to L - 1 do
|
|
if Result^.Idx = J then
|
|
begin
|
|
ChainIdx := I;
|
|
exit;
|
|
end
|
|
else
|
|
Inc(Result);
|
|
end;
|
|
Result := nil;
|
|
ChainIdx := -1;
|
|
end;
|
|
|
|
procedure TSparse%1%Array.Rehash;
|
|
var I, J, R, F, H : Integer;
|
|
N : TSparse%1%ArrayHashList;
|
|
P, Q : PSparse%1%Record;
|
|
begin
|
|
R := SparseArrayRehashSize(FCount);
|
|
SetLength(N, R);
|
|
for I := 0 to Length(FHashList) - 1 do
|
|
for J := 0 to Length(FHashList[I]) - 1 do
|
|
begin
|
|
P := @FHashList[I][J];
|
|
H := Integer(HashInteger(P^.Idx) and (R - 1));
|
|
F := Length(N[H]);
|
|
SetLength(N[H], F + 1);
|
|
Q := @N[H][F];
|
|
Q^.Idx := P^.Idx;
|
|
Q^.Value := P^.Value;
|
|
end;
|
|
FHashList := N;
|
|
FHashSize := R;
|
|
end;
|
|
|
|
function TSparse%1%Array.GetCount: Integer;
|
|
begin
|
|
Result := FCount;
|
|
end;
|
|
|
|
function TSparse%1%Array.GetItem(const Idx: Integer): %2%;
|
|
var P : PSparse%1%Record;
|
|
I, J : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if not Assigned(P) then
|
|
IndexError;
|
|
Result := P^.Value;
|
|
end;
|
|
|
|
function TSparse%1%Array.LocateItem(const Idx: Integer; var Value: %2%): Boolean;
|
|
var P : PSparse%1%Record;
|
|
I, J : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if Assigned(P) then
|
|
begin
|
|
Value := P^.Value;
|
|
Result := True;
|
|
end
|
|
else
|
|
begin
|
|
Value := %3%;
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TSparse%1%Array.SetItem(const Idx: Integer; const Value: %2%);
|
|
var P : PSparse%1%Record;
|
|
I, J : Integer;
|
|
L : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if Assigned(P) then
|
|
P^.Value := Value
|
|
else
|
|
begin
|
|
L := FHashSize;
|
|
if L = 0 then
|
|
begin
|
|
Rehash;
|
|
L := FHashSize;
|
|
Assert(L > 0);
|
|
end;
|
|
I := Integer(HashInteger(Idx) and (L - 1));
|
|
J := Length(FHashList[I]);
|
|
SetLength(FHashList[I], J + 1);
|
|
P := @FHashList[I][J];
|
|
P^.Idx := Idx;
|
|
P^.Value := Value;
|
|
Inc(FCount);
|
|
if (FCount + 1) div SparseArrayAverageHashChainSize > L then
|
|
Rehash;
|
|
end;
|
|
end;
|
|
|
|
function TSparse%1%Array.HasItem(const Idx: Integer): Boolean;
|
|
var I, J : Integer;
|
|
begin
|
|
Result := Assigned(LocateItemRecord(Idx, I, J));
|
|
end;
|
|
|
|
function TSparse%1%Array.IsEmpty: Boolean;
|
|
begin
|
|
Result := FCount = 0;
|
|
end;
|
|
|
|
function TSparse%1%Array.FindFirst(var Idx: Integer; var Value: %2%): Boolean;
|
|
var I : Integer;
|
|
P : PSparse%1%Record;
|
|
begin
|
|
for I := 0 to Length(FHashList) - 1 do
|
|
if Length(FHashList[I]) > 0 then
|
|
begin
|
|
P := @FHashList[I][0];
|
|
Idx := P^.Idx;
|
|
Value := P^.Value;
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Idx := -1;
|
|
Value := %3%;
|
|
Result := False;
|
|
end;
|
|
|
|
function TSparse%1%Array.FindNext(var Idx: Integer; var Value: %2%): Boolean;
|
|
var P : PSparse%1%Record;
|
|
I, J, L : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if not Assigned(P) then
|
|
IndexError;
|
|
Inc(J);
|
|
if J >= Length(FHashList[I]) then
|
|
begin
|
|
J := 0;
|
|
L := Length(FHashList);
|
|
Inc(I);
|
|
while I < L do
|
|
if Length(FHashList[I]) > 0 then
|
|
break
|
|
else
|
|
Inc(I);
|
|
if I >= L then
|
|
begin
|
|
Idx := -1;
|
|
Value := %3%;
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
end;
|
|
P := @FHashList[I][J];
|
|
Idx := P^.Idx;
|
|
Value := P^.Value;
|
|
Result := True;
|
|
end;
|
|
{%ENDDEF}
|
|
{$IFDEF SupportAnsiString}
|
|
{ }
|
|
{ TSparseAnsiStringArray }
|
|
{ }
|
|
procedure TSparseAnsiStringArray.Assign(const Source: TObject);
|
|
var I, L : Integer;
|
|
begin
|
|
if Source is TSparseAnsiStringArray then
|
|
begin
|
|
Clear;
|
|
L := Length(TSparseAnsiStringArray(Source).FHashList);
|
|
SetLength(FHashList, L);
|
|
for I := 0 to L - 1 do
|
|
FHashList[I] := Copy(TSparseAnsiStringArray(Source).FHashList[I]);
|
|
FHashSize := TSparseAnsiStringArray(Source).FHashSize;
|
|
FCount := TSparseAnsiStringArray(Source).FCount;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TSparseAnsiStringArray.Clear;
|
|
begin
|
|
FHashList := nil;
|
|
FHashSize := 0;
|
|
FCount := 0;
|
|
end;
|
|
|
|
{%TEMPLATE TSparseArrayImpl 'AnsiString' 'AnsiString' ''''''}
|
|
procedure TSparseAnsiStringArray.Delete(const Idx: Integer);
|
|
var P : PSparseAnsiStringRecord;
|
|
I, J : Integer;
|
|
L : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if not Assigned(P) then
|
|
IndexError;
|
|
P^.Value := '';
|
|
L := Length(FHashList[I]);
|
|
if J < L - 1 then
|
|
begin
|
|
Move(FHashList[I][J + 1], FHashList[I][J], (L - J - 1) * Sizeof(TSparseAnsiStringRecord));
|
|
ZeroMem(FHashList[I][L - 1], Sizeof(TSparseAnsiStringRecord));
|
|
end;
|
|
SetLength(FHashList[I], L - 1);
|
|
Dec(FCount);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ TSparseInt64Array }
|
|
{ }
|
|
procedure TSparseInt64Array.Assign(const Source: TObject);
|
|
var I, L : Integer;
|
|
begin
|
|
if Source is TSparseInt64Array then
|
|
begin
|
|
Clear;
|
|
L := Length(TSparseInt64Array(Source).FHashList);
|
|
SetLength(FHashList, L);
|
|
for I := 0 to L - 1 do
|
|
FHashList[I] := Copy(TSparseInt64Array(Source).FHashList[I]);
|
|
FHashSize := TSparseInt64Array(Source).FHashSize;
|
|
FCount := TSparseInt64Array(Source).FCount;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TSparseInt64Array.Clear;
|
|
begin
|
|
FHashList := nil;
|
|
FHashSize := 0;
|
|
FCount := 0;
|
|
end;
|
|
|
|
{%TEMPLATE TSparseArrayImpl 'Int64' 'Int64' '0'}
|
|
procedure TSparseInt64Array.Delete(const Idx: Integer);
|
|
var P : PSparseInt64Record;
|
|
I, J : Integer;
|
|
L : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if not Assigned(P) then
|
|
IndexError;
|
|
L := Length(FHashList[I]);
|
|
if J < L - 1 then
|
|
Move(FHashList[I][J + 1], FHashList[I][J], (L - J - 1) * Sizeof(TSparseInt64Record));
|
|
SetLength(FHashList[I], L - 1);
|
|
Dec(FCount);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ TSparseExtendedArray }
|
|
{ }
|
|
procedure TSparseExtendedArray.Assign(const Source: TObject);
|
|
var I, L : Integer;
|
|
begin
|
|
if Source is TSparseExtendedArray then
|
|
begin
|
|
Clear;
|
|
L := Length(TSparseExtendedArray(Source).FHashList);
|
|
SetLength(FHashList, L);
|
|
for I := 0 to L - 1 do
|
|
FHashList[I] := Copy(TSparseExtendedArray(Source).FHashList[I]);
|
|
FHashSize := TSparseExtendedArray(Source).FHashSize;
|
|
FCount := TSparseExtendedArray(Source).FCount;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TSparseExtendedArray.Clear;
|
|
begin
|
|
FHashList := nil;
|
|
FHashSize := 0;
|
|
FCount := 0;
|
|
end;
|
|
|
|
{%TEMPLATE TSparseArrayImpl 'Extended' 'Extended' '0.0'}
|
|
procedure TSparseExtendedArray.Delete(const Idx: Integer);
|
|
var P : PSparseExtendedRecord;
|
|
I, J : Integer;
|
|
L : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if not Assigned(P) then
|
|
IndexError;
|
|
L := Length(FHashList[I]);
|
|
if J < L - 1 then
|
|
Move(FHashList[I][J + 1], FHashList[I][J], (L - J - 1) * Sizeof(TSparseExtendedRecord));
|
|
SetLength(FHashList[I], L - 1);
|
|
Dec(FCount);
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ TSparseObjectArray }
|
|
{ }
|
|
constructor TSparseObjectArray.Create(const AIsItemOwner: Boolean);
|
|
begin
|
|
inherited Create;
|
|
FIsItemOwner := AIsItemOwner;
|
|
end;
|
|
|
|
destructor TSparseObjectArray.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSparseObjectArray.Init;
|
|
begin
|
|
inherited Init;
|
|
FIsItemOwner := False;
|
|
end;
|
|
|
|
procedure TSparseObjectArray.Assign(const Source: TObject);
|
|
var I, L : Integer;
|
|
begin
|
|
if Source is TSparseObjectArray then
|
|
begin
|
|
Clear;
|
|
L := Length(TSparseObjectArray(Source).FHashList);
|
|
SetLength(FHashList, L);
|
|
for I := 0 to L - 1 do
|
|
FHashList[I] := Copy(TSparseObjectArray(Source).FHashList[I]);
|
|
FHashSize := TSparseObjectArray(Source).FHashSize;
|
|
FCount := TSparseObjectArray(Source).FCount;
|
|
FIsItemOwner := False;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TSparseObjectArray.Clear;
|
|
var I, J : Integer;
|
|
begin
|
|
if FIsItemOwner then
|
|
for I := 0 to Length(FHashList) - 1 do
|
|
for J := 0 to Length(FHashList[I]) - 1 do
|
|
FreeAndNil(FHashList[I][J].Value);
|
|
FHashList := nil;
|
|
FHashSize := 0;
|
|
FCount := 0;
|
|
end;
|
|
|
|
{%TEMPLATE TSparseArrayImpl 'Object' 'TObject' 'nil' }
|
|
procedure TSparseObjectArray.Delete(const Idx: Integer);
|
|
var P : PSparseObjectRecord;
|
|
I, J : Integer;
|
|
L : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if not Assigned(P) then
|
|
IndexError;
|
|
if FIsItemOwner then
|
|
FreeAndNil(P^.Value);
|
|
L := Length(FHashList[I]);
|
|
if J < L - 1 then
|
|
Move(FHashList[I][J + 1], FHashList[I][J], (L - J - 1) * Sizeof(TSparseObjectRecord));
|
|
SetLength(FHashList[I], L - 1);
|
|
Dec(FCount);
|
|
end;
|
|
|
|
function TSparseObjectArray.ReleaseItem(const Idx: Integer): TObject;
|
|
var P : PSparseObjectRecord;
|
|
I, J : Integer;
|
|
begin
|
|
P := LocateItemRecord(Idx, I, J);
|
|
if not Assigned(P) then
|
|
IndexError;
|
|
Result := P^.Value;
|
|
P^.Value := nil;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ TDoublyLinkedItem }
|
|
{ }
|
|
function TDoublyLinkedItem.HasNext: Boolean;
|
|
begin
|
|
Result := Assigned(Next);
|
|
end;
|
|
|
|
function TDoublyLinkedItem.Last: TDoublyLinkedItem;
|
|
var P : TDoublyLinkedItem;
|
|
begin
|
|
P := self;
|
|
repeat
|
|
Result := P;
|
|
P := P.Next;
|
|
until not Assigned(P);
|
|
end;
|
|
|
|
function TDoublyLinkedItem.Count: Integer;
|
|
var N : TDoublyLinkedItem;
|
|
begin
|
|
Result := 1;
|
|
N := FNext;
|
|
while Assigned(N) do
|
|
begin
|
|
Inc(Result);
|
|
N := N.Next;
|
|
end;
|
|
end;
|
|
|
|
function TDoublyLinkedItem.HasPrev: Boolean;
|
|
begin
|
|
Result := Assigned(FPrev);
|
|
end;
|
|
|
|
function TDoublyLinkedItem.First: TDoublyLinkedItem;
|
|
var P : TDoublyLinkedItem;
|
|
begin
|
|
P := self;
|
|
repeat
|
|
Result := P;
|
|
P := P.Prev;
|
|
until not Assigned(P);
|
|
end;
|
|
|
|
procedure TDoublyLinkedItem.Delete;
|
|
begin
|
|
Remove;
|
|
Free;
|
|
end;
|
|
|
|
procedure TDoublyLinkedItem.Remove;
|
|
begin
|
|
if Assigned(Next) then
|
|
Next.Prev := FPrev;
|
|
if Assigned(Prev) then
|
|
Prev.Next := FNext;
|
|
end;
|
|
|
|
function TDoublyLinkedItem.RemoveNext: TDoublyLinkedItem;
|
|
begin
|
|
Result := FNext;
|
|
if Assigned(Result) then
|
|
begin
|
|
FNext := Result.Next;
|
|
if Assigned(FNext) then
|
|
FNext.Prev := self;
|
|
end;
|
|
end;
|
|
|
|
procedure TDoublyLinkedItem.DeleteNext;
|
|
begin
|
|
RemoveNext.Free;
|
|
end;
|
|
|
|
function TDoublyLinkedItem.RemovePrev: TDoublyLinkedItem;
|
|
begin
|
|
Result := FPrev;
|
|
if Assigned(Result) then
|
|
begin
|
|
FPrev := Result.Prev;
|
|
if Assigned(FPrev) then
|
|
FPrev.Next := self;
|
|
end;
|
|
end;
|
|
|
|
procedure TDoublyLinkedItem.DeletePrev;
|
|
begin
|
|
RemovePrev.Free;
|
|
end;
|
|
|
|
procedure TDoublyLinkedItem.InsertAfter(const Item: TDoublyLinkedItem);
|
|
begin
|
|
Assert(Assigned(Item));
|
|
Item.Next := FNext;
|
|
Item.Prev := self;
|
|
if Assigned(FNext) then
|
|
FNext.Prev := Item;
|
|
FNext := Item;
|
|
end;
|
|
|
|
procedure TDoublyLinkedItem.InsertBefore(const Item: TDoublyLinkedItem);
|
|
begin
|
|
Assert(Assigned(Item));
|
|
Item.Next := self;
|
|
Item.Prev := FPrev;
|
|
if Assigned(FPrev) then
|
|
FPrev.Next := Item;
|
|
FPrev := Item;
|
|
end;
|
|
|
|
destructor TDoublyLinkedItem.DestroyList;
|
|
var N : TDoublyLinkedItem;
|
|
begin
|
|
while Assigned(FNext) do
|
|
begin
|
|
N := FNext;
|
|
FNext := N.Next;
|
|
N.Free;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
|
|
|
|
{%DEFINE LinkedItemImpl}
|
|
{ }
|
|
{-TDoublyLinked%1% }
|
|
{ }
|
|
constructor TDoublyLinked%1%.Create(const V: %2%);
|
|
begin
|
|
inherited Create;
|
|
Value := V;
|
|
end;
|
|
|
|
procedure TDoublyLinked%1%.InsertAfter(const V: %2%);
|
|
begin
|
|
inherited InsertAfter(TDoublyLinked%1%.Create(V));
|
|
end;
|
|
|
|
procedure TDoublyLinked%1%.InsertBefore(const V: %2%);
|
|
begin
|
|
inherited InsertBefore(TDoublyLinked%1%.Create(V));
|
|
end;
|
|
|
|
procedure TDoublyLinked%1%.InsertFirst(const V: %2%);
|
|
begin
|
|
TDoublyLinked%1%(First).InsertBefore(V);
|
|
end;
|
|
|
|
procedure TDoublyLinked%1%.Append(const V: %2%);
|
|
begin
|
|
TDoublyLinked%1%(Last).InsertAfter(V);
|
|
end;
|
|
|
|
function TDoublyLinked%1%.FindNext(const Find: %2%): TDoublyLinked%1%;
|
|
begin
|
|
Result := self;
|
|
repeat
|
|
if Result.Value = Find then
|
|
exit;
|
|
Result := TDoublyLinked%1%(Result.Next);
|
|
until not Assigned(Result);
|
|
end;
|
|
|
|
function TDoublyLinked%1%.FindPrev(const Find: %2%): TDoublyLinked%1%;
|
|
begin
|
|
Result := self;
|
|
repeat
|
|
if Result.Value = Find then
|
|
exit;
|
|
Result := TDoublyLinked%1%(Result.Prev);
|
|
until not Assigned(Result);
|
|
end;
|
|
|
|
|
|
{%ENDDEF}
|
|
{%TEMPLATE LinkedItemImpl 'Integer' 'Integer'}
|
|
{%TEMPLATE LinkedItemImpl 'Extended' 'Extended'}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE LinkedItemImpl 'String' 'AnsiString'}
|
|
{$ENDIF}
|
|
{%TEMPLATE LinkedItemImpl 'Object' 'TObject'}
|
|
{ }
|
|
{ Open array to Linked list }
|
|
{ }
|
|
{%DEFINE OpenArrayToLinkedListImpl}
|
|
function As%2%Linked%3%List(const V: Array of %1%): T%2%Linked%3%;
|
|
var I, L : T%2%Linked%3%;
|
|
F : Integer;
|
|
begin
|
|
Result := nil;
|
|
L := nil;
|
|
for F := 0 to High(V) do
|
|
begin
|
|
I := T%2%Linked%3%.Create(V [F]);
|
|
if not Assigned(L) then
|
|
begin
|
|
L := I;
|
|
Result := I;
|
|
end else
|
|
begin
|
|
L.InsertAfter(I);
|
|
L := I;
|
|
end;
|
|
end;
|
|
end;
|
|
{%ENDDEF}
|
|
{%TEMPLATE OpenArrayToLinkedListImpl 'Integer' 'Doubly' 'Integer'}
|
|
{%TEMPLATE OpenArrayToLinkedListImpl 'Extended' 'Doubly' 'Extended'}
|
|
{$IFDEF SupportAnsiString}
|
|
{%TEMPLATE OpenArrayToLinkedListImpl 'AnsiString' 'Doubly' 'String'}
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ TDoublyLinkedList }
|
|
{ }
|
|
Destructor TDoublyLinkedList.Destroy;
|
|
begin
|
|
DeleteList;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TDoublyLinkedList.IsEmpty: Boolean;
|
|
begin
|
|
Result := not Assigned(FFirst);
|
|
end;
|
|
|
|
procedure TDoublyLinkedList.Append(const Item: TDoublyLinkedItem);
|
|
begin
|
|
if not Assigned(Item) then
|
|
exit;
|
|
if not Assigned(FLast) then
|
|
begin
|
|
FFirst := Item;
|
|
FLast := Item;
|
|
Item.Prev := nil;
|
|
Item.Next := nil;
|
|
end else
|
|
begin
|
|
FLast.InsertAfter(Item);
|
|
FLast := Item;
|
|
end;
|
|
Inc(FCount);
|
|
end;
|
|
|
|
procedure TDoublyLinkedList.InsertFront(const Item: TDoublyLinkedItem);
|
|
begin
|
|
if not Assigned(Item) then
|
|
exit;
|
|
if not Assigned(FFirst) then
|
|
begin
|
|
FFirst := Item;
|
|
FLast := Item;
|
|
Item.Prev := nil;
|
|
Item.Next := nil;
|
|
end else
|
|
begin
|
|
FFirst.InsertBefore(Item);
|
|
FFirst := Item;
|
|
end;
|
|
Inc(FCount);
|
|
end;
|
|
|
|
procedure TDoublyLinkedList.Remove(const Item: TDoublyLinkedItem);
|
|
begin
|
|
if not Assigned(Item) then
|
|
exit;
|
|
if FFirst = Item then
|
|
FFirst := Item.Next;
|
|
if FLast = Item then
|
|
FLast := Item.Prev;
|
|
Item.Remove;
|
|
Dec(FCount);
|
|
end;
|
|
|
|
function TDoublyLinkedList.RemoveFirst: TDoublyLinkedItem;
|
|
var N : TDoublyLinkedItem;
|
|
begin
|
|
Result := FFirst;
|
|
if not Assigned(Result) then
|
|
exit;
|
|
if Result = FLast then
|
|
begin
|
|
FFirst := nil;
|
|
FLast := nil;
|
|
end else
|
|
begin
|
|
N := Result.Next;
|
|
Result.Remove;
|
|
FFirst := N;
|
|
end;
|
|
Dec(FCount);
|
|
end;
|
|
|
|
function TDoublyLinkedList.RemoveLast: TDoublyLinkedItem;
|
|
var P : TDoublyLinkedItem;
|
|
begin
|
|
Result := FLast;
|
|
if not Assigned(Result) then
|
|
exit;
|
|
if Result = FFirst then
|
|
begin
|
|
FFirst := nil;
|
|
FLast := nil;
|
|
end
|
|
else
|
|
begin
|
|
P := Result.Prev;
|
|
Result.Remove;
|
|
FLast := P;
|
|
end;
|
|
Dec(FCount);
|
|
end;
|
|
|
|
procedure TDoublyLinkedList.Delete(const Item: TDoublyLinkedItem);
|
|
begin
|
|
Remove(Item);
|
|
Item.Free;
|
|
end;
|
|
|
|
procedure TDoublyLinkedList.DeleteFirst;
|
|
begin
|
|
RemoveFirst.Free;
|
|
end;
|
|
|
|
procedure TDoublyLinkedList.DeleteLast;
|
|
begin
|
|
RemoveLast.Free;
|
|
end;
|
|
|
|
procedure TDoublyLinkedList.DeleteList;
|
|
var F : TDoublyLinkedItem;
|
|
begin
|
|
F := FFirst;
|
|
FFirst := nil;
|
|
FLast := nil;
|
|
if Assigned(F) then
|
|
F.DestroyList;
|
|
FCount := 0;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ Self testing code }
|
|
{ }
|
|
{$IFDEF DEBUG}
|
|
{$IFDEF TEST}
|
|
{$ASSERTIONS ON}
|
|
procedure Test_Array;
|
|
var I : Integer;
|
|
F : TIntegerArray;
|
|
begin
|
|
// TIntegerArray
|
|
F := TIntegerArray.Create;
|
|
for I := 0 to 16384 do
|
|
Assert(F.AppendItem(I) = I, 'Array.AppendItem');
|
|
Assert(F.Count = 16385, 'Array.Count');
|
|
for I := 0 to 16384 do
|
|
Assert(F[I] = I, 'Array.GetItem');
|
|
for I := 0 to 16384 do
|
|
F[I] := I + 1;
|
|
for I := 0 to 16384 do
|
|
Assert(F[I] = I + 1, 'Array.SetItem');
|
|
F.Delete(0, 1);
|
|
Assert(F.Count = 16384, 'Array.Delete');
|
|
for I := 0 to 16383 do
|
|
Assert(F[I] = I + 2, 'Array.Delete');
|
|
F.Insert(0, 2);
|
|
F[0] := 0;
|
|
F[1] := 1;
|
|
for I := 0 to 16384 do
|
|
Assert(F[I] = I, 'Array.Insert');
|
|
|
|
F.Count := 4;
|
|
Assert(F.Count = 4, 'Array.SetCount');
|
|
F[0] := 9;
|
|
F[1] := -2;
|
|
F[2] := 3;
|
|
F[3] := 4;
|
|
F.Sort;
|
|
Assert(F[0] = -2, 'Array.Sort');
|
|
Assert(F[1] = 3, 'Array.Sort');
|
|
Assert(F[2] = 4, 'Array.Sort');
|
|
Assert(F[3] = 9, 'Array.Sort');
|
|
|
|
F.Count := 7;
|
|
F[0] := 3;
|
|
F[1] := 5;
|
|
F[2] := 5;
|
|
F[3] := 2;
|
|
F[4] := 5;
|
|
F[5] := 5;
|
|
F[6] := 1;
|
|
F.Sort;
|
|
Assert(F[0] = 1, 'Array.Sort');
|
|
Assert(F[1] = 2, 'Array.Sort');
|
|
Assert(F[2] = 3, 'Array.Sort');
|
|
Assert(F[3] = 5, 'Array.Sort');
|
|
Assert(F[4] = 5, 'Array.Sort');
|
|
Assert(F[5] = 5, 'Array.Sort');
|
|
Assert(F[6] = 5, 'Array.Sort');
|
|
|
|
F.Count := 7;
|
|
F[0] := 1;
|
|
F[1] := 5;
|
|
F[2] := 5;
|
|
F[3] := 1;
|
|
F[4] := 5;
|
|
F[5] := 2;
|
|
F[6] := 1;
|
|
F.RemoveDuplicates(False);
|
|
Assert(F.Count = 3, 'Array.RemoveDuplicates');
|
|
Assert(F[0] = 1, 'Array.RemoveDuplicates');
|
|
Assert(F[1] = 5, 'Array.RemoveDuplicates');
|
|
Assert(F[2] = 2, 'Array.RemoveDuplicates');
|
|
|
|
F.Count := 7;
|
|
F[0] := 1;
|
|
F[1] := 1;
|
|
F[2] := 1;
|
|
F[3] := 2;
|
|
F[4] := 5;
|
|
F[5] := 5;
|
|
F[6] := 5;
|
|
F.RemoveDuplicates(True);
|
|
Assert(F.Count = 3, 'Array.RemoveDuplicates');
|
|
Assert(F[0] = 1, 'Array.RemoveDuplicates');
|
|
Assert(F[1] = 2, 'Array.RemoveDuplicates');
|
|
Assert(F[2] = 5, 'Array.RemoveDuplicates');
|
|
|
|
F.Clear;
|
|
Assert(F.Count = 0, 'Array.Clear');
|
|
F.Free;
|
|
end;
|
|
|
|
procedure Test_Dictionary;
|
|
{$IFDEF SupportAnsiString}
|
|
var F : TIntegerDictionaryA;
|
|
G : TStringDictionaryA;
|
|
I : Integer;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF SupportAnsiString}
|
|
F := TIntegerDictionaryA.Create;
|
|
for I := 0 to 16384 do
|
|
F.Add(IntToStringA(I), I);
|
|
Assert(F.Count = 16385, 'Dictionary.Count');
|
|
for I := 0 to 16384 do
|
|
Assert(F.GetKeyByIndex(I) = IntToStringA(I), 'Dictionary.GetKeyByIndex');
|
|
for I := 0 to 16384 do
|
|
Assert(F[IntToStringA(I)] = I, 'Dictionary.GetItem');
|
|
Assert(F['0'] = 0, 'Dictionary.GetItem');
|
|
Assert(F['4001'] = 4001, 'Dictionary.GetItem');
|
|
Assert(F['16384'] = 16384, 'Dictionary.GetItem');
|
|
for I := 0 to 16384 do
|
|
Assert(F.GetItemByIndex(I) = I, 'Dictionary.GetItemByIndex');
|
|
Assert(F.HasKey('5'), 'Dictionary.HasKey');
|
|
Assert(not F.HasKey('X'), 'Dictionary.HasKey');
|
|
F.Rename('5', 'X');
|
|
Assert(not F.HasKey('5'), 'Dictionary.Rename');
|
|
Assert(F.HasKey('X'), 'Dictionary.Rename');
|
|
Assert(F['X'] = 5, 'Dictionary.Rename');
|
|
F.Delete('X');
|
|
Assert(not F.HasKey('X'), 'Dictionary.Delete');
|
|
Assert(F.Count = 16384, 'Dictionary.Delete');
|
|
F.Delete('0');
|
|
Assert(not F.HasKey('0'), 'Dictionary.Delete');
|
|
Assert(F.Count = 16383, 'Dictionary.Delete');
|
|
F.DeleteItemByIndex(0);
|
|
Assert(not F.HasKey('1'), 'Dictionary.DeleteItemByIndex');
|
|
Assert(F.Count = 16382, 'Dictionary.DeleteItemByIndex');
|
|
F.Free;
|
|
|
|
G := TStringDictionaryA.Create;
|
|
for I := 0 to 16384 do
|
|
G.Add(IntToStringA(I), IntToStr(I));
|
|
Assert(G.Count = 16385, 'Dictionary.Count');
|
|
for I := 0 to 16384 do
|
|
Assert(G.GetKeyByIndex(I) = IntToStringA(I), 'Dictionary.GetKeyByIndex');
|
|
Assert(G['0'] = '0', 'Dictionary.GetItem');
|
|
Assert(G['5'] = '5', 'Dictionary.GetItem');
|
|
Assert(G['16384'] = '16384', 'Dictionary.GetItem');
|
|
for I := 0 to 16384 do
|
|
Assert(G.GetItemByIndex(I) = IntToStr(I), 'Dictionary.GetItemByIndex');
|
|
Assert(G.HasKey('5'), 'Dictionary.HasKey');
|
|
Assert(not G.HasKey('X'), 'Dictionary.HasKey');
|
|
G.Rename('5', 'X');
|
|
Assert(not G.HasKey('5'), 'Dictionary.Rename');
|
|
Assert(G.HasKey('X'), 'Dictionary.Rename');
|
|
Assert(G['X'] = '5', 'Dictionary.Rename');
|
|
G.Delete('X');
|
|
Assert(not G.HasKey('X'), 'Dictionary.Delete');
|
|
Assert(G.Count = 16384, 'Dictionary.Delete');
|
|
G.Delete('0');
|
|
Assert(not G.HasKey('0'), 'Dictionary.Delete');
|
|
Assert(G.Count = 16383, 'Dictionary.Delete');
|
|
G.DeleteItemByIndex(0);
|
|
Assert(not G.HasKey('1'), 'Dictionary.DeleteItemByIndex');
|
|
Assert(G.Count = 16382, 'Dictionary.DeleteItemByIndex');
|
|
G.Free;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure Test_SparseArray;
|
|
var A, D : TSparseObjectArray;
|
|
B : Array[0..2] of TObject;
|
|
I, J : Integer;
|
|
V : TObject;
|
|
{$IFDEF SupportAnsiString}
|
|
S, T : TSparseAnsiStringArray;
|
|
{$ENDIF}
|
|
begin
|
|
B[0] := TObject.Create;
|
|
B[1] := TObject.Create;
|
|
B[2] := TObject.Create;
|
|
A := TSparseObjectArray.Create;
|
|
try
|
|
Assert(A.Count = 0);
|
|
Assert(A.IsEmpty);
|
|
Assert(not A.FindFirst(I, V));
|
|
Assert(A.IsEqual(A));
|
|
Assert(not A.LocateItem(0, V));
|
|
Assert(not Assigned(V));
|
|
A[100] := B[0];
|
|
Assert(A.Count = 1);
|
|
Assert(not A.IsEmpty);
|
|
Assert(A[100] = B[0]);
|
|
Assert(not A.LocateItem(0, V));
|
|
Assert(A.LocateItem(100, V));
|
|
Assert(V = B[0]);
|
|
Assert(not A.HasItem(1000));
|
|
A[1000] := B[1];
|
|
Assert(A.HasItem(1000));
|
|
Assert(A.Count = 2);
|
|
Assert(A[1000] = B[1]);
|
|
A[-50000] := B[2];
|
|
Assert(A.Count = 3);
|
|
Assert(A[100] = B[0]);
|
|
Assert(A[1000] = B[1]);
|
|
Assert(A[-50000] = B[2]);
|
|
Assert(A.IsEqual(A));
|
|
A[100] := B[1];
|
|
Assert(A[100] = B[1]);
|
|
A.Delete(1000);
|
|
Assert(A.Count = 2);
|
|
Assert(not A.HasItem(1000));
|
|
Assert(A.FindFirst(I, V));
|
|
Assert((I = 100) or (I = -50000));
|
|
J := I;
|
|
Assert(A.FindNext(I, V));
|
|
Assert(((I = 100) or (I = -50000)) and (I <> J));
|
|
Assert(not A.FindNext(I, V));
|
|
A.Clear;
|
|
Assert(A.Count = 0);
|
|
Assert(A.IsEmpty);
|
|
Assert(not A.FindFirst(I, V));
|
|
|
|
A[0] := B[0];
|
|
A[-10] := B[1];
|
|
A[20] := B[2];
|
|
Assert(A.Count = 3);
|
|
Assert((A[0] = B[0]) and (A[-10] = B[1]) and (A[20] = B[2]));
|
|
D := A.Duplicate as TSparseObjectArray;
|
|
Assert(D.Count = 3);
|
|
Assert((D[0] = B[0]) and (D[-10] = B[1]) and (D[20] = B[2]));
|
|
Assert(A.IsEqual(D));
|
|
Assert(D.IsEqual(A));
|
|
D[0] := B[1];
|
|
Assert(not A.IsEqual(D));
|
|
Assert(not D.IsEqual(A));
|
|
D[1] := B[1];
|
|
Assert(not A.IsEqual(D));
|
|
Assert(D.Count = 4);
|
|
Assert((D[0] = B[1]) and (D[1] = B[1]));
|
|
Assert(A.Count = 3);
|
|
Assert((A[0] = B[0]) and (A[-10] = B[1]) and (A[20] = B[2]));
|
|
Assert(not A.HasItem(1));
|
|
D.Delete(1);
|
|
Assert(D.Count = 3);
|
|
Assert(not D.HasItem(1));
|
|
D[0] := B[0];
|
|
Assert(D.IsEqual(A));
|
|
D.Free;
|
|
Assert((A[0] = B[0]) and (A[-10] = B[1]) and (A[20] = B[2]));
|
|
finally
|
|
A.Free;
|
|
B[2].Free;
|
|
B[1].Free;
|
|
B[0].Free;
|
|
end;
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
S := TSparseAnsiStringArray.Create;
|
|
T := TSparseAnsiStringArray.Create;
|
|
try
|
|
Assert(S.IsEmpty);
|
|
Assert(S.Count = 0);
|
|
Assert(S.IsEqual(T));
|
|
for I := 1 to 1000 do
|
|
begin
|
|
S[I * 3] := IntToStringA(I);
|
|
T[I] := IntToStringA(I);
|
|
Assert(S.HasItem(I * 3));
|
|
Assert(not S.HasItem(I * 3 + 1));
|
|
end;
|
|
Assert(S.Count = 1000);
|
|
Assert(T.Count = 1000);
|
|
for I := 1 to 1000 do
|
|
begin
|
|
Assert(S[I * 3] = IntToStringA(I));
|
|
Assert(T[I] = IntToStringA(I));
|
|
end;
|
|
for I := 1 to 1000 do
|
|
begin
|
|
S[I * 3] := IntToStringA(I + 1);
|
|
S[I * 3 - 1] := IntToStringA(I);
|
|
T[1000 + I * 2] := IntToStringA(I);
|
|
end;
|
|
Assert(S.Count = 2000);
|
|
Assert(T.Count = 2000);
|
|
for I := 1 to 1000 do
|
|
begin
|
|
Assert(S[I * 3] = IntToStringA(I + 1));
|
|
Assert(S[I * 3 - 1] = IntToStringA(I));
|
|
Assert(T[I] = IntToStringA(I));
|
|
Assert(T[1000 + I * 2] = IntToStringA(I));
|
|
end;
|
|
Assert(not S.IsEqual(T));
|
|
S.Clear;
|
|
Assert(S.Count = 0);
|
|
finally
|
|
FreeAndNil(T);
|
|
FreeAndNil(S);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure Test_HashedStringArray;
|
|
{$IFDEF SupportAnsiString}
|
|
var A : THashedAnsiStringArray;
|
|
B : THashedRawByteStringArray;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF SupportAnsiString}
|
|
A := THashedAnsiStringArray.Create(True);
|
|
try
|
|
A.AppendItem('abc');
|
|
Assert(A.Count = 1);
|
|
A.Clear;
|
|
Assert(A.Count = 0);
|
|
A.AppendItem('def');
|
|
Assert(A.Count = 1);
|
|
finally
|
|
A.Free;
|
|
end;
|
|
//
|
|
A := THashedAnsiStringArray.Create(False);
|
|
try
|
|
A.AppendItem('123');
|
|
A.AppendItem('267');
|
|
A.AppendItem('328');
|
|
A.AppendItem('423');
|
|
A.AppendItem('523a');
|
|
Assert(A.PosNext('123') = 0);
|
|
Assert(A.PosNext('423') = 3);
|
|
A.Delete(0);
|
|
Assert(A.PosNext('123') = -1);
|
|
Assert(A.PosNext('423') = 2);
|
|
finally
|
|
A.Free;
|
|
end;
|
|
|
|
B := THashedRawByteStringArray.Create(True);
|
|
try
|
|
B.AppendItem('abc');
|
|
Assert(B.Count = 1);
|
|
B.Clear;
|
|
Assert(B.Count = 0);
|
|
B.AppendItem('def');
|
|
Assert(B.Count = 1);
|
|
finally
|
|
B.Free;
|
|
end;
|
|
//
|
|
B := THashedRawByteStringArray.Create(False);
|
|
try
|
|
B.AppendItem('123');
|
|
B.AppendItem('267');
|
|
B.AppendItem('328');
|
|
B.AppendItem('423');
|
|
B.AppendItem('523a');
|
|
Assert(B.PosNext('123') = 0);
|
|
Assert(B.PosNext('423') = 3);
|
|
B.Delete(0);
|
|
Assert(B.PosNext('123') = -1);
|
|
Assert(B.PosNext('423') = 2);
|
|
finally
|
|
B.Free;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure Test;
|
|
begin
|
|
Test_Array;
|
|
Test_Dictionary;
|
|
Test_SparseArray;
|
|
Test_HashedStringArray;
|
|
end;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
end.
|
|
|