
Purpose: Framework Core Low-Level Data Processing Functions
- this unit is a part of the Open Source Synopse mORMot framework 2, licensed under a MPL/GPL/LGPL three license - see LICENSE.md
| Unit Name | Description | |
|---|---|---|
| mormot.core.base | Framework Core Shared Types and RTL-like Functions | |
| mormot.core.buffers | Framework Core Low-Level Memory Buffer Process | |
| mormot.core.datetime | Framework Core Low-Level Date and Time Support | |
| mormot.core.os | Framework Core Low-Level Wrappers to the Operating-System API | |
| mormot.core.rtti | Framework Core Low-Level Cross-Compiler RTTI Definitions | |
| mormot.core.text | Framework Core Low-Level Text Processing | |
| mormot.core.unicode | Framework Core Low-Level Unicode UTF-8 UTF-16 Ansi Conversion |
| Objects | Description | |
|---|---|---|
| EDynArray | The kind of exceptions raised during TDynArray/TDynArrayHashed process | |
| IAutoFree | Interface for TAutoFree to register another TObject instance to an existing IAutoFree local variable | |
| IAutoLocker | An interface used by TAutoLocker to protect multi-thread execution | |
| TAutoFree | Simple reference-counted storage for local objects | |
| TAutoLocker | Reference-counted block code critical section | |
| TDynArray | A wrapper around a dynamic array with one dimension | |
| TDynArrayHashed | Used to access any dynamic arrray items using fast hash | |
| TDynArrayHasher | Implements O(1) lookup to any dynamic array content | |
| TDynArrayLocked | Just a wrapper record to join a TDynArray, its Count and a TRWLightLock | |
| TInterfacedCollection | Any TCollection used between client and server shall inherit from this class | |
| TInterfacedObjectLocked | Adding locking methods to a TInterfacedObject with virtual constructor | |
| TInterfacedObjectRWLocked | Adding light locking methods to a TInterfacedObject with virtual constructor | |
| TInterfacedObjectWithCustomCreate | Abstract parent class with threadsafe implementation of IInterface and a virtual constructor | |
| TPersistentWithCustomCreate | Abstract parent class with a virtual constructor, ready to be overridden to initialize the instance | |
| TRadixTree | Implement an abstract Radix Tree over UTF-8 case-insensitive text | |
| TRadixTreeNode | Implement an abstract Radix Tree node | |
| TRadixTreeNodeParams | Implement an abstract Radix Tree static or <param> node | |
| TRadixTreeParams | Implement an abstract Radix Tree with static or <param> nodes | |
| TRawUtf8Hashed | Store a TRawUtf8DynArray with its efficient hash table | |
| TRawUtf8Interning | Allow to store only one copy of distinct RawUtf8 values | |
| TRawUtf8InterningAbstract | Defined here as forward definition of the TRawUtf8Interning final class | |
| TRawUtf8InterningSlot | Used to store one list of hashed RawUtf8 in TRawUtf8Interning pool | |
| TRawUtf8List | Thread-safe TStringList-class optimized for our native UTF-8 string type | |
| TRawUtf8ListLocked | Some declarations used for backward compatibility only | |
| TSynInterfacedObject | An abstract ancestor, for implementing a custom TInterfacedObject like class | |
| TSynList | Simple and efficient TList, without any notification | |
| TSynObjectList | Simple and efficient TObjectList, without any notification | |
| TSynObjectListLightLocked | Add TRWLightLock non-upgradable methods to a TSynObjectList | |
| TSynObjectListLocked | Add TRWLock upgradable methods to a TSynObjectList | |
| TSynObjectListSorted | An ordered thread-safe TSynObjectList | |
| TSynPersistent | Our own empowered TPersistent-like parent class | |
| TSynPersistentLock | Adding locking methods to a TSynPersistent with virtual constructor | |
| TSynPersistentLocked | Used for backward compatibility only with existing code | |
| TSynPersistentRWLightLock | Adding light non-upgradable multiple Read / exclusive Write locking methods to a TSynPersistent with virtual constructor | |
| TSynPersistentRWLock | Adding light upgradable multiple Read / exclusive Write locking methods to a TSynPersistent with virtual constructor | |
| TSynPersistentStore | Abstract high-level handling of (SynLZ-)compressed persisted storage |
TPersistentWithCustomCreate = class(TPersistent)
Abstract parent class with a virtual constructor, ready to be overridden to initialize the instance
- you can specify such a class if you need an object including published properties (like TPersistent) with a virtual constructor (e.g. to initialize some nested class properties)
constructor Create; virtual;
This virtual constructor will be called at instance creation
- this constructor does nothing, but is declared as virtual so that inherited classes may safely override this default void implementation
TInterfacedObjectWithCustomCreate = class(TInterfacedObject)
Abstract parent class with threadsafe implementation of IInterface and a virtual constructor
- you can specify e.g. such a class to TRestServer.ServiceRegister() if you need an interfaced object with a virtual constructor, ready to be overridden to initialize the instance
constructor Create; virtual;
This virtual constructor will be called at instance creation
- this constructor does nothing, but is declared as virtual so that inherited classes may safely override this default void implementation
procedure RefCountUpdate(Release: boolean); virtual;
Used to mimic TInterfacedObject reference counting
- Release=true will call TInterfacedObject._Release
- Release=false will call TInterfacedObject._AddRef
- could be used to emulate proper reference counting of the instance via interfaces variables, but still storing plain class instances (e.g. in a global list of instances) - warning: use with extreme caution!
TSynInterfacedObject = class(TObject)
An abstract ancestor, for implementing a custom TInterfacedObject like class
- by default, will do nothing: no instance would be retrieved by QueryInterface unless the VirtualQueryInterface protected method is overriden, and _AddRef/_Release methods would call VirtualAddRef and VirtualRelease pure abstract methods
- using this class will leverage the signature difference between Delphi and FPC, among all supported platforms
- the class includes a RefCount integer field
constructor Create; virtual;
This virtual constructor will be called at instance creation
- this constructor does nothing, but is declared as virtual so that inherited classes may safely override this default void implementation
property RefCount: integer read fRefCount write fRefCount;
The associated reference count
TInterfacedCollection = class(TCollection)
Any TCollection used between client and server shall inherit from this class
- you should override the GetClass virtual method to provide the expected collection item class to be used on server side
- another possibility is to register a TCollection/TCollectionItem pair via a call to Rtti.RegisterCollection()
constructor Create; reintroduce; virtual;
This constructor will call GetClass to initialize the collection
class function GetClass: TCollectionItemClass; virtual; abstract;
You shall override this abstract method
IAutoFree = interface(IInterface)
Interface for TAutoFree to register another TObject instance to an existing IAutoFree local variable
- WARNING: both FPC and Delphi 10.4+ don't keep the IAutoFree instance up to the end-of-method -> you should not use TAutoFree for new projects :(
procedure ForMethod;
Do-nothing method to circumvent the Delphi 10.4 IAutoFree early release
TAutoFree = class(TInterfacedObject)
Simple reference-counted storage for local objects
- WARNING: both FPC and Delphi 10.4+ don't keep the IAutoFree instance up to the end-of-method -> you should not use TAutoFree for new projects :(
- be aware that it won't implement a full ARC memory model, but may be just used to avoid writing some try ... finally blocks on local variables
- use with caution, only on well defined local scope
constructor Create(const varObjPairs: array of pointer); reintroduce; overload;
Initialize the TAutoFree class for several local variables
- do not call this constructor, but class function Several() instead
constructor Create(var localVariable; obj: TObject); reintroduce; overload;
Initialize the TAutoFree class for one local variable
- do not call this constructor, but class function One() instead
destructor Destroy; override;
Will finalize the associated TObject instances
- note that releasing the TObject instances won't be protected, so any exception here may induce a memory leak: use only with "safe" simple objects, e.g. mORMot's TOrm
class function One(var localVariable; obj: TObject): IAutoFree;
Protect one local TObject variable instance life time
- for instance, instead of writing:
var myVar: TMyClass; begin myVar := TMyClass.Create; try ... use myVar finally myVar.Free; end; end;
- you may write:
var myVar: TMyClass; begin TAutoFree.One(myVar,TMyClass.Create); ... use myVar end; // here myVar will be released
- warning: under FPC, you should assign the result of this method to a local IAutoFree variable - see bug http://bugs.freepascal.org/view.php?id=26602
- Delphi 10.4 also did change it and release the IAutoFree before the end of the current method, so we inlined a void method call trying to circumvent this problem - https://quality.embarcadero.com/browse/RSP-30050
- for both Delphi 10.4+ and FPC, you may use with TAutoFree.One() do
class function Several(const varObjPairs: array of pointer): IAutoFree;
Protect several local TObject variable instances life time
- specified as localVariable/objectInstance pairs
- you may write:
var var1, var2: TMyClass; begin TAutoFree.Several([ @var1,TMyClass.Create, @var2,TMyClass.Create]); ... use var1 and var2 end; // here var1 and var2 will be released
- warning: under FPC, you should assign the result of this method to a local IAutoFree variable - see bug http://bugs.freepascal.org/view.php?id=26602
- Delphi 10.4 also did change it and release the IAutoFree before the end of the current method, and an "array of pointer" cannot be inlined by the Delphi compiler, so you should explicitly call ForMethod:
TAutoFree.Several([ @var1,TMyClass.Create, @var2,TMyClass.Create]).ForMethod;
procedure Another(var localVariable; obj: TObject);
Protect another TObject variable to an existing IAutoFree instance life time
- you may write:
var var1, var2: TMyClass; auto: IAutoFree; begin auto := TAutoFree.One(var1,TMyClass.Create);, .... do something auto.Another(var2,TMyClass.Create); ... use var1 and var2 end; // here var1 and var2 will be released
IAutoLocker = interface(IInterface)
An interface used by TAutoLocker to protect multi-thread execution
function ProtectMethod: IUnknown;
Will enter the mutex until the IUnknown reference is released
- using an IUnknown interface to let the compiler auto-generate a try..finally block statement to release the lock for the code block
- could be used as such under Delphi:
begin ... // unsafe code fSharedAutoLocker.ProtectMethod; ... // thread-safe code end; // local hidden IUnknown will release the lock for the method
- warning: under FPC, you should assign its result to a local variable - see bug http://bugs.freepascal.org/view.php?id=26602
var LockFPC: IUnknown; begin ... // unsafe code LockFPC := fSharedAutoLocker.ProtectMethod; ... // thread-safe code end; // LockFPC will release the lock for the method
or
begin ... // unsafe code with fSharedAutoLocker.ProtectMethod do begin ... // thread-safe code end; // local hidden IUnknown will release the lock for the method end;
function Safe: PSynLocker;
Gives an access to the internal low-level TSynLocker instance used
procedure Enter;
Enter the mutex
- any call to Enter should be ended with a call to Leave, and protected by a try..finally block, as such:
begin ... // unsafe code fSharedAutoLocker.Enter; try ... // thread-safe code finally fSharedAutoLocker.Leave; end; end;
procedure Leave;
Leave the mutex
- any call to Leave should be preceded with a call to Enter
TAutoLocker = class(TInterfacedObjectWithCustomCreate)
Reference-counted block code critical section
- you can use one instance of this to protect multi-threaded execution
- the main class may initialize a IAutoLocker property in Create, then call IAutoLocker.ProtectMethod in any method to make its execution thread safe
- this class inherits from TInterfacedObjectWithCustomCreate so you could define one published property of a mormot.core.interface.pas TInjectableObject as IAutoLocker so that this class may be automatically injected
- consider inherit from high-level TSynPersistentLock or call low-level fSafe := NewSynLocker / fSafe^.DoneAndFreemem instead
constructor Create; override;
Initialize the mutex
destructor Destroy; override;
Finalize the mutex
function ProtectMethod: IUnknown;
Will enter the mutex until the IUnknown reference is released
- as expected by IAutoLocker interface
- could be used as such under Delphi:
begin ... // unsafe code fSharedAutoLocker.ProtectMethod; ... // thread-safe code end; // local hidden IUnknown will release the lock for the method
- warning: under FPC, you should assign its result to a local variable - see bug http://bugs.freepascal.org/view.php?id=26602
var LockFPC: IUnknown; begin ... // unsafe code LockFPC := fSharedAutoLocker.ProtectMethod; ... // thread-safe code end; // LockFPC will release the lock for the method
or
begin ... // unsafe code with fSharedAutoLocker.ProtectMethod do begin ... // thread-safe code end; // local hidden IUnknown will release the lock for the method end;
function Safe: PSynLocker;
Access to the locking methods of this instance
- as expected by IAutoLocker interface
procedure Enter; virtual;
Enter the mutex
- as expected by IAutoLocker interface
- any call to Enter should be ended with a call to Leave, and protected by a try..finally block, as such:
begin ... // unsafe code fSharedAutoLocker.Enter; try ... // thread-safe code finally fSharedAutoLocker.Leave; end; end;
procedure Leave; virtual;
Leave the mutex
- as expected by IAutoLocker interface
property Locker: TSynLocker read fSafe;
Direct access to the locking methods of this instance
- faster than IAutoLocker.Safe function
TSynPersistent = class(TObjectWithCustomCreate)
Our own empowered TPersistent-like parent class
- TPersistent has an unexpected speed overhead due a giant lock introduced to manage property name fixup resolution (which we won't use outside the UI)
- this class has a virtual constructor, so is a preferred alternative to both TPersistent and TPersistentWithCustomCreate classes
- features some protected methods to customize its JSON serialization
- for best performance, any type inheriting from this class will bypass some regular steps: do not implement interfaces or use TMonitor with them!
procedure Assign(Source: TSynPersistent); virtual;
Allows to implement a TPersistent-like assignement mechanism
- inherited class should override AssignTo() protected method to implement the proper assignment
TSynList = class(TObject)
Simple and efficient TList, without any notification
- regular TList has an internal notification mechanism which slows down basic process, and can't be easily inherited
- stateless methods (like Add/Clear/Exists/Remove) are defined as virtual since can be overriden e.g. by TSynObjectListLocked to add a TSynLocker
constructor Create; virtual;
Virtual constructor called at instance creation
function Add(item: pointer): PtrInt; virtual;
function Exists(item: pointer): boolean; virtual;
Fast check if one item exists in the list
function IndexOf(item: pointer): PtrInt; virtual;
Fast retrieve one item in the list
function Insert(item: pointer; index: PtrInt): PtrInt;
Insert one item to the list at a given position
function Remove(item: pointer): PtrInt; virtual;
Fast delete one item in the list
procedure Clear; virtual;
procedure Delete(index: integer; dontfree: boolean = false); virtual;
property Count: integer read fCount;
How many items are stored in this TList instance
property Items[index: integer]: pointer read Get;
Low-level array-like access to the items stored in this TList instance
- warning: if index is out of range, will return nil and won't raise any exception
property List: TPointerDynArray read fList;
Low-level access to the items stored in this TList instance
TSynObjectList = class(TSynList)
Simple and efficient TObjectList, without any notification
constructor Create(aOwnObjects: boolean = true; aItemClass: TClass = nil); reintroduce; virtual;
Initialize the object list
- can optionally specify an item class for efficient JSON serialization
destructor Destroy; override;
Finalize the store items
function NewItem: pointer;
Create a new ItemClass instance, Add() it and return it
procedure Clear; override;
Delete all objects of the list
procedure ClearFromLast; virtual;
Delete all objects of the list in reverse order
- for some kind of processes, owned objects should be removed from the last added to the first
- will use slower but safer FreeAndNilSafe() instead of plain Free
procedure Delete(index: integer; dontfree: boolean = false); override;
Delete one object from the list
- will also Free the item if OwnObjects was set, and dontfree is false
property ItemClass: TClass read fItemClass write fItemClass;
Optional class of the stored items
- used e.g. by _JL_TSynObjectList() when unserializing from JSON
property OwnObjects: boolean read fOwnObjects write fOwnObjects;
Flag set if this list will Free its items on Delete/Clear/Destroy
TSynPersistentLock = class(TSynPersistent)
Adding locking methods to a TSynPersistent with virtual constructor
- you may use this class instead of the RTL TCriticalSection, since it would use a TSynLocker which does not suffer from CPU cache line conflit, and is cross-compiler whereas TMonitor is Delphi-specific and buggy (at least before XE5)
- if you don't need TSynPersistent overhead, consider plain TSynLocked class
constructor Create; override;
Initialize the instance, and its associated lock
destructor Destroy; override;
Finalize the instance, and its associated lock
procedure Lock;
Could be used as a short-cut to Safe.Lock
procedure Unlock;
Could be used as a short-cut to Safe.UnLock
property Safe: PSynLocker read fSafe;
Access to the associated instance critical section
- call Safe.Lock/UnLock to protect multi-thread access on this storage
TSynPersistentRWLightLock = class(TSynPersistent)
Adding light non-upgradable multiple Read / exclusive Write locking methods to a TSynPersistent with virtual constructor
property Safe: TRWLightLock read fSafe;
Access to the associated non-upgradable TRWLightLock instance
- call Safe methods to protect multi-thread access on this storage
TSynPersistentRWLock = class(TSynPersistent)
Adding light upgradable multiple Read / exclusive Write locking methods to a TSynPersistent with virtual constructor
property Safe: TRWLock read fSafe;
Access to the associated upgradable TRWLock instance
- call Safe methods to protect multi-thread access on this storage
TSynPersistentLocked = class(TSynPersistentLock)
Used for backward compatibility only with existing code
TInterfacedObjectLocked = class(TInterfacedObjectWithCustomCreate)
Adding locking methods to a TInterfacedObject with virtual constructor
constructor Create; override;
TSynLocker would increase inherited fields offset initialize the object instance, and its associated lock
destructor Destroy; override;
Release the instance (including the locking resource)
property Safe: PSynLocker read fSafe;
Access to the locking methods of this instance
- use Safe.Lock/TryLock with a try ... finally Safe.Unlock block
TInterfacedObjectRWLocked = class(TInterfacedObjectWithCustomCreate)
Adding light locking methods to a TInterfacedObject with virtual constructor
property Safe: TRWLock read fSafe;
Access to the multiple Read / exclusive Write locking methods of this instance
TSynObjectListLightLocked = class(TSynObjectList)
Add TRWLightLock non-upgradable methods to a TSynObjectList
- this class expands the regular TSynObjectList to include a TRWLightLock
- you need to call the Safe locking methods by hand to protect the execution of all methods, since even Add/Clear/ClearFromLast/Remove/Exists have not been overriden because TRWLighLock.WriteLock is not reentrant
property Safe: TRWLightLock read fSafe;
The light single Read / exclusive Write LightLock associated to this list
- could be used to protect shared resources within the internal process, for index-oriented methods like Delete/Items/Count...
- use Safe LightLock methods with a try ... finally bLightLock
TSynObjectListLocked = class(TSynObjectList)
Add TRWLock upgradable methods to a TSynObjectList
- this class expands the regular TSynObjectList to include a TRWLock
- you need to call the Safe locking methods by hand to protect the execution of index-oriented methods (like Delete/Items/Count...): the list content may change in the background, so using indexes is thread-safe
- on the other hand, Add/Clear/ClearFromLast/Remove stateless methods have been overriden in this class to call Safe lock methods, and therefore are thread-safe and protected to any background change
function Add(item: pointer): PtrInt; override;
Add one item to the list using Safe.WriteLock
function Exists(item: pointer): boolean; override;
Check an item using Safe.ReadOnlyLock
function Remove(item: pointer): PtrInt; override;
Fast delete one item in the list, using Safe.WriteLock
procedure Clear; override;
Delete all items of the list using Safe.WriteLock
procedure ClearFromLast; override;
Delete all items of the list in reverse order, using Safe.WriteLock
property Safe: TRWLock read fSafe;
The light single Read / exclusive Write lock associated to this list
- could be used to protect shared resources within the internal process, for index-oriented methods like Delete/Items/Count...
- use Safe lock methods within a try ... finally block
TSynObjectListSorted = class(TSynObjectListLocked)
An ordered thread-safe TSynObjectList
- items will be stored in order, for O(log(n)) fast search
constructor Create(const aCompare: TOnObjectCompare; aOwnsObjects: boolean = true); reintroduce;
Initialize the object list to be sorted with the supplied function
function Add(item: pointer): PtrInt; override;
Add in-order one item to the list using Safe.WriteLock
- returns the sorted index when item was inserted
- returns < 0 if item was found, as -(existingindex + 1)
function Find(item: TObject): TObject;
Fast retrieve one item in the list using O(log(n)) binary search
- supplied item should have enough information for fCompare to work
function IndexOf(item: pointer): PtrInt; override;
Fast retrieve one item in the list using O(log(n)) binary search
- this overriden version won't search for the item pointer itself, but will use the Compare() function until it is 0
property Compare: TOnObjectCompare read fCompare write fCompare;
How two stored objects are stored
TSynPersistentStore = class(TSynPersistentRWLock)
Abstract high-level handling of (SynLZ-)compressed persisted storage
- LoadFromReader/SaveToWriter abstract methods should be overriden with proper binary persistence implementation
constructor Create(const aName: RawUtf8); reintroduce; overload; virtual;
Initialize a void storage with the supplied name
constructor CreateFrom(const aBuffer: RawByteString; aLoad: TAlgoCompressLoad = aclNormal);
Initialize a storage from a SaveTo persisted buffer
- raise a EFastReader exception on decoding error
constructor CreateFromBuffer(aBuffer: pointer; aBufferLen: integer; aLoad: TAlgoCompressLoad = aclNormal);
Initialize a storage from a SaveTo persisted buffer
- raise a EFastReader exception on decoding error
constructor CreateFromFile(const aFileName: TFileName; aLoad: TAlgoCompressLoad = aclNormal);
Initialize a storage from a SaveTo persisted buffer
- raise a EFastReader exception on decoding error
function LoadFromFile(const aFileName: TFileName; aLoad: TAlgoCompressLoad = aclNormal): boolean;
Initialize the storage from a SaveToFile content
- actually call the LoadFromReader() virtual method for persistence
- returns false if the file is not found, true if the file was loaded without any problem, or raise a EFastReader exception on decoding error
function SaveTo(nocompression: boolean = false; BufLen: integer = 65536; ForcedAlgo: TAlgoCompress = nil; BufferOffset: integer = 0): RawByteString; overload;
Persist the content as a SynLZ-compressed binary blob
- just an overloaded wrapper
function SaveToFile(const aFileName: TFileName; nocompression: boolean = false; BufLen: integer = 65536; ForcedAlgo: TAlgoCompress = nil): PtrUInt;
Persist the content as a SynLZ-compressed binary file
- to be retrieved later on via LoadFromFile method
- returns the number of bytes of the resulting file
- actually call the SaveTo method for persistence
procedure LoadFrom(const aBuffer: RawByteString; aLoad: TAlgoCompressLoad = aclNormal); overload;
Fill the storage from a SaveTo persisted buffer
- actually call the LoadFromReader() virtual method for persistence
- raise a EFastReader exception on decoding error
procedure LoadFrom(aBuffer: pointer; aBufferLen: integer; aLoad: TAlgoCompressLoad = aclNormal); overload; virtual;
Initialize the storage from a SaveTo persisted buffer
- actually call the LoadFromReader() virtual method for persistence
- raise a EFastReader exception on decoding error
procedure SaveTo(out aBuffer: RawByteString; nocompression: boolean = false; BufLen: integer = 65536; ForcedAlgo: TAlgoCompress = nil; BufferOffset: integer = 0); overload; virtual;
Persist the content as a SynLZ-compressed binary blob
- to be retrieved later on via LoadFrom method
- actually call the SaveToWriter() protected virtual method for persistence
- you can specify ForcedAlgo if you want to override the default AlgoSynLZ
- BufferOffset could be set to reserve some bytes before the compressed buffer
property LoadFromLastUncompressed: integer read fLoadFromLastUncompressed;
After a LoadFrom(), contains the uncompressed data size read
property Name: RawUtf8 read fName;
One optional text associated with this storage
- you can define this field as published to serialize its value in log/JSON
property SaveToLastUncompressed: integer read fSaveToLastUncompressed;
After a SaveTo(), contains the uncompressed data size written
TRawUtf8InterningAbstract = class(TSynPersistent)
Defined here as forward definition of the TRawUtf8Interning final class
EDynArray = class(ESynException)
The kind of exceptions raised during TDynArray/TDynArrayHashed process
TDynArray = object(TObject)
A wrapper around a dynamic array with one dimension
- provide TList-like methods using fast RTTI information
- can be used to fast save/retrieve all memory content to a TStream
- note that the "const Item" is not checked at compile time nor runtime: you must ensure that Item matchs the element type of the dynamic array; all Item*() methods will use pointers for safety
- can use external Count storage to make Add() and Delete() much faster (avoid most reallocation of the memory buffer)
- Note that TDynArray is just a wrapper around an existing dynamic array: methods can modify the content of the associated variable but the TDynArray doesn't contain any data by itself. It is therefore aimed to initialize a TDynArray wrapper on need, to access any existing dynamic array.
- is defined as an object or as a record, due to a bug in Delphi 2009/2010 compiler (at least): this structure is not initialized if defined as an object on the stack, but will be as a record :(
function Add(const Item): PtrInt;
Add an element to the dynamic array
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write Add(i+10) e.g.)
- returns the index of the added element in the dynamic array
- note that because of dynamic array internal memory managment, adding may reallocate the list every time a record is added, unless an external count variable has been specified in Init(...,@Count) method
function AddArray(const DynArrayVar; aStartIndex: integer = 0; aCount: integer = -1): integer;
Add items from a given dynamic array variable
- the supplied source DynArray MUST be of the same exact type as the current used for this TDynArray - warning: pass here a reference to a "array of ..." variable, not another TDynArray instance; if you want to add another TDynArray, use AddDynArray() method
- you can specify the start index and the number of items to take from the source dynamic array (leave as -1 to add till the end)
- returns the number of items added to the array
function ClearSafe: boolean;
Delete the whole dynamic array content, ignoring exceptions
- returns true if no exception occurred when calling Clear, false otherwise
- you should better not call this method, which will catch and ignore all exceptions - but it may somewhat make sense in a destructor
- this method will recognize T*ObjArray types and free all instances
function Compares(B: PDynArray; IgnoreCompare: boolean = false; CaseSensitive: boolean = true): integer;
Compare the content of the two arrays
- use any supplied Compare property (unless ignorecompare=true), or following the RTTI element description on all array items
- T*ObjArray kind of arrays will properly compare their properties
function Delete(aIndex: PtrInt): boolean;
Delete one item inside the dynamic array
- the deleted element is finalized if necessary
- this method will recognize T*ObjArray types and free all instances
function Equals(B: PDynArray; IgnoreCompare: boolean = false; CaseSensitive: boolean = true): boolean;
Compare the content of the two arrays, returning TRUE if both match
- use any supplied Compare property (unless ignorecompare=true), or following the RTTI element description on all array items
- T*ObjArray kind of arrays will properly compare their properties
function FastLocateOrAddSorted(const Item; wasAdded: PBoolean = nil): integer;
Search and add an element value inside a sorted dynamic array
- this method will use the Compare property function for the search
- will be faster than a manual FindAndAddIfNotExisting+Sort process
- returns the index of the existing Item and wasAdded^=false
- returns the sorted index of the inserted Item and wasAdded^=true
- if the array is not sorted, returns -1 and wasAdded^=false
- is just a wrapper around FastLocateSorted+FastAddSorted
function FastLocateSorted(const Item; out Index: integer): boolean;
Search for an element value inside a sorted dynamic array
- this method will use the Compare property function for the search
- will be faster than a manual FindAndAddIfNotExisting+Sort process
- returns TRUE and the index of existing Item, or FALSE and the index where the Item is to be inserted so that the array remains sorted
- you should then call FastAddSorted() later with the returned Index
- if the array is not sorted, returns FALSE and Index=-1
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (no FastLocateSorted(i+10) e.g.)
- warning: Index out parameter should be integer, not PtrInt
function Find(const Item; const aIndex: TIntegerDynArray; aCompare: TDynArraySortCompare): PtrInt; overload;
Search for an element value inside the dynamic array, from an external aIndex[] lookup table - e.g. created by CreateOrderedIndex()
- return the index found (0..Count-1), or -1 if Item was not found
- if an indexed lookup is supplied, it must already be sorted: this function will then use fast O(log(n)) binary search over aCompare
- if the indexed lookup is not correct (e.g. aIndex=nil), iterate O(n) using aCompare - it won't fallback to IndexOf() RTTI search
- warning: the lookup aIndex[] should be synchronized if array content is modified (in case of addition or deletion)
function Find(const Item; aCompare: TDynArraySortCompare = nil): PtrInt; overload;
Search for an element inside the dynamic array using the Compare function
- this method will use the Compare property function, or the supplied aCompare for the search; if none of them are set, it will fallback to IndexOf() to perform a default case-sensitive RTTI search
- return the index found (0..Count-1), or -1 if Item was not found
- if the array is sorted, it will use fast O(log(n)) binary search
- if the array is not sorted, it will use slower O(n) iterating search
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write Find(i+10) e.g.)
function FindAllSorted(const Item; out FindCount: integer): pointer; overload;
Search the item pointers which match a given value in a sorted dynamic array
- this method will use the Compare property function for the search
- return nil and FindCount = 0 if no matching item was found
- return the a pointer to the first matching item, and FindCount >=1
- warning: FindCount out parameter should be integer, not PtrInt
function FindAllSorted(const Item; out FirstIndex, LastIndex: integer): boolean; overload;
Search the items range which match a given value in a sorted dynamic array
- this method will use the Compare property function for the search
- returns TRUE and the matching indexes, or FALSE if none found
- if the array is not sorted, returns FALSE
- warning: FirstIndex/LastIndex parameters should be integer, not PtrInt
function FindAndAddIfNotExisting(const Item; aIndex: PIntegerDynArray = nil; aCompare: TDynArraySortCompare = nil): integer;
Search for an element value, then add it if none matched
- this method will use the Compare property function for the search, or the supplied indexed lookup table and its associated compare function, and fallback to case-sensitive RTTI search if none is defined
- if no Item content matches, the item will added to the array
- can be used e.g. as a simple dictionary: if Compare will match e.g. the first string field (i.e. set to SortDynArrayString), you can fill the first string field with the searched value (if returned index is >= 0)
- return the index found (0..Count-1), or -1 if Item was not found and the supplied element has been successfully added
- if the array is sorted, it will use fast O(log(n)) binary search
- if the array is not sorted, it will use slower O(n) iterating search
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write Find(i+10) e.g.)
function FindAndDelete(const Item; aIndex: PIntegerDynArray = nil; aCompare: TDynArraySortCompare = nil): integer;
Search for an element value, then delete it if match
- this method will use the Compare property function for the search, or the supplied indexed lookup table and its associated compare function, and fallback to case-sensitive RTTI search if none is defined
- if Item content matches, this item will be deleted from the array
- can be used e.g. as a simple dictionary: if Compare will match e.g. the first string field (i.e. set to SortDynArrayString), you can fill the first string field with the searched value (if returned index is >= 0)
- return the index deleted (0..Count-1), or -1 if Item was not found
- if the array is sorted, it will use fast O(log(n)) binary search
- if the array is not sorted, it will use slower O(n) iterating search
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write Find(i+10) e.g.)
function FindAndFill(var Item; aIndex: PIntegerDynArray = nil; aCompare: TDynArraySortCompare = nil): integer;
Search for an element value, then fill all properties if match
- this method will use the Compare property function for the search, or the supplied indexed lookup table and its associated compare function, and fallback to case-sensitive RTTI search if none is defined
- if Item content matches, all Item fields will be filled with the record
- can be used e.g. as a simple dictionary: if Compare will match e.g. the first string field (i.e. set to SortDynArrayString), you can fill the first string field with the searched value (if returned index is >= 0)
- return the index found (0..Count-1), or -1 if Item was not found
- if the array is sorted, it will use fast O(log(n)) binary search
- if the array is not sorted, it will use slower O(n) iterating search
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write Find(i+10) e.g.)
function FindAndUpdate(const Item; aIndex: PIntegerDynArray = nil; aCompare: TDynArraySortCompare = nil): integer;
Search for an element value, then update the item if match
- this method will use the Compare property function for the search, or the supplied indexed lookup table and its associated compare function, and fallback to case-sensitive RTTI search if none is defined
- if Item content matches, this item will be updated with the supplied value
- can be used e.g. as a simple dictionary: if Compare will match e.g. the first string field (i.e. set to SortDynArrayString), you can fill the first string field with the searched value (if returned index is >= 0)
- return the index found (0..Count-1), or -1 if Item was not found
- if the array is sorted, it will use fast O(log(n)) binary search
- if the array is not sorted, it will use slower O(n) iterating search
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write Find(i+10) e.g.)
function IndexOf(const Item; CaseInSensitive: boolean = true): PtrInt;
Search for an element inside the dynamic array using RTTI
- return the index found (0..Count-1), or -1 if Item was not found
- will search for all properties content of Item: TList.IndexOf() searches by address, this method searches by content using the RTTI element description (and not the Compare property function)
- use the Find() method if you want the search via the Compare property function, or e.g. to search only with some part of the element content
- will work with simple types: binaries (byte, word, integer, Int64, Currency, array[0..255] of byte, packed records with no reference-counted type within...), string types (e.g. array of string), and packed records with binary and string types within (like TFileVersion)
- won't work with not packed types (like a shorstring, or a record with byte or word fields with {$A+}): in this case, the padding data (i.e. the bytes between the aligned fields) can be filled as random, and there is no way with standard RTTI to identify randomness from values
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write IndexOf(i+10) e.g.)
function InitSpecific(aTypeInfo: PRttiInfo; var aValue; aKind: TRttiParserType; aCountPointer: PInteger = nil; aCaseInsensitive: boolean = false): TRttiParserType;
Initialize the wrapper with a one-dimension dynamic array
- also set the Compare() function from a supplied TRttiParserType
- aKind=ptNone will guess the type from Info.ArrayRtti/ArrayFirstField
- will raise an exception if there is not enough RTTI available
- no RTTI check is made over the corresponding array layout: you shall ensure that the aKind parameter matches at least the first field of the dynamic array item definition
- aCaseInsensitive will be used for ptStringTypes
function IsSorted(aCompare: TDynArraySortCompare = nil): boolean;
Will check all items against aCompare
function IsVoid: boolean;
Check if the wrapper points to a dynamic array
- i.e. if Void has been called before
function ItemCompare(A, B: pointer; CaseInSensitive: boolean = false): integer;
Compare the content of two items, returning -1, 0 or +1s
- use the Compare() property function (if set) or using Info.Cache.ItemInfoManaged if available - and fallbacks to binary comparison
function ItemCopyAt(index: PtrInt; Dest: pointer): boolean;
Will copy one element content from its index into another variable
- do nothing and return false if index is out of range or Dest is nil
function ItemCopyFirstField(Source, Dest: pointer): boolean;
Will copy the first field value of an array element
- will use the array KnownType to guess the copy routine to use
- returns false if the type information is not enough for a safe copy
function ItemEquals(A, B: pointer; CaseInSensitive: boolean = false): boolean;
Compare the content of two items, returning TRUE if both values equal
- use the Compare() property function (if set) or using Info.Cache.ItemInfoManaged if available - and fallbacks to binary comparison
function ItemLoadFind(Source, SourceMax: PAnsiChar): integer;
Search for an array element as saved by the ItemSave method
- same as ItemLoad() + Find()/IndexOf() + ItemLoadClear()
- will call Find() method if Compare property is set
- will call generic IndexOf() method if no Compare property is set
function ItemLoadMem(Source, SourceMax: PAnsiChar): RawByteString;
Load an array element as saved by the ItemSave method
- this overloaded method will retrieve the element as a memory buffer, which should be cleared by ItemLoadMemClear() before release
function ItemMoveTo(index: PtrInt; Dest: pointer): boolean;
Will move one element content from its index into another variable
- will erase the internal item after copy
- do nothing and return false if index is out of range or Dest is nil
function ItemPtr(index: PtrInt): pointer;
Returns a pointer to an element of the array
- returns nil if aIndex is out of range
- since TDynArray is just a wrapper around an existing array, you should better use direct access to its wrapped variable, and not this (slightly) slower and more error prone method (such pointer access lacks of strong typing abilities), which is designed for TDynArray abstract/internal use
function ItemSave(Item: pointer): RawByteString;
Save an array element into a serialized binary content
- use the same layout as TDynArray.SaveTo, but for a single item
- you can use ItemLoad method later to retrieve its content
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write ItemSave(i+10) e.g.)
function ItemSize: PtrUInt;
Just a convenient wrapper of Info.Cache.ItemSize
function LoadFrom(Source: PAnsiChar; SourceMax: PAnsiChar = nil): PAnsiChar;
Unserialize dynamic array content from binary written by TDynArray.SaveTo
- return nil if the Source buffer is incorrect: invalid type, wrong checksum, or SourceMax overflow
- return a non nil pointer just after the Source content on success
- this method will raise an ESynException for T*ObjArray types
function LoadFromBinary(const Buffer: RawByteString): boolean;
Unserialize the dynamic array content from a TDynArray.SaveTo binary string
- same as LoadFrom, and will check for any buffer overflow since we know the actual end of input buffer
- will read mORMot 1.18 binary content, but will ignore the Hash32 stored checksum which is not needed any more
function LoadFromJson(P: PUtf8Char; EndOfObject: PUtf8Char = nil; CustomVariantOptions: PDocVariantOptions = nil; Tolerant: boolean = false; Interning: TRawUtf8InterningAbstract = nil): PUtf8Char; overload;
Load the dynamic array content from an UTF-8 encoded JSON buffer
- expect the format as saved by TTextWriter.AddDynArrayJson method, i.e. handling TbooleanDynArray, TIntegerDynArray, TInt64DynArray, TCardinalDynArray, TDoubleDynArray, TCurrencyDynArray, TWordDynArray, TByteDynArray, TRawUtf8DynArray, TWinAnsiDynArray, TRawByteStringDynArray, TStringDynArray, TWideStringDynArray, TSynUnicodeDynArray, TTimeLogDynArray and TDateTimeDynArray as JSON array - or any customized Rtti.RegisterFromText/TRttiJson.RegisterCustomSerializer format
- or any other kind of array as Base64 encoded binary stream precessed via JSON_BASE64_MAGIC_C (UTF-8 encoded \uFFF0 special code)
- typical handled content could be
'[1,2,3,4]' or '["\uFFF0base64encodedbinary"]'
- return a pointer at the end of the data read from P, nil in case of an invalid input buffer
- this method will recognize T*ObjArray types, and will first free any existing instance before unserializing, to avoid memory leak
- set e.g. @JSON_[mFast] as CustomVariantOptions parameter to handle complex JSON object or arrays as TDocVariant into variant fields
- can use an associated TRawUtf8Interning instance for RawUtf8 values
- warning: the content of P^ will be modified during parsing: make a local copy if it will be needed later (using e.g. the overloaded method)
function LoadFromJson(const Json: RawUtf8; CustomVariantOptions: PDocVariantOptions = nil; Tolerant: boolean = false; Interning: TRawUtf8InterningAbstract = nil): boolean; overload;
Load the dynamic array content from an UTF-8 encoded JSON buffer
- this method will make a private copy of the JSON for in-place parsing
- returns false in case of invalid input buffer, true on success
function New: PtrInt;
Add an element to the dynamic array, returning its index
- note: if you use this method to add a new item with a reference to the dynamic array, be aware that the following trigger a GPF on FPC:
with Values[DynArray.New] do // otherwise Values is nil -> GPF begin Field1 := 1; ...
- so you should either use a local variable:
i := DynArray.New; with Values[i] do // otherwise Values is nil -> GPF begin
- or even better, don't use the dubious "with Values[...] do" but NewPtr
function NewPtr: pointer;
Add an element to the dynamic array, returning its pointer
- a slightly faster alternative to ItemPtr(New)
function Peek(var Dest): boolean;
Get the last element stored in the dynamic array
- Add + Pop/Peek will implement a LIFO (Last-In-First-Out) stack
- warning: Dest must be of the same exact type than the dynamic array
- returns true if the item was successfully copied into Dest
- use Pop() if you also want to remove the item
function PeekHead(var Dest): boolean;
Get the first element stored in the dynamic array
- Add + PopHead/PeekHead will implement a FIFO (First-In-First-Out) stack
- warning: Dest must be of the same exact type than the dynamic array
- returns true if the item was successfully copied and removed
- use PopHead() if you also want to remove the item
function Pop(var Dest): boolean;
Get and remove the last element stored in the dynamic array
- Add + Pop/Peek will implement a LIFO (Last-In-First-Out) stack
- warning: Dest must be of the same exact type than the dynamic array
- returns true if the item was successfully copied and removed
- use Peek() if you don't want to remove the item, but just get its value
function PopHead(var Dest): boolean;
Get and remove the first element stored in the dynamic array
- Add + PopHead/PeekHead will implement a FIFO (First-In-First-Out) stack
- removing from head will move all items so TSynQueue is faster
- warning: Dest must be of the same exact type than the dynamic array
- returns true if the item was successfully copied and removed
- use PeekHead() if you don't want to remove the item, but get its value
- first slot will be deleted and all content moved, so may take some time
function SaveTo: RawByteString; overload;
Save the dynamic array content into a RawByteString
- will use a proprietary binary format, with some variable-length encoding of the string length - note that if you change the type definition, any previously-serialized content will fail, maybe triggering unexpected GPF: use SaveToTypeInfoHash if you share this binary data accross executables
- this method will raise an ESynException for T*ObjArray types
- use TDynArray.LoadFrom to decode the saved buffer
- warning: legacy Hash32 checksum will be stored as 0, so may be refused by mORMot TDynArray.LoadFrom before 1.18.5966
function SaveToJson(EnumSetsAsText: boolean = false; reformat: TTextWriterJsonFormat = jsonCompact): RawUtf8; overload;
Serialize the dynamic array content as JSON
function SetParserType(aKind: TRttiParserType; aCaseInsensitive: boolean): TRttiParserType;
Set a specific TRttiParserType for this dynamic array
- could be called after InitRtti() to set the Compare() function
- as used by InitSpecific() after InitRtti(Rtti.RegisterType(aTypeInfo))
procedure AddDynArray(aSource: PDynArray; aStartIndex: integer = 0; aCount: integer = -1);
Add items from a given TDynArray
- the supplied source TDynArray MUST be of the same exact type as the current used for this TDynArray, otherwise it won't do anything
- you can specify the start index and the number of items to take from the source dynamic array (leave as -1 to add till the end)
procedure Clear;
Delete the whole dynamic array content
- this method will recognize T*ObjArray types and free all instances
procedure Copy(Source: PDynArray; ObjArrayByRef: boolean = false);
Set all content of one dynamic array to the current array
- both must be of the same exact type
- T*ObjArray will be reallocated and copied by content (using a temporary JSON serialization), unless ObjArrayByRef is true and pointers are copied
procedure CopyFrom(const Source; MaxItem: integer; ObjArrayByRef: boolean = false);
Set all content of one dynamic array to the current array
- both must be of the same exact type
- T*ObjArray will be reallocated and copied by content (using a temporary JSON serialization), unless ObjArrayByRef is true and pointers are copied
procedure CopyTo(out Dest; ObjArrayByRef: boolean = false);
Set all content of the current dynamic array to another array variable
- both must be of the same exact type
- resulting length(Dest) will match the exact items count, even if an external Count integer variable is used by this instance
- T*ObjArray will be reallocated and copied by content (using a temporary JSON serialization), unless ObjArrayByRef is true and pointers are copied
procedure CreateOrderedIndex(var aIndex: TIntegerDynArray; aCompare: TDynArraySortCompare); overload;
Sort the dynamic array items using a lookup array of indexes
- in comparison to the Sort method, this CreateOrderedIndex won't change the dynamic array content, but only create (or update) the supplied integer lookup array, using the specified comparison function
- if aCompare is not supplied, the method will use fCompare (if defined)
- you should provide either a void either a valid lookup table, that is a table with one to one lookup (e.g. created with FillIncreasing)
- if the lookup table has less items than the main dynamic array, its content will be recreated
procedure CreateOrderedIndex(out aIndex: TSynTempBuffer; aCompare: TDynArraySortCompare); overload;
Sort the dynamic array items using a lookup array of indexes
- this overloaded method will use the supplied TSynTempBuffer for index storage, so use PIntegerArray(aIndex.buf) to access the values
- caller should always make aIndex.Done once done
procedure CreateOrderedIndexAfterAdd(var aIndex: TIntegerDynArray; aCompare: TDynArraySortCompare);
Sort using a lookup array of indexes, after a Add()
- will resize aIndex if necessary, and set aIndex[Count-1] := Count-1
procedure EnsureSorted(aCompare: TDynArraySortCompare = nil);
Will check all items against aCompare, calling Sort() if needed
- faster than plain Sort() if the array is likely to be already sorted
procedure FastAddSorted(Index: PtrInt; const Item);
Insert a sorted element value at the proper place
- the index should have been computed by FastLocateSorted(): false
- you may consider using FastLocateOrAddSorted() instead
procedure FastDeleteSorted(Index: PtrInt);
Delete a sorted element value at the proper place
- plain Delete(Index) would reset the fSorted flag to FALSE, so use this method with a FastLocateSorted/FastAddSorted array
procedure FillZero;
Will call FillZero() on all items, mainly binaries and strings
- could be used on a dynamic array to avoid memory forensic after release
procedure Init(aTypeInfo: PRttiInfo; var aValue; aCountPointer: PInteger = nil);
Initialize the wrapper with a one-dimension dynamic array
- the dynamic array must have been defined with its own type (e.g. TIntegerDynArray = array of integer)
- if aCountPointer is set, it will be used instead of length() to store the dynamic array items count - it will be much faster when adding items to the array, because the dynamic array won't need to be resized each time - but in this case, you should use the Count property instead of length(array) or high(array) when accessing the data: in fact length(array) will store the memory size reserved, not the items count
- if aCountPointer is set, its content will be set to 0, whatever the array length is, or the current aCountPointer^ value is - to bypass this behavior and keep an existing Count, call UseExternalCount() after Init()
- a sample usage may be:
var DA: TDynArray; A: TIntegerDynArray; begin DA.Init(TypeInfo(TIntegerDynArray), A); (...)
- a sample usage may be (using a count variable):
var DA: TDynArray; A: TIntegerDynArray; ACount: integer; i: integer; begin DA.Init(TypeInfo(TIntegerDynArray), A, @ACount); for i := 1 to 100000 do DA.Add(i); // MUCH faster using the ACount variable (...) // now you should use DA.Count or Count instead of length(A)
procedure InitFrom(aAnother: PDynArray; var aValue);
Fast initialize a wrapper for an existing dynamic array of the same type
- is slightly faster than
InitRtti(aAnother.Info, aValue, nil);
procedure InitRtti(aInfo: TRttiCustom; var aValue; aCountPointer: PInteger); overload;
Initialize the wrapper with a one-dimension dynamic array
- low-level method, as called by Init() and InitSpecific()
- can be called directly for a very fast TDynArray initialization
- warning: caller should check that aInfo.Kind=rkDynArray
procedure InitRtti(aInfo: TRttiCustom; var aValue); overload;
Initialize the wrapper with a one-dimension dynamic array
- low-level method, as called by Init() and InitSpecific()
- can be called directly for a very fast TDynArray initialization
- warning: caller should check that aInfo.Kind=rkDynArray
procedure Insert(Index: PtrInt; const Item);
Add an element to the dynamic array at the position specified by Index
- warning: Item must be of the same exact type than the dynamic array, and must be a reference to a variable (you can't write Insert(10,i+10) e.g.)
procedure ItemClear(Item: pointer);
Will reset the element content
- i.e. release any managed type memory, and fill Item with zeros
procedure ItemCopy(Source, Dest: pointer);
Will copy one element content
procedure ItemCopyFrom(Source: pointer; index: PtrInt; ClearBeforeCopy: boolean = false);
Will copy one variable content into an indexed element
- do nothing if index is out of range
- ClearBeforeCopy will call ItemClear() before the copy, which may be safer if the source item is a copy of Values[index] with some dynamic arrays
procedure ItemLoad(Source, SourceMax: PAnsiChar; Item: pointer);
Load an array element as saved by the ItemSave method into Item variable
- warning: Item must be of the same exact type than the dynamic array
procedure ItemLoadMemClear(var ItemTemp: RawByteString);
Finalize a temporary buffer used to store an element via ItemLoadMem()
- will release any managed type referenced inside the RawByteString, then void the variable
- is just a wrapper around ItemClear(pointer(ItemTemp)) + ItemTemp := ''
procedure ItemRandom(Item: pointer);
Will fill the element with some random content
- this method is thread-safe using Rtti.DoLock/DoUnLock
procedure LoadFromReader(var Read: TFastReader);
Unserialize dynamic array content from binary written by TDynArray.SaveTo
procedure LoadFromStream(Stream: TCustomMemoryStream);
Load the dynamic array content from a (memory) stream
- stream content must have been created using SaveToStream method
- will handle array of binaries values (byte, word, integer...), array of strings or array of packed records, with binaries and string properties
- will use a proprietary binary format, with some variable-length encoding of the string length - note that if you change the type definition, any previously-serialized content will fail, maybe triggering unexpected GPF: use SaveToTypeInfoHash if you share this binary data accross executables
procedure Reverse;
Will reverse all array items, in place
procedure SaveTo(W: TBufferWriter); overload;
Save the dynamic array content using our binary serialization
- will use a proprietary binary format, with some variable-length encoding of the string length - note that if you change the type definition, any previously-serialized content will fail, maybe triggering unexpected GPF
- this method will raise an ESynException for T*ObjArray types
- use TDynArray.LoadFrom to decode the saved buffer
- warning: legacy Hash32 checksum will be stored as 0, so may be refused by mORMot TDynArray.LoadFrom before 1.18.5966
procedure SaveToJson(W: TTextWriter; ObjectOptions: TTextWriterWriteObjectOptions = []); overload;
Serialize the dynamic array content as JSON
- is just a wrapper around TTextDateWTTextWriterriter.AddTypedJson()
- this method will therefore recognize T*ObjArray types
procedure SaveToJson(out result: RawUtf8; Options: TTextWriterOptions; ObjectOptions: TTextWriterWriteObjectOptions = []; reformat: TTextWriterJsonFormat = jsonCompact); overload;
Serialize the dynamic array content as JSON
- is just a wrapper around TTextWriter.AddTypedJson()
- this method will therefore recognize T*ObjArray types
procedure SaveToJson(out result: RawUtf8; EnumSetsAsText: boolean = false; reformat: TTextWriterJsonFormat = jsonCompact); overload;
Serialize the dynamic array content as JSON
procedure SaveToStream(Stream: TStream);
Save the dynamic array content into a (memory) stream
- will handle array of binaries values (byte, word, integer...), array of strings or array of packed records, with binaries and string properties
- will use a proprietary binary format, with some variable-length encoding of the string length - note that if you change the type definition, any previously-serialized content will fail, maybe triggering unexpected GPF: use SaveToTypeInfoHash if you share this binary data accross executables
- Stream position will be set just after the added data
- is optimized for memory streams, but will work with any kind of TStream
procedure Slice(var Dest; Limit: cardinal; Offset: cardinal = 0);
Select a sub-section (slice) of a dynamic array content
procedure SliceAsDynArray(Dest: PPointer; Offset: integer = 0; Limit: integer = 0);
Assign the current dynamic array content into a variable
- by default (Offset=Limit=0), the whole array is set with no memory (re)allocation, just finalize the Dest slot, then make Inc(RefCnt) and force the internal length/Capacity to equal Count
- Offset/Limit could be used to create a new dynamic array with some part of the existing content (Offset<0 meaning from the end):
SliceAsDynArray(DA); // items 0..Count-1 (assign with refcount) SliceAsDynArray(DA, 10); // items 10..Count-1 SliceAsDynArray(DA, 0, 10); // first 0..9 items SliceAsDynArray(DA, 10, 20); // items 10..29 - truncated if Count < 20 SliceAsDynArray(DA, -10); // last Count-10..Count-1 items
procedure Sort(aCompare: TDynArraySortCompare = nil); overload;
Sort the dynamic array items, using the Compare property function
- it will change the dynamic array content, and exchange all items in order to be sorted in increasing order according to Compare function
procedure Sort(const aCompare: TOnDynArraySortCompare; aReverse: boolean = false); overload;
Sort the dynamic array items, using a Compare method (not function)
- it will change the dynamic array content, and exchange all items in order to be sorted in increasing order according to Compare function, unless aReverse is true
- it won't mark the array as Sorted, since the comparer is local
procedure SortRange(aStart, aStop: integer; aCompare: TDynArraySortCompare = nil);
Sort some dynamic array items, using the Compare property function
- this method allows to sort only some part of the items
- it will change the dynamic array content, and exchange all items in order to be sorted in increasing order according to Compare function
procedure UseExternalCount(aCountPointer: PInteger);
Define the reference to an external count integer variable
- Init and InitSpecific methods will reset the aCountPointer to 0: you can use this method to set the external count variable without overriding the current value
procedure Void;
Initialize the wrapper to point to no dynamic array
- it won't clear the wrapped array, just reset the fValue internal pointer
- in practice, will disable the other methods
property Capacity: PtrInt read GetCapacity write SetCapacity;
The internal buffer capacity
- if no external Count pointer was set with Init, is the same as Count
- if an external Count pointer is set, you can set a value to this property before a massive use of the Add() method e.g.
- if no external Count pointer is set, set a value to this property will affect the Count value, i.e. Add() will append after this count
- this property will recognize T*ObjArray types, so will free any stored instance if the array is sized down
property Compare: TDynArraySortCompare read fCompare write SetCompare;
The compare function to be used for Sort and Find methods
- by default, no comparison function is set
- common functions exist for base types: e.g. SortDynArrayByte, SortDynArrayBoolean, SortDynArrayWord, SortDynArrayInteger, SortDynArrayCardinal, SortDynArraySingle, SortDynArrayInt64, SortDynArrayDouble, SortDynArrayAnsiString, SortDynArrayAnsiStringI, SortDynArrayString, SortDynArrayStringI, SortDynArrayUnicodeString, SortDynArrayUnicodeStringI
property Count: PtrInt read GetCount write SetCount;
Retrieve or set the number of items of the dynamic array
- same as length(DynArray) or SetLength(DynArray)
- this property will recognize T*ObjArray types, so will free any stored instance if the array is sized down
property CountExternal: PInteger read fCountP;
Low-level direct access to the external count (if defined at Init)
- use UseExternalCount() after Init to avoid resetting the count to 0
property Info: TRttiCustom read fInfo;
Low-level extended RTTI access
- use e.g. Info.ArrayRtti to access the item RTTI, or Info.Cache.ItemInfoManaged to get the managed item TypeInfo()
property NoFinalize: boolean read fNoFinalize write fNoFinalize;
Can be set to TRUE to avoid any item finalization
- e.g. with T*ObjArray - handle with care to avoid memory leaks
property Sorted: boolean read fSorted write fSorted;
Must be TRUE if the array is currently in sorted order according to the compare function
- Add/Delete/Insert/Load* methods will reset this property to false
- Sort method will set this property to true
- you MUST set this property to false if you modify the dynamic array content in your code, so that Find() won't try to wrongly use binary search in an unsorted array, and miss its purpose
property Value: PPointer read fValue;
Low-level direct access to the storage variable
TDynArrayLocked = record
Just a wrapper record to join a TDynArray, its Count and a TRWLightLock
Count: integer;
Will store the length of the TDynArray
DynArray: TDynArray;
The wrapper to a dynamic array
Safe: TRWLightLock;
Lightweight multiple Reads / exclusive Write non-upgradable lock
TDynArrayHasher = object(TObject)
Implements O(1) lookup to any dynamic array content
- this won't handle the storage process (like add/update), just efficiently maintain a hash table over an existing dynamic array: several TDynArrayHasher could be applied to a single TDynArray wrapper
- TDynArrayHashed will use a TDynArrayHasher on its own storage
function Equals(Item: pointer; ndx: PtrInt): boolean;
Compare one given item from its index with a value
- using either EventCompare() or Compare() functions
function Find(Item: pointer; aHashCode: cardinal): PtrInt; overload;
Search for a hashed element value inside the dynamic array with hashing
function Find(aHashCode: cardinal; aForAdd: boolean): PtrInt; overload;
Search for a hash position inside the dynamic array with hashing
function Find(Item: pointer): PtrInt; overload;
Search for an element value inside the dynamic array with hashing
function FindBeforeAdd(Item: pointer; out wasAdded: boolean; aHashCode: cardinal): PtrInt;
Search an hashed element value for adding, updating the internal hash table
- trigger hashing if Count reaches CountTrigger
function FindBeforeDelete(Item: pointer): PtrInt;
Search and delete an element value, updating the internal hash table
function FindOrNew(aHashCode: cardinal; Item: pointer; aHashTableIndex: PPtrInt): PtrInt;
Returns position in array, or next void index in HashTable[] as -(index+1)
function FindOrNewComp(aHashCode: cardinal; Item: pointer; Comp: TDynArraySortCompare = nil): PtrInt;
Returns position in array, or -1 if not found with a custom comparer
function GetHashFromIndex(aIndex: PtrInt): cardinal;
Retrieve the low-level hash of a given item
function HashOne(Item: pointer): cardinal;
Compute the hash of a given item
function Scan(Item: pointer): PtrInt;
Search for an element value inside the dynamic array without hashing
procedure ForceReHash(duplicates: PInteger = nil);
Full computation of the internal hash table
- to be called after items have been manually updated - e.g. after Clear
- can return the number of duplicated values found (likely to be 0)
procedure Init(aDynArray: PDynArray; aHashItem: TDynArrayHashOne; const aEventHash: TOnDynArrayHashOne; aHasher: THasher; aCompare: TDynArraySortCompare; const aEventCompare: TOnDynArraySortCompare; aCaseInsensitive: boolean);
Initialize the hash table for a given dynamic array storage
- you can call this method several times, e.g. if aCaseInsensitive changed
procedure InitSpecific(aDynArray: PDynArray; aKind: TRttiParserType; aCaseInsensitive: boolean; aHasher: THasher);
Initialize a known hash table for a given dynamic array storage
- you can call this method several times, e.g. if aCaseInsensitive changed
property Compare: TDynArraySortCompare read fCompare;
Associated item comparison - may differ from DynArray^.Compare
property EventCompare: TOnDynArraySortCompare read fEventCompare write SetEventCompare;
Custom method-based comparison function
- should be set just after Init, when no item has been stored
property EventHash: TOnDynArrayHashOne read fEventHash write SetEventHash;
Custom method-based hashing function
- should be set just after Init, when no item has been stored
property Hasher: THasher read fHasher;
Associated item hasher
TDynArrayHashed = object(TDynArray)
Used to access any dynamic arrray items using fast hash
- by default, binary sort could be used for searching items for TDynArray: using a hash is faster on huge arrays for implementing a dictionary
- in this current implementation, modification (update or delete) of an element is not handled yet: you should rehash all content - only TDynArrayHashed.FindHashedForAdding / FindHashedAndUpdate / FindHashedAndDelete will refresh the internal hash
- this object extends the TDynArray type, since presence of Hashs[] dynamic array will increase code size if using TDynArrayHashed instead of TDynArray
- in order to have the better performance, you should use an external Count variable, AND set the Capacity property to the expected maximum count (this will avoid most re-hashing for FindHashedForAdding+FindHashedAndUpdate)
- consider using TSynDictionary from mormot.core.json for a thread-safe stand-alone storage of key/value pairs
function AddAndMakeUniqueName(aName: RawUtf8): pointer;
Search for a given element name, make it unique, and add it to the array
- expected element layout is to have a RawUtf8 field at first position
- the aName is searched (using hashing) to be unique, and if not the case, some suffix is added to make it unique, counting from _1 to _999
- use internally FindHashedForAdding method
- this version will set the field content with the unique value
- returns a pointer to the newly added element (to set other fields)
function AddUniqueName(const aName: RawUtf8; const ExceptionMsg: RawUtf8; const ExceptionArgs: array of const; aNewIndex: PPtrInt = nil): pointer; overload;
Ensure a given element name is unique, then add it to the array
- expected element layout is to have a RawUtf8 field at first position
- the aName is searched (using hashing) to be unique, and if not the case, an ESynException.CreateUtf8() is raised with the supplied arguments
- use internally FindHashedForAdding method
- this version will set the field content with the unique value
- returns a pointer to the newly added element (to set other fields)
function AddUniqueName(const aName: RawUtf8; aNewIndex: PPtrInt = nil): pointer; overload;
Ensure a given element name is unique, then add it to the array
- just a wrapper to AddUniqueName(aName,'',[],aNewIndex)
function FindFromHash(const Item; aHashCode: cardinal): PtrInt;
Search for an element value inside the dynamic array using its hash
- returns -1 if not found, or the index in the dynamic array if found
- aHashCode parameter constains an already hashed value of the item, to be used e.g. after a call to HashFind()
function FindHashed(const Item): PtrInt;
Search for an element value inside the dynamic array using hashing
- Item should be of the type expected by both the hash function and Compare/EventCompare methods: e.g. if the searched/hashed field contains a string as first field, you can safely use a string variable as Item
- Item must refer to a variable: e.g. you can't write FindHashed(i+10)
- will call fHashItem(Item,fHasher) to compute the needed hash
- returns -1 if not found, or the index in the dynamic array if found
function FindHashedAndDelete(const Item; FillDeleted: pointer = nil; noDeleteEntry: boolean = false): PtrInt;
Search for an element value inside the dynamic array using hashing, and delete it if matchs
- return the index deleted (0..Count-1), or -1 if Item was not found
- can optionally copy the deleted item to FillDeleted^ before erased
- Item should be of the type expected by Compare/EventCompare (no need to supply a full array item), and by design must refer to a variable: e.g. you can't write FindHashedAndDelete(i+10)
- it won't call slow ForceReHash but refresh the hash table as needed
function FindHashedAndFill(var ItemToFill): PtrInt;
Search for an element value inside the dynamic array using hashing, and fill ItemToFill with the found content
- return the index found (0..Count-1), or -1 if Item was not found
- ItemToFill should be of the type expected by the dynamic array, since all its fields will be set on match
function FindHashedAndUpdate(const Item; AddIfNotExisting: boolean): PtrInt;
Search for an element value inside the dynamic array using hashing, then update any matching item, or add the item if none matched
- by design, hashed field shouldn't have been modified by this update, otherwise the method won't be able to find and update the old hash: in this case, you should first call FindHashedAndDelete(OldItem) then FindHashedForAdding(NewItem) to properly handle the internal hash table
- if AddIfNotExisting is FALSE, returns the index found (0..Count-1), or -1 if Item was not found - Update will force slow rehash all content
- if AddIfNotExisting is TRUE, returns the index found (0..Count-1), or the index newly created/added is the Item value was not matching - add won't rehash all content - for even faster process (avoid rehash), please set the Capacity property
- Item should be of the type expected by the dynamic array, since its content will be copied into the dynamic array, and by design it must refer to a variable: e.g. you can't write FindHashedAndUpdate(i+10)
function FindHashedForAdding(const Item; out wasAdded: boolean; aHashCode: cardinal; noAddEntry: boolean = false): PtrInt; overload;
Search for an element value inside the dynamic array using hashing, and add a void entry to the array if was not found (unless noAddEntry is set)
- overloaded method accepting an already hashed value of the item, to be used e.g. after a call to HashFind()
function FindHashedForAdding(const Item; out wasAdded: boolean; noAddEntry: boolean = false): PtrInt; overload;
Search for an element value inside the dynamic array using hashing, and add a void entry to the array if was not found (unless noAddEntry is set)
- this method will use hashing for fast retrieval
- Item should be of the type expected by both the hash function and Compare/EventCompare methods: e.g. if the searched/hashed field contains a string as first field, you can safely use a string variable as Item
- returns either the index in the dynamic array if found (and set wasAdded to false), either the newly created index in the dynamic array (and set wasAdded to true)
- for faster process (avoid rehash), please set the Capacity property
- warning: in contrast to the Add() method, if an entry is added to the array (wasAdded=true), the entry is left VOID: you must set the field content to expecting value - in short, Item is used only for searching, not copied to the newly created entry in the array - check FindHashedAndUpdate() for a method actually copying Item fields
function Scan(const Item): PtrInt;
Search for an element value inside the dynamic array without hashing
- is preferred to Find(), since EventCompare would be used if defined
- Item should be of the type expected by Compare/EventCompare (no need to supply a full array item), and by design must refer to a variable: e.g. you can't write Scan(i+10)
- returns -1 if not found, or the index in the dynamic array if found
procedure ForceReHash;
Will recompute all hash from the current items of the dynamic array
- can be called on purpose, when modifications have been performed on the dynamic array content (e.g. in case of element deletion or update, or after calling LoadFrom/Clear method) - this is not necessary after FindHashedForAdding / FindHashedAndUpdate / FindHashedAndDelete methods
- returns the number of duplicated items found - which should be 0
procedure Init(aTypeInfo: PRttiInfo; var aValue; aHashItem: TDynArrayHashOne = nil; aCompare: TDynArraySortCompare = nil; aHasher: THasher = nil; aCountPointer: PInteger = nil; aCaseInsensitive: boolean = false);
Initialize the wrapper with a one-dimension dynamic array
- this version accepts some hash-dedicated parameters: aHashItem to set how to hash each element, aCompare to handle hash collision
- if no aHashItem is supplied, it will hash according to the RTTI, i.e. strings or binary types, and the first field for records (strings included)
- if no aCompare is supplied, it will use default Equals() method
- if no THasher function is supplied, it will use the one supplied in DefaultHasher global variable, set to crc32c() by default - using SSE4.2 instruction if available
- if CaseInsensitive is set to TRUE, it will ignore difference in 7-bit alphabetic characters (e.g. compare 'a' and 'A' as equal)
procedure InitRtti(aRtti: TRttiCustom; var aValue; aHashItem: TDynArrayHashOne = nil; aCompare: TDynArraySortCompare = nil; aHasher: THasher = nil; aCountPointer: PInteger = nil; aCaseInsensitive: boolean = false);
Initialize the wrapper with a one-dimension dynamic array from our RTTI
procedure InitSpecific(aTypeInfo: PRttiInfo; var aValue; aKind: TRttiParserType; aCountPointer: PInteger = nil; aCaseInsensitive: boolean = false; aHasher: THasher = nil);
Initialize the wrapper with a one-dimension dynamic array
- this version accepts to specify how both hashing and comparison should occur, setting the TRttiParserType kind of first/hashed field
- djNone and djCustom are too vague, and will raise an exception
- no RTTI check is made over the corresponding array layout: you shall ensure that aKind matches the dynamic array element definition
- aCaseInsensitive will be used for djRawUtf8..djHash512 text comparison
property EventCompare: TOnDynArraySortCompare read fHash.fEventCompare write SetEventCompare;
Alternative event-oriented Compare function to be used for Sort and Find
- will be used instead of Compare, to allow object-oriented callbacks
- should be set just after Init, when not item has been stored
property EventHash: TOnDynArrayHashOne read fHash.fEventHash write SetEventHash;
Alternative event-oriented Hash function
- this object-oriented callback will be used instead of HashItem() on each dynamic array entries - HashItem will still be used on const Item values, since they may be just a sub part of the stored entry
- should be set just after Init, when not item has been stored
property Hash[aIndex: PtrInt]: cardinal read GetHashFromIndex;
Retrieve the hash value of a given item, from its index
property Hasher: TDynArrayHasher read fHash;
Access to the internal hash table
- you can call e.g. Hasher.Clear to invalidate the whole hash table
property HashItem: TDynArrayHashOne read fHash.fHashItem;
Custom hash function used for hashing of a dynamic array element
TRawUtf8Hashed = object(TObject)
Store a TRawUtf8DynArray with its efficient hash table
procedure Init;
Initialize the RawUtf8 dynamic array and hasher
TRawUtf8InterningSlot = object(TObject)
Used to store one list of hashed RawUtf8 in TRawUtf8Interning pool
- Delphi "object" is buggy on stack -> also defined as record with methods
- each slot has its own TRWLightLock for efficient concurrent reads
function Clean(aMaxRefCount: TStrCnt): integer;
Reclaim any unique RawUtf8 values
- any string with an usage count <= aMaxRefCount will be removed
function Existing(const aText: RawUtf8; aTextHash: cardinal): pointer;
Return the interned value, if any
procedure Clear;
Delete all stored RawUtf8 values
procedure Init;
Initialize the RawUtf8 slot (and its Safe mutex)
procedure Unique(var aResult: RawUtf8; const aText: RawUtf8; aTextHash: cardinal);
Returns the interned RawUtf8 value
procedure UniqueFromBuffer(var aResult: RawUtf8; aText: PUtf8Char; aTextLen: PtrInt; aTextHash: cardinal); overload;
Returns the interned RawUtf8 value
- only allocates new aResult string if needed
procedure UniqueFromBuffer(var aResult: RawUtf8; aText: PUtf8Char; aTextLen: PtrInt); overload;
Returns the interned RawUtf8 value with no pre-computed hash
procedure UniqueText(var aText: RawUtf8; aTextHash: cardinal);
Ensure the supplied RawUtf8 value is interned
property Count: integer read fHash.Count;
How many items are currently stored in Value[]
TRawUtf8Interning = class(TRawUtf8InterningAbstract)
Allow to store only one copy of distinct RawUtf8 values
- thanks to the Copy-On-Write feature of string variables, this may reduce a lot the memory overhead of duplicated text content
- this class is thread-safe and optimized for performance
constructor Create(aHashTables: integer = 4); reintroduce;
Initialize the storage and its internal hash pools
- aHashTables is the pool size, and should be a power of two <= 512 (1, 2, 4, 8, 16, 32, 64, 128, 256, 512)
function Clean(aMaxRefCount: TStrCnt = 1): integer;
Reclaim any unique RawUtf8 values
- i.e. run a garbage collection process of all values with RefCount=1 by default, i.e. all string which are not used any more; you may set aMaxRefCount to a higher value, depending on your expecations, i.e. 2 to delete all string which are referenced only once outside of the pool
- returns the number of unique RawUtf8 cleaned from the internal pool
- to be executed on a regular basis - but not too often, since the process can be time consumming, and void the benefit of interning
function Count: integer;
How many items are currently stored in this instance
function Existing(const aText: RawUtf8): pointer;
Check if a RawUtf8 value is already stored within this class
- if not existing, returns nil and don't add it to the pool
- if existing, returns pointer(fValue[i]) of the unique stored RawUtf8
- use e.g. for very fast per-pointer lookup of interned property names
function Unique(const aText: RawUtf8): RawUtf8; overload;
Return a RawUtf8 variable stored within this class
- if aText occurs for the first time, add it to the internal string pool
- if aText does exist in the internal string pool, return the shared instance (with its reference counter increased), to reduce memory usage
function Unique(aText: PUtf8Char; aTextLen: PtrInt): RawUtf8; overload;
Return a RawUtf8 variable stored within this class from a text buffer
- if aText occurs for the first time, add it to the internal string pool
- if aText does exist in the internal string pool, return the shared instance (with its reference counter increased), to reduce memory usage
procedure Clear;
Delete any previous storage pool
procedure Unique(var aResult: RawUtf8; const aText: RawUtf8); overload;
Return a RawUtf8 variable stored within this class
- if aText occurs for the first time, add it to the internal string pool
- if aText does exist in the internal string pool, return the shared instance (with its reference counter increased), to reduce memory usage
procedure Unique(var aResult: RawUtf8; aText: PUtf8Char; aTextLen: PtrInt); overload;
Return a RawUtf8 variable stored within this class from a text buffer
- if aText occurs for the first time, add it to the internal string pool
- if aText does exist in the internal string pool, return the shared instance (with its reference counter increased), to reduce memory usage
- this method won't allocate any memory if aText is already interned
procedure UniqueText(var aText: RawUtf8);
Ensure a RawUtf8 variable is stored within this class
- if aText occurs for the first time, add it to the internal string pool
- if aText does exist in the internal string pool, set the shared instance (with its reference counter increased), to reduce memory usage
procedure UniqueVariant(var aResult: variant; const aText: RawUtf8); overload;
Return a variant containing a RawUtf8 stored within this class
- similar to RawUtf8ToVariant(), but with string interning
- see also UniqueVariant() from mormot.core.variants if you want to intern only non-numerical values
procedure UniqueVariant(var aResult: variant); overload;
Ensure a variant contains only RawUtf8 stored within this class
- supplied variant should be a varString containing a RawUtf8 value
procedure UniqueVariantString(var aResult: variant; const aText: string);
Return a variant containing a RawUtf8 stored within this class
- similar to RawUtf8ToVariant(StringToUtf8()), but with string interning
- this method expects the text to be supplied as a RTL string, which will be converted into a variant containing a RawUtf8 varString instance
TRawUtf8List = class(TSynPersistentRWLock)
Thread-safe TStringList-class optimized for our native UTF-8 string type
- can optionally store associated some TObject instances
- high-level methods of this class are thread-safe
- if fNoDuplicate flag is defined, an internal hash table will be maintained to perform IndexOf() lookups in O(1) linear way
- not thread-safe by default, unless fThreadSafe is set to use the TRWLock
constructor Create(aOwnObjects: boolean; aNoDuplicate: boolean = false; aCaseSensitive: boolean = true); reintroduce; overload;
Backward compatiliby overloaded constructor
- please rather use the overloaded CreateEx(TRawUtf8ListFlags)
- for instance, Create(true) is CreateEx([fObjectsOwned, fCaseSensitive]);
constructor Create; overload; override;
Initialize the RawUtf8/Objects storage with [fCaseSensitive] flags
constructor CreateEx(aFlags: TRawUtf8ListFlags);
Initialize the RawUtf8/Objects storage with extended flags
- by default, any associated Objects[] are just weak references; you may supply fOwnObjects flag to force object instance management
- if you want the stored text items to be unique, set fNoDuplicate and then an internal hash table will be maintained for fast IndexOf()
- you can set fCaseSensitive to let the UTF-8 lookup be case-sensitive
- not thread-safe by default, unless fThreadSafe is set to use a R/W lock
- is defined as CreateEx instead of overload Create to avoid weird Delphi compilation issues, especially within packages
destructor Destroy; override;
Finalize the internal objects stored
- if instance was created with fOwnObjects flag
function Add(const aText: RawUtf8; aRaiseExceptionIfExisting: boolean = false): PtrInt;
Store a new RawUtf8 item
- without the fNoDuplicate flag, it will always add the supplied value
- if fNoDuplicate was set and aText already exists (using the internal hash table), it will return -1 unless aRaiseExceptionIfExisting is forced
- thread-safe method
function AddObject(const aText: RawUtf8; aObject: TObject; aRaiseExceptionIfExisting: boolean = false; aFreeAndReturnExistingObject: PPointer = nil; aReplaceExistingObject: boolean = false): PtrInt;
Store a new RawUtf8 item, and its associated TObject
- without the fNoDuplicate flag, it will always add the supplied value
- if fNoDuplicate was set and aText already exists (using the internal hash table), it will return -1 unless aRaiseExceptionIfExisting is forced; optionally freeing the supplied aObject if aFreeAndReturnExistingObject is set, in which pointer the existing Objects[] is copied (see AddObjectUnique as a convenient wrapper around this behavior); if aFreeAndReturnExistingObject is nil, and aReplaceExistingObject is true, the existing object is freed and replaced by aObject
- thread-safe method
function AddOrReplaceObject(const aText: RawUtf8; aObject: TObject): PtrInt;
Force the storage of a RawUtf8 item, and its associated TObject
- without the fNoDuplicate flag, it will always add the supplied value
- if fNoDuplicate was set and aText already exists (using the internal hash table), it will free any existing Objects[] and put aObject in its place
- thread-safe method, using an internal Hash Table to speedup IndexOf()
function Contains(const aText: RawUtf8; aFirstIndex: integer = 0): PtrInt;
Search for any RawUtf8 item containing some text
- uses PosEx() on the stored lines
- this method is not thread-safe since the internal list may change and the returned index may not be accurate any more
- by design, aText lookup can't use the internal Hash Table
function Delete(const aText: RawUtf8): PtrInt; overload;
Delete a stored RawUtf8 item, and its associated TObject
- will search for the value using IndexOf(aText), and returns its index
- returns -1 if no entry was found and deleted
- thread-safe method, using the internal Hash Table if fNoDuplicate is set
function DeleteFromName(const Name: RawUtf8): PtrInt; virtual;
Delete a stored RawUtf8 item, and its associated TObject, from a given Name when stored as 'Name=Value' pairs
- raise no exception in case of out of range supplied index
- thread-safe method, but not using the internal Hash Table
- consider using TSynNameValue if you expect efficient name/value process
function EqualValueAt(Index: PtrInt; const aText: RawUtf8): boolean;
Compare a Value with some RawUtf8 text
- this method is not thread-safe
function Exists(const aText: RawUtf8): boolean;
Find a RawUtf8 item in the stored Strings[] list
- search is case sensitive if fCaseSensitive flag was set (default)
- this method is thread-safe
- uses the internal Hash Table if fNoDuplicate was set
function GetObjectFrom(const aText: RawUtf8): pointer;
Get a stored Object item by its associated UTF-8 text
- returns nil and raise no exception if aText doesn't exist
- thread-safe method, unless returned TObject is deleted in the background
function GetText(const Delimiter: RawUtf8 = #13#10): RawUtf8;
Retrieve the all lines, separated by the supplied delimiter
- this method is thread-safe
function GetValueAt(Index: PtrInt): RawUtf8;
Access to the Value of a given 'Name=Value' pair at a given position
- this method is not thread-safe
- consider using TSynNameValue if you expect efficient name/value process
function IndexOf(const aText: RawUtf8): PtrInt;
Find a RawUtf8 item in the stored Strings[] list
- this search is case sensitive if fCaseSensitive flag was set (which is the default)
- this method is not thread-safe since the internal list may change and the returned index may not be accurate any more
- see also Exists() and GetObjectFrom() method
- uses the internal Hash Table if fNoDuplicate was set
function IndexOfName(const Name: RawUtf8): PtrInt;
Find the index of a given Name when stored as 'Name=Value' pairs
- search on Name is case-insensitive with 'Name=Value' pairs
- this method is not thread-safe, and won't use the internal Hash Table
- consider using TSynNameValue if you expect efficient name/value process
function IndexOfObject(aObject: TObject): PtrInt;
Find a TObject item index in the stored Objects[] list
- this method is not thread-safe since the internal list may change and the returned index may not be accurate any more
- aObject lookup won't use the internal Hash Table
function PopFirst(out aText: RawUtf8; aObject: PObject = nil): boolean;
Retrieve and delete the first RawUtf8 item in the list
- could be used as a FIFO, calling Add() as a "push" method
- thread-safe method
function PopLast(out aText: RawUtf8; aObject: PObject = nil): boolean;
Retrieve and delete the last RawUtf8 item in the list
- could be used as a FILO, calling Add() as a "push" method
- thread-safe method
function UpdateValue(const Name: RawUtf8; var Value: RawUtf8; ThenDelete: boolean): boolean;
Retrieve Value from an existing Name=Value, then optinally delete the entry
- if Name is found, will fill Value with the stored content and return true
- if Name is not found, Value is not modified, and false is returned
- thread-safe method, but not using the internal Hash Table
- consider using TSynNameValue if you expect efficient name/value process
procedure AddObjectUnique(const aText: RawUtf8; aObjectToAddOrFree: PPointer);
Try to store a new RawUtf8 item and its associated TObject
- fNoDuplicate should have been specified in the list flags
- if aText doesn't exist, will add the values
- if aText exist, will call aObjectToAddOrFree.Free and set the value already stored in Objects[] into aObjectToAddOrFree - allowing dual commit thread-safe update of the list, e.g. after a previous unsuccessful call to GetObjectFrom(aText)
- thread-safe method, using an internal Hash Table to speedup IndexOf()
- in fact, this method is just a wrapper around
AddObject(aText,aObjectToAddOrFree^,false,@aObjectToAddOrFree);
procedure AddRawUtf8List(List: TRawUtf8List);
Append a specified list to the current content
- thread-safe method
procedure BeginUpdate;
The OnChange event will be raised only when EndUpdate will be called
- this method will also call Safe.Lock for thread-safety
procedure Clear; virtual;
Erase all stored RawUtf8 items
- and corresponding objects (if aOwnObjects was true at constructor)
- thread-safe method, also clearing the internal Hash Table
procedure Delete(Index: PtrInt); overload;
Delete a stored RawUtf8 item, and its associated TObject
- raise no exception in case of out of range supplied index
- this method is not thread-safe: use Safe.Lock/UnLock if needed
procedure EndUpdate;
Call the OnChange event if changes occurred
- this method will also call Safe.UnLock for thread-safety
procedure LoadFromFile(const FileName: TFileName);
Set all lines from a text file
- will assume text file with no BOM is already UTF-8 encoded
- this method is thread-safe
procedure SaveToFile(const FileName: TFileName; const Delimiter: RawUtf8 = #13#10);
Write all lines into a new UTF-8 file
- this method is thread-safe
procedure SaveToStream(Dest: TStream; const Delimiter: RawUtf8 = #13#10);
Write all lines into the supplied stream
- this method is thread-safe
procedure SetFrom(const aText: TRawUtf8DynArray; const aObject: TObjectDynArray);
Set low-level text and objects from existing arrays
procedure SetText(const aText: RawUtf8; const Delimiter: RawUtf8 = #13#10);
Set all lines, separated by the supplied delimiter
- this method is thread-safe
property Capacity: PtrInt read GetCapacity write SetCapacity;
Set or retrieve the current memory capacity of the RawUtf8 list
- reading this property is not thread-safe, since size may change
property CaseSensitive: boolean read GetCaseSensitive write SetCaseSensitive;
Set if IndexOf() shall be case sensitive or not
- default is TRUE
- matches fCaseSensitive in Flags
property Count: PtrInt read GetCount;
Return the count of stored RawUtf8
- reading this property is not thread-safe, since size may change
property Flags: TRawUtf8ListFlags read fFlags write fFlags;
Access to the low-level flags of this list
property Names[Index: PtrInt]: RawUtf8 read GetName;
Retrieve the corresponding Name when stored as 'Name=Value' pairs
- reading this property is not thread-safe, since content may change
- consider TSynNameValue if you expect more efficient name/value process
property NameValueSep: AnsiChar read fNameValueSep write fNameValueSep;
The char separator between 'Name=Value' pairs
- equals '=' by default
- consider TSynNameValue if you expect more efficient name/value process
property NoDuplicate: boolean read GetNoDuplicate;
Set if the list doesn't allow duplicated UTF-8 text
- if true, an internal hash table is maintained for faster IndexOf()
- matches fNoDuplicate in Flags
property ObjectPtr: PPointerArray read GetObjectPtr;
Direct access to the memory of the TObjectDynArray items
- reading this property is not thread-safe, since content may change
property Objects[Index: PtrInt]: pointer read GetObject write PutObject;
Get or set a Object item
- returns nil and raise no exception in case of out of range supplied index
- reading this property is not thread-safe, since content may change
property OnChange: TNotifyEvent read fOnChange write fOnChange;
Event triggered when an entry is modified
property Str[Index: PtrInt]: string read GetS write PutS;
Get or set an item as RTL string, ready to be used with the UI
- returns '' and raise no exception in case of out of range supplied index
- wrap Strings[] with Utf8ToString/StringToUtf8 functions
- reading this property is not thread-safe, since content may change
property Strings[Index: PtrInt]: RawUtf8 read Get write Put;
Get or set a RawUtf8 item
- returns '' and raise no exception in case of out of range supplied index
- if you want to use it with the UI, use Utf8ToString() function
- reading this property is not thread-safe, since content may change
property Text: RawUtf8 read GetTextCRLF write SetTextCRLF;
Set or retrieve all items as text lines
- lines are separated by #13#10 (CRLF) by default; use GetText and SetText methods if you want to use another line delimiter (even a comma)
- this property is thread-safe
property TextPtr: PPUtf8CharArray read GetTextPtr;
Direct access to the memory of the TRawUtf8DynArray items
- reading this property is not thread-safe, since content may change
property ValuePtr: TRawUtf8DynArray read fValue;
Direct access to the TRawUtf8DynArray instance
- reading this property is not thread-safe, since content may change
property Values[const Name: RawUtf8]: RawUtf8 read GetValue write SetValue;
Access to the corresponding 'Name=Value' pairs
- search on Name is case-insensitive with 'Name=Value' pairs
- reading this property is thread-safe, but won't use the hash table
- consider TSynNameValue if you expect more efficient name/value process
property ValuesArray: TDynArrayHashed read fValues;
Direct access to the TRawUtf8DynArray items dynamic array wrapper
- using this property is not thread-safe, since content may change
TRawUtf8ListLocked = class(TRawUtf8List)
Some declarations used for backward compatibility only
TRadixTreeNode = class(TObject)
Implement an abstract Radix Tree node
Chars: RawUtf8;
The characters to be compared at this level
Child: array of TRadixTreeNode;
The nested nodes
Depth: integer;
How many branches are within this node - used to sort by priority
Flags: TRadixTreeNodeFlags;
Describe the content of this node
FullText: RawUtf8;
The whole text up to this level
Owner: TRadixTree;
The main Tree holding this node
constructor Create(aOwner: TRadixTree); reintroduce;
Initialize this node instance
destructor Destroy; override;
Finalize this Radix Tree node
function Find(P: PUtf8Char): TRadixTreeNode;
Search for the node corresponding to a given text
function Split(const Text: RawUtf8): TRadixTreeNode; virtual;
Instantiate a new node with the same class and properties
procedure ToText(var Result: RawUtf8; Level: integer);
Internal debugging/testing method
TRadixTree = class(TObject)
Implement an abstract Radix Tree over UTF-8 case-insensitive text
- as such, this class is not very useful if you just need to lookup for a text value: a TDynArrayHasher/TDictionary is faster and uses less RAM
- but, once extended e.g. as TUriTree, it can very efficiently parse some text with variants parts (e.g. parameters)
constructor Create(aNodeClass: TRadixTreeNodeClass; aOptions: TRadixTreeOptions = []); reintroduce;
For efficient rtoCaseInsensitiveUri initialize the Radix Tree
destructor Destroy; override;
Finalize this Radix Tree
function Find(const Text: RawUtf8): TRadixTreeNode;
Search for the node corresponding to a given text
- more than 6 million lookups per second, with 1000 items stored
function Insert(Text: RawUtf8; Node: TRadixTreeNode = nil; NodeClass: TRadixTreeNodeClass = nil): TRadixTreeNode;
Low-level insertion of a given Text entry as a given child
- may return an existing node instance, if Text was already inserted
function ToText: RawUtf8;
Internal debugging/testing method
procedure AfterInsert;
To be called after Insert() to consolidate the internal tree state
- nodes will be sorted by search priority, i.e. the longest depths first
- as called e.g. by TUriTree.Setup()
procedure Clear;
Finalize this Radix Tree node
property Options: TRadixTreeOptions read fOptions;
Define how TRadixTreeNode.Lookup() will process this node
- as set with this class constructor
property Root: TRadixTreeNode read fRoot;
Low-level access to the root node of the Radix Tree
TRadixTreeNodeParams = class(TRadixTreeNode)
Implement an abstract Radix Tree static or <param> node
Names: TRawUtf8DynArray;
All the <param1> <param2> names, in order, up to this parameter
- equals nil for static nodes
- is referenced as pointer into THttpServerRequestAbstract.fRouteName
function Lookup(P: PUtf8Char; Ctxt: TObject): TRadixTreeNodeParams;
Main search method, recognizing static or <param> patterns
function Split(const Text: RawUtf8): TRadixTreeNode; override;
Overriden to support the additional Names fields
TRadixTreeParams = class(TRadixTree)
Implement an abstract Radix Tree with static or <param> nodes
function Setup(const aFromUri: RawUtf8; out aNames: TRawUtf8DynArray): TRadixTreeNodeParams;
Low-level registration of a new URI path, with <param> support
- returns the node matching the given URI
- called e.g. from TUriRouter.Rewrite/Run methods
- will recognize <param> alphanumerical and <int:id> integer parameters
PDocVariantOptions = ^TDocVariantOptions;
Pointer to a set of options for a TDocVariant storage
- defined in this unit to avoid circular reference with mormot.core.variants
- use e.g. @JSON_[mFast], @JSON_[mDefault], or any other TDocVariantModel
PDynArray = ^TDynArray;
A pointer to a TDynArray Wrapper instance
PDynArrayHasher = ^TDynArrayHasher;
Pointer to a TDynArrayHasher instance
TDocVariantOption = ( dvoIsArray, dvoIsObject, dvoNameCaseSensitive, dvoCheckForDuplicatedNames, dvoReturnNullForUnknownProperty, dvoValueCopiedByReference, dvoJsonParseDoNotTryCustomVariants, dvoJsonParseDoNotGuessCount, dvoJsonObjectParseWithinString, dvoSerializeAsExtendedJson, dvoAllowDoubleValue, dvoInternNames, dvoInternValues );
Possible options for a TDocVariant JSON/BSON document storage
- defined in this unit to avoid circular reference with mormot.core.variants
- dvoIsArray and dvoIsObject will store the "Kind: TDocVariantKind" state - you should never have to define these two options directly
- dvoNameCaseSensitive will be used for every name lookup - here case-insensitivity is restricted to a-z A-Z 0-9 and _ characters
- dvoCheckForDuplicatedNames will be used for method TDocVariantData.AddValue(), but not when setting properties at variant level: for consistency, "aVariant.AB := aValue" will replace any previous value for the name "AB"
- dvoReturnNullForUnknownProperty will be used when retrieving any value from its name (for dvObject kind of instance), or index (for dvArray or dvObject kind of instance)
- by default, internal values will be copied by-value from one variant instance to another, to ensure proper safety - but it may be too slow: if you set dvoValueCopiedByReference, the internal TDocVariantData.VValue/VName instances will be copied by-reference, to avoid memory allocations, BUT it may break internal process if you change some values in place (since VValue/VName and VCount won't match) - as such, if you set this option, ensure that you use the content as read-only
- any registered custom types may have an extended JSON syntax (e.g. TBsonVariant does for MongoDB types), and will be searched during JSON parsing, unless dvoJsonParseDoNotTryCustomVariants is set (slightly faster)
- the parser will try to guess the array or object size by pre-fetching some content: you can set dvoJsonParseDoNotGuessCount if your input has a lot of nested documents, and manual resize is preferred - this option will be forced by InitJson if a huge nest of objects is detected
- by default, it will only handle direct JSON [array] of {object}: but if you define dvoJsonObjectParseWithinString, it will also try to un-escape a JSON string first, i.e. handle "[array]" or "{object}" content (may be used e.g. when JSON has been retrieved from a database TEXT column) - is used for instance by VariantLoadJson()
- JSON serialization will follow the standard layout, unless dvoSerializeAsExtendedJson is set so that the property names would not be escaped with double quotes, writing '{name:"John",age:123}' instead of '{"name":"John","age":123}': this extended json layout is compatible with http://docs.mongodb.org/manual/reference/mongodb-extended-json and with TDocVariant JSON unserialization, also our SynCrossPlatformJSON unit, but NOT recognized by most JSON clients, like AJAX/JavaScript or C#/Java
- by default, only integer/Int64/currency number values are allowed, unless dvoAllowDoubleValue is set and 32-bit floating-point conversion is tried, with potential loss of precision during the conversion
- dvoInternNames and dvoInternValues will use shared TRawUtf8Interning instances to maintain a list of RawUtf8 names/values for all TDocVariant, so that redundant text content will be allocated only once on heap
- see JSON_[TDocVariantModel] and all JSON_* constants as useful sets, and TDocVariantData.IsObject/IsArray/IsCaseSensitive/Has wrapper methods
TDocVariantOptions = set of TDocVariantOption;
Set of options for a TDocVariant storage
- defined in this unit to avoid circular reference with mormot.core.variants
- see JSON_[TDocVariantModel] and all JSON_* constants (e.g. JSON_FAST or JSON_FAST_FLOAT) as potential values
- when specifying the options, you should not include dvoIsArray nor dvoIsObject directly in the set, but explicitly define TDocVariantDataKind
- see TDocVariantData.IsObject/IsArray/IsCaseSensitive/Has wrapper methods which are faster than 16-bit "if dvo* in VOptions" on Intel
TDocVariantOptionsBool = array[boolean] of TDocVariantOptions;
A boolean array of TDocVariant storage options
TDynArrayHashOne = function(const Item; Hasher: THasher): cardinal;
Function prototype to be used for hashing of a dynamic array element
- this function must use the supplied hasher on the Item data
TDynArrayKind = TRttiParserType;
Internal enumeration used to specify some standard arrays
- mORMot 1.18 did have two serialization engines - we unified it
- defined only for backward compatible code; use TRttiParserType instead
TDynArraySortCompare = function(const A, B): integer;
Function prototype to be used for TDynArray Sort and Find method
- common functions exist for base types: see e.g. SortDynArrayBoolean, SortDynArrayByte, SortDynArrayWord, SortDynArrayInteger, SortDynArrayCardinal, SortDynArrayInt64, SortDynArrayQWord, SordDynArraySingle, SortDynArrayDouble, SortDynArrayAnsiString, SortDynArrayAnsiStringI, SortDynArrayUnicodeString, SortDynArrayUnicodeStringI, SortDynArrayString, SortDynArrayStringI
- any custom type (even records) can be compared then sort by defining such a custom function
- must return 0 if A=B, -1 if A<B, 1 if A>B
TInterfacedCollectionClass = class of TInterfacedCollection;
Class-reference type (metaclass) of a TInterfacedCollection kind
TInterfacedObjectWithCustomCreateClass = class of TInterfacedObjectWithCustomCreate;
Used to determine the exact class type of a TInterfacedObjectWithCustomCreate
- could be used to create instances using its virtual constructor
TOnDynArrayHashOne = function(const Item): cardinal of object;
Event handler to be used for hashing of a dynamic array element
- can be set as an alternative to TDynArrayHashOne
TOnDynArraySortCompare = function(const A, B): integer of object;
Event oriented version of TDynArraySortCompare
TOnNotifySortedIntegerChange = procedure(const Sender; Value: integer) of object;
Event handler called by NotifySortedIntegerChanges()
- Sender is an opaque const value, maybe a TObject or any pointer
TOnObjectCompare = function(A, B: TObject): integer;
Event used by TSynObjectListSorted to compare its instances
TOnValueGreater = function(IndexA, IndexB: PtrInt): boolean of object;
Comparison function as expected by MedianQuickSelect()
- should return TRUE if Values[IndexA]>Values[IndexB]
TPersistentWithCustomCreateClass = class of TPersistentWithCustomCreate;
Used to determine the exact class type of a TPersistentWithCustomCreateClass
- could be used to create instances using its virtual constructor
TRadixTreeNodeClass = class of TRadixTreeNode;
Our TRadixTree works on dynamic/custom types of node classes
TRadixTreeNodeFlags = set of ( rtfParam, rtfParamInteger, rtfParamPath);
Refine the TRadixTreeNode content
- rtfParam is <param> node, i.e. a TRadixTreeNodeParams with Names <> nil
- rtfParamInteger is for a rtfParam which value should be only an integer, either from rtoIntegerParams global flag, or individually as <int:###>
- rtfParamPath is for a rtfParam which value should be the whole path, until the end of the URI or the beginning of the parameters (i.e. at '?'), set individually as <path:###> parameter - * being synonymous to <path:path>
TRadixTreeOptions = set of ( rtoCaseInsensitiveUri, rtoIntegerParams);
Allow to customize TRadixTree process
- e.g. if static text matching should be case-insensitive (but <params> are always case-sensitive, because they are user-specific runtime variables)
- if <param> values should be only plain integers, never alphabetical text - you may also specify int:xxx for a single parameter, e.g. as <int:id>
TRawUtf8ListFlags = set of ( fObjectsOwned, fCaseSensitive, fNoDuplicate, fOnChangeTrigerred, fThreadSafe);
Possible values used by TRawUtf8List.Flags
TRttiBinaryLoad = function(Data: pointer; var Source: TFastReader; Info: PRttiInfo): PtrInt;
Internal function handler for binary persistence of any RTTI type value
- i.e. the kind of functions called via RTTI_BINARYLOAD[] lookup table
- work with managed and unmanaged types
- fill Data^ from Source, returning the size in Data^ as bytes
TRttiBinaryLoads = array[TRttiKind] of TRttiBinaryLoad;
The type of RTTI_BINARYLOAD[] efficient lookup table
TRttiBinarySave = function(Data: pointer; Dest: TBufferWriter; Info: PRttiInfo): PtrInt;
Internal function handler for binary persistence of any RTTI type value
- i.e. the kind of functions called via RTTI_BINARYSAVE[] lookup table
- work with managed and unmanaged types
- persist Data^ into Dest, returning the size in Data^ as bytes
TRttiBinarySaves = array[TRttiKind] of TRttiBinarySave;
The type of RTTI_BINARYSAVE[] efficient lookup table
TRttiCompare = function(Data1, Data2: pointer; Info: PRttiInfo; out Compared: integer): PtrInt;
Internal function handler for fast comparison of any RTTI type value
- i.e. the kind of functions called via RTTI_COMPARE[] lookup table
- work with managed and unmanaged types
- returns the size in Data1/Data2^ as bytes, and the result in Compared
TRttiCompares = array[TRttiKind] of TRttiCompare;
The type of RTTI_COMPARE[] efficient lookup table
TSynObjectListClass = class of TSynObjectList;
Meta-class of TSynObjectList type
TSynPersistentClass = class of TSynPersistent;
Used to determine the exact class type of a TSynPersistent
djNone = ptNone;
Deprecated TDynArrayKind enumerate mapping
- defined only for backward compatible code; use TRttiParserType instead
HASH_PO2 = 1 shl 18;
- and Delphi Win32 is not efficient at 64-bit multiplication, anyway use 16-bit Hash table when indexes fit in a word (array Capacity < 65535)
- to reduce memory consumption and slightly enhance CPU cache efficiency
- e.g. arrays of size 1..127 use only 256*2=512 bytes for their hash table defined for inlining bitwise division in TDynArrayHasher.HashTableIndex
- HashTableSize<=HASH_PO2 is expected to be a power of two (fast binary op); limit is set to 262,144 hash table slots (=512KB), for Capacity=131,072 items
- above this limit, a set of increasing primes is used; using a prime as hashtable modulo enhances its distribution, especially for a weak hash function
- 64-bit CPU and FPC can efficiently compute a prime reduction using Lemire algorithm, but power of two sizes still have a better practical performance for lower (and most common) content until it consumes too much memory
SORT_LSTRING: array[boolean] of TDynArraySortCompare = ( SortDynArrayAnsiString, SortDynArrayAnsiStringI);
Redirect to the proper SortDynArrayAnsiString/SortDynArrayAnsiStringI
| Functions or procedures | Description | |
|---|---|---|
| AnyScanExists | Fast search of a binary value position in a fixed-size array | |
| AnyScanIndex | Fast search of a binary value position in a fixed-size array | |
| BinaryCompare | Comparison of two arrays of values by content, using RTTI | |
| BinaryCompare | Comparison of two values by content, using RTTI | |
| BinaryEquals | Check equality of two values by content, using RTTI | |
| BinaryLoad | Unserialize any value from BinarySave() memory buffer, using RTTI | |
| BinaryLoad | Unserialize any value from BinarySave() RawByteString, using RTTI | |
| BinaryLoadBase64 | Unserialize any value from BinarySaveBase64() encoding, using RTTI | |
| BinarySave | Binary persistence of any value using RTTI, into a TBufferWriter stream | |
| BinarySave | Binary persistence of any value using RTTI, into a RawByteString buffer | |
| BinarySave | Binary persistence of any value using RTTI, into a memory buffer | |
| BinarySave | Binary persistence of any value using RTTI, into a TSynTempBuffer buffer | |
| BinarySaveBase64 | Binary persistence of any value using RTTI, into a Base64-encoded text | |
| BinarySaveBytes | Binary persistence of any value using RTTI, into a TBytes buffer | |
| BinarySaveLength | How many bytes a BinarySave() may return | |
| CopyAndSortInt64 | Copy an integer array, then sort it, low values first | |
| CopyAndSortInteger | Copy an integer array, then sort it, low values first | |
| CopyInt64 | Create a new 64-bit integer dynamic array with the values from another one | |
| CopyInteger | Create a new 32-bit integer dynamic array with the values from another one | |
| DeduplicateInt64 | Sort and remove any 64-bit duplicated integer from Values[] | |
| DeduplicateInt64 | Sort and remove any 64-bit duplicated integer from Values[] | |
| DeduplicateInt64Sorted | Low-level function called by DeduplicateInt64() | |
| DeduplicateInteger | Sort and remove any 32-bit duplicated integer from Values[] | |
| DeduplicateInteger | Sort and remove any 32-bit duplicated integer from Values[] | |
| DeduplicateIntegerSorted | Low-level function called by DeduplicateInteger() | |
| DeleteSection | Delete a whole [Section] | |
| DeleteSection | Delete a whole [Section] | |
| DynArray | Initialize the structure with a one-dimension dynamic array | |
| DynArrayAdd | Wrapper around TDynArray.Add | |
| DynArrayCompare | Raw comparison of two dynamic arrays | |
| DynArrayDelete | Wrapper around TDynArray.Delete | |
| DynArrayEquals | Compare two dynamic arrays by calling TDynArray.Equals | |
| DynArrayHashOne | Get the hash function corresponding to a given standard array type | |
| DynArrayLoad | Fill a dynamic array content from a binary serialization as saved by DynArraySave() / TDynArray.Save() | |
| DynArrayLoadHeader | Low-level binary unserialization as saved by DynArraySave/TDynArray.Save | |
| DynArraySave | Serialize a dynamic array content as binary, ready to be loaded by DynArrayLoad() / TDynArray.Load() | |
| DynArraySave | Raw binary serialization of a dynamic array | |
| DynArraySortIndexed | Sort any dynamic array, generating an external array of indexes | |
| DynArraySortIndexed | Sort any dynamic array, via a supplied array of indexes | |
| DynArraySortOne | Get the comparison function corresponding to a given standard array type | |
| ExcludeInt64 | Remove some 64-bit integer from Values[] | |
| ExcludeInteger | Remove some 32-bit integer from Values[] | |
| ExistsIniName | Return TRUE if Value of UpperName does exist in P, till end of current section | |
| ExistsIniNameValue | Return TRUE if one of the Value of UpperName exists in P, till end of current section | |
| FindIniEntry | Find a Name= Value in a [Section] of a INI RawUtf8 Content | |
| FindIniEntryFile | Find a Name= Value in a [Section] of a .INI file | |
| FindIniEntryInteger | Find a Name= numeric Value in a [Section] of a INI RawUtf8 Content and return it as an integer, or 0 if not found | |
| FindIniNameValue | Find the Value of UpperName in P, till end of current section | |
| FindIniNameValueInteger | Find the integer Value of UpperName in P, till end of current section | |
| FindSectionFirstLine | Find the position of the [SEARCH] section in source | |
| FindSectionFirstLineW | Find the position of the [SEARCH] section in source | |
| FindWinAnsiIniEntry | Find a Name= Value in a [Section] of a INI WinAnsi Content | |
| GetSectionContent | Retrieve the whole content of a section as a string | |
| GetSectionContent | Retrieve the whole content of a section as a string | |
| IncludeInt64 | Ensure some 64-bit integer from Values[] will only contain Included[] | |
| IncludeInteger | Ensure some 32-bit integer from Values[] will only contain Included[] | |
| IniToObject | Fill a class Instance properties from an .ini content | |
| Int64ToUInt32 | Copy some Int64 values into an unsigned integer array | |
| IsHtmlContentTypeTextual | Returns TRUE if the supplied HTML Headers contains 'Content-Type: text/...', 'Content-Type: application/json' or 'Content-Type: application/xml' | |
| IsWebSocketUpgrade | Search if the WebSocketUpgrade() header is present | |
| MaxInt64 | Find the maximum 64-bit integer in Values[] | |
| MaxInteger | Find the maximum 32-bit integer in Values[] | |
| MedianQuickSelect | Compute the median of a serie of values, using "Quickselect" | |
| MedianQuickSelectInteger | Compute the median of an integer serie of values, using "Quickselect" | |
| NotifySortedIntegerChanges | Compares two 32-bit signed sorted integer arrays, and call event handlers to notify the corresponding modifications in an O(n) time | |
| ObjArraySort | Sort any TObjArray with a given comparison function | |
| ObjectCompare | Comparison of two TObject published properties, using RTTI | |
| ObjectCompare | Comparison of published properties of several TObject instances, using RTTI | |
| ObjectEquals | Case-sensitive comparison of two TObject published properties, using RTTI | |
| ObjectEqualsI | Case-insensitive comparison of two TObject published properties, using RTTI | |
| ObjectToIni | Serialize a class Instance properties into an .ini content | |
| QuickSortIndexedPUtf8Char | Deprecated TRawUtf8MethodList should be replaced by a TSynDictionary sort a dynamic array of PUtf8Char items, via an external array of indexes | |
| RecordEquals | Check equality of two records by content | |
| RecordLoad | Fill a record content from a memory buffer as saved by RecordSave() | |
| RecordLoad | Fill a record content from a memory buffer as saved by RecordSave() | |
| RecordLoadBase64 | Read a record content from a Base64 encoded content | |
| RecordSave | Save a record content into a RawByteString | |
| RecordSave | Save a record content into a destination memory buffer | |
| RecordSave | Save a record content into a destination memory buffer | |
| RecordSave | Save a record content into a destination memory buffer | |
| RecordSaveBase64 | Save a record content into a Base64 encoded UTF-8 text content | |
| RecordSaveBytes | Save a record content into a TBytes dynamic array | |
| RecordSaveLength | Compute the number of bytes needed to save a record content using the RecordSave() function | |
| ReplaceSection | Replace a whole [Section] content by a new content | |
| ReplaceSection | Replace a whole [Section] content by a new content | |
| Reverse | Fill already allocated Reversed[] so that Reversed[Values[i]]=i | |
| SumInteger | Sum all 32-bit integers in Values[] | |
| UpdateIniEntry | Update a Name= Value in a [Section] of a INI RawUtf8 Content | |
| UpdateIniEntryFile | Update a Name= Value in a [Section] of a .INI file | |
| UpdateIniNameValue | Replace a value from a given set of name=value lines | |
| VariantHash | Crc32c-based hash of a variant value | |
| _BC_SQWord | Some low-level comparison methods used by mormot.core.json |
function AnyScanExists(P, V: pointer; Count, VSize: PtrInt): boolean;
Fast search of a binary value position in a fixed-size array
- Count is the number of entries in P^[]
function AnyScanIndex(P, V: pointer; Count, VSize: PtrInt): PtrInt;
Fast search of a binary value position in a fixed-size array
- Count is the number of entries in P^[]
- return index of P^[index]=V^, comparing VSize bytes
- return -1 if Value was not found
function BinaryCompare(A, B: pointer; Info: PRttiInfo; CaseInSensitive: boolean): integer; overload;
Comparison of two values by content, using RTTI
function BinaryCompare(A, B: pointer; Info: PRttiInfo; Count: PtrInt; CaseInSensitive: boolean): integer; overload;
Comparison of two arrays of values by content, using RTTI
function BinaryEquals(A, B: pointer; Info: PRttiInfo; PSize: PInteger; Kinds: TRttiKinds; CaseInSensitive: boolean): boolean;
Check equality of two values by content, using RTTI
- optionally returns the known in-memory PSize of the value
function BinaryLoad(Data: pointer; const Source: RawByteString; Info: PRttiInfo; Kinds: TRttiKinds; TryCustomVariants: PDocVariantOptions = nil): boolean; overload;
Unserialize any value from BinarySave() RawByteString, using RTTI
function BinaryLoad(Data: pointer; Source: PAnsiChar; Info: PRttiInfo; Len: PInteger; SourceMax: PAnsiChar; Kinds: TRttiKinds; TryCustomVariants: PDocVariantOptions = nil): PAnsiChar; overload;
Unserialize any value from BinarySave() memory buffer, using RTTI
function BinaryLoadBase64(Source: PAnsiChar; Len: PtrInt; Data: pointer; Info: PRttiInfo; UriCompatible: boolean; Kinds: TRttiKinds; WithCrc: boolean = true; TryCustomVariants: PDocVariantOptions = nil): boolean;
Unserialize any value from BinarySaveBase64() encoding, using RTTI
- optionally contains a trailing crc32c hash before the actual data
function BinarySave(Data: pointer; Dest: PAnsiChar; Info: PRttiInfo; out Len: integer; Kinds: TRttiKinds): PAnsiChar; overload; deprecated;
Binary persistence of any value using RTTI, into a memory buffer
- deprecated function - use overloaded BinarySave() functions instead
procedure BinarySave(Data: pointer; var Dest: TSynTempBuffer; Info: PRttiInfo; Kinds: TRttiKinds; WithCrc: boolean = false); overload;
Binary persistence of any value using RTTI, into a TSynTempBuffer buffer
procedure BinarySave(Data: pointer; Info: PRttiInfo; Dest: TBufferWriter); overload;
Binary persistence of any value using RTTI, into a TBufferWriter stream
function BinarySave(Data: pointer; Info: PRttiInfo; Kinds: TRttiKinds; WithCrc: boolean = false): RawByteString; overload;
Binary persistence of any value using RTTI, into a RawByteString buffer
function BinarySaveBase64(Data: pointer; Info: PRttiInfo; UriCompatible: boolean; Kinds: TRttiKinds; WithCrc: boolean = true): RawUtf8;
Binary persistence of any value using RTTI, into a Base64-encoded text
- contains a trailing crc32c hash before the actual data
function BinarySaveBytes(Data: pointer; Info: PRttiInfo; Kinds: TRttiKinds): TBytes;
Binary persistence of any value using RTTI, into a TBytes buffer
function BinarySaveLength(Data: pointer; Info: PRttiInfo; Len: PInteger; Kinds: TRttiKinds): integer; deprecated;
How many bytes a BinarySave() may return
- deprecated function - use overloaded BinarySave() functions instead
procedure CopyAndSortInt64(Values: PInt64Array; ValuesCount: integer; var Dest: TInt64DynArray);
Copy an integer array, then sort it, low values first
procedure CopyAndSortInteger(Values: PIntegerArray; ValuesCount: integer; var Dest: TIntegerDynArray);
Copy an integer array, then sort it, low values first
procedure CopyInt64(const Source: TInt64DynArray; out Dest: TInt64DynArray);
Create a new 64-bit integer dynamic array with the values from another one
procedure CopyInteger(const Source: TIntegerDynArray; out Dest: TIntegerDynArray);
Create a new 32-bit integer dynamic array with the values from another one
function DeduplicateInt64(var Values: TInt64DynArray; Count: PtrInt): PtrInt; overload;
Sort and remove any 64-bit duplicated integer from Values[]
- returns the new Values[] length
procedure DeduplicateInt64(var Values: TInt64DynArray); overload;
Sort and remove any 64-bit duplicated integer from Values[]
function DeduplicateInt64Sorted(val: PInt64Array; last: PtrInt): PtrInt;
Low-level function called by DeduplicateInt64()
- warning: caller should ensure that last>0
function DeduplicateInteger(var Values: TIntegerDynArray; Count: PtrInt): PtrInt; overload;
Sort and remove any 32-bit duplicated integer from Values[]
- returns the new Values[] length
procedure DeduplicateInteger(var Values: TIntegerDynArray); overload;
Sort and remove any 32-bit duplicated integer from Values[]
function DeduplicateIntegerSorted(val: PIntegerArray; last: PtrInt): PtrInt;
Low-level function called by DeduplicateInteger()
function DeleteSection(SectionFirstLine: PUtf8Char; var Content: RawUtf8; EraseSectionHeader: boolean = true): boolean; overload;
Delete a whole [Section]
- if EraseSectionHeader is TRUE (default), then the [Section] line is also deleted together with its content lines
- return TRUE if something was changed in Content
- return FALSE if [Section] doesn't exist or is already void
- SectionFirstLine may have been obtained by FindSectionFirstLine() function above
function DeleteSection(var Content: RawUtf8; const SectionName: RawUtf8; EraseSectionHeader: boolean = true): boolean; overload;
Delete a whole [Section]
- if EraseSectionHeader is TRUE (default), then the [Section] line is also deleted together with its content lines
- return TRUE if something was changed in Content
- return FALSE if [Section] doesn't exist or is already void
function DynArray(aTypeInfo: PRttiInfo; var aValue; aCountPointer: PInteger = nil): TDynArray;
Initialize the structure with a one-dimension dynamic array
- the dynamic array must have been defined with its own type (e.g. TIntegerDynArray = array of integer)
- if aCountPointer is set, it will be used instead of length() to store the dynamic array items count - it will be much faster when adding elements to the array, because the dynamic array won't need to be resized each time - but in this case, you should use the Count property instead of length(array) or high(array) when accessing the data: in fact length(array) will store the memory size reserved, not the items count
- if aCountPointer is set, its content will be set to 0, whatever the array length is, or the current aCountPointer^ value is
- a typical usage could be:
var IntArray: TIntegerDynArray; begin with DynArray(TypeInfo(TIntegerDynArray), IntArray) do begin (...) end; (...) bin := DynArray(TypeInfo(TIntegerDynArray), IntArray).SaveTo;
function DynArrayAdd(TypeInfo: PRttiInfo; var DynArray; const Item): integer; overload;
Wrapper around TDynArray.Add
- warning: the Item type is not checked at runtime, so should be as expected
- not very fast, but could be useful for simple code
function DynArrayCompare(A, B: PAnsiChar; ExternalCountA, ExternalCountB: PInteger; Info: PRttiInfo; CaseInSensitive: boolean): integer; overload;
Raw comparison of two dynamic arrays
- as called e.g. by TDynArray.Equals, using ExternalCountA/B optional parameter
- RTTI_COMPARE[true/false,rkDynArray] are wrappers to this, with ExternalCount=nil
- if Info=TypeInfo(TObjectDynArray) then will compare any T*ObjArray
function DynArrayDelete(TypeInfo: PRttiInfo; var DynArray; Index: PtrInt): boolean; overload;
Wrapper around TDynArray.Delete
- not very fast, but could be useful for simple code
function DynArrayEquals(TypeInfo: PRttiInfo; var Array1, Array2; Array1Count: PInteger = nil; Array2Count: PInteger = nil; CaseInsensitive: boolean = false): boolean;
Compare two dynamic arrays by calling TDynArray.Equals
- if Info=TypeInfo(TObjectDynArray) then will compare any T*ObjArray
function DynArrayHashOne(Kind: TRttiParserType; CaseInsensitive: boolean = false): TDynArrayHashOne;
Get the hash function corresponding to a given standard array type
- as used internally by TDynArrayHasher.Init and exported here for testing
function DynArrayLoad(var Value; Source: PAnsiChar; TypeInfo: PRttiInfo; TryCustomVariants: PDocVariantOptions = nil; SourceMax: PAnsiChar = nil): PAnsiChar;
Fill a dynamic array content from a binary serialization as saved by DynArraySave() / TDynArray.Save()
- Value shall be set to the target dynamic array field
- is a wrapper around BinaryLoad(rkDynArray)
function DynArrayLoadHeader(var Source: TFastReader; ArrayInfo, ItemInfo: PRttiInfo): integer;
Low-level binary unserialization as saved by DynArraySave/TDynArray.Save
- as used by DynArrayLoad() and TDynArrayLoadFrom
- returns the stored length() of the dynamic array, and Source points to the stored binary data itself
procedure DynArraySave(Data: PAnsiChar; ExternalCount: PInteger; Dest: TBufferWriter; Info: PRttiInfo); overload;
Raw binary serialization of a dynamic array
- as called e.g. by TDynArray.SaveTo, using ExternalCount optional parameter
- RTTI_BINARYSAVE[rkDynArray] is a wrapper to this function, with ExternalCount=nil
function DynArraySave(var Value; TypeInfo: PRttiInfo): RawByteString; overload;
Serialize a dynamic array content as binary, ready to be loaded by DynArrayLoad() / TDynArray.Load()
- Value shall be set to the source dynamic arry field
- is a wrapper around BinarySave(rkDynArray)
procedure DynArraySortIndexed(Values: pointer; ItemSize, Count: integer; Indexes: PCardinalArray; Compare: TDynArraySortCompare); overload;
Sort any dynamic array, via a supplied array of indexes
- this function expects Indexes[] to be already allocated and filled
procedure DynArraySortIndexed(Values: pointer; ItemSize, Count: integer; out Indexes: TSynTempBuffer; Compare: TDynArraySortCompare); overload;
Sort any dynamic array, generating an external array of indexes
- this function will use the supplied TSynTempBuffer for index storage, so use PIntegerArray(Indexes.buf) to access the values
- caller should always make Indexes.Done once finshed
function DynArraySortOne(Kind: TRttiParserType; CaseInsensitive: boolean): TDynArraySortCompare;
Get the comparison function corresponding to a given standard array type
- as used e.g. internally by TDynArray
procedure ExcludeInt64(var Values, Excluded: TInt64DynArray; ExcludedSortSize: integer = 32);
Remove some 64-bit integer from Values[]
- Excluded is declared as var, since it will be sorted in-place during process if it contains more than ExcludedSortSize items (i.e. if the sort is worth it)
procedure ExcludeInteger(var Values, Excluded: TIntegerDynArray; ExcludedSortSize: integer = 32);
Remove some 32-bit integer from Values[]
- Excluded is declared as var, since it will be sorted in-place during process if it contains more than ExcludedSortSize items (i.e. if the sort is worth it)
function ExistsIniName(P: PUtf8Char; UpperName: PAnsiChar): boolean;
Return TRUE if Value of UpperName does exist in P, till end of current section
- expect UpperName as 'NAME='
function ExistsIniNameValue(P: PUtf8Char; const UpperName: RawUtf8; UpperValues: PPAnsiChar): boolean;
Return TRUE if one of the Value of UpperName exists in P, till end of current section
- expect UpperName e.g. as 'CONTENT-TYPE: '
- expect UpperValues to be an array of upper values with left side matching, and ending with nil - as expected by IdemPPChar(), i.e. with at least 2 chars
function FindIniEntry(const Content, Section, Name: RawUtf8; const DefaultValue: RawUtf8 = ''): RawUtf8;
Find a Name= Value in a [Section] of a INI RawUtf8 Content
- this function scans the Content memory buffer, and is therefore very fast (no temporary TMemIniFile is created)
- if Section equals '', find the Name= value before any [Section]
function FindIniEntryFile(const FileName: TFileName; const Section, Name: RawUtf8; const DefaultValue: RawUtf8 = ''): RawUtf8;
Find a Name= Value in a [Section] of a .INI file
- if Section equals '', find the Name= value before any [Section]
- use internally fast FindIniEntry() function above
function FindIniEntryInteger(const Content, Section, Name: RawUtf8): integer;
Find a Name= numeric Value in a [Section] of a INI RawUtf8 Content and return it as an integer, or 0 if not found
- this function scans the Content memory buffer, and is therefore very fast (no temporary TMemIniFile is created)
- if Section equals '', find the Name= value before any [Section]
function FindIniNameValue(P: PUtf8Char; UpperName: PAnsiChar; const DefaultValue: RawUtf8 = ''): RawUtf8;
Find the Value of UpperName in P, till end of current section
- expect UpperName as 'NAME='
function FindIniNameValueInteger(P: PUtf8Char; const UpperName: RawUtf8): PtrInt;
Find the integer Value of UpperName in P, till end of current section
- expect UpperName as 'NAME='
- return 0 if no NAME= entry was found
function FindSectionFirstLine(var source: PUtf8Char; search: PAnsiChar): boolean;
Find the position of the [SEARCH] section in source
- return true if [SEARCH] was found, and store pointer to the line after it in source
function FindSectionFirstLineW(var source: PWideChar; search: PUtf8Char): boolean;
Find the position of the [SEARCH] section in source
- return true if [SEARCH] was found, and store pointer to the line after it in source
- this version expects source^ to point to an Unicode char array
function FindWinAnsiIniEntry(const Content, Section, Name: RawUtf8): RawUtf8;
Find a Name= Value in a [Section] of a INI WinAnsi Content
- same as FindIniEntry(), but the value is converted from WinAnsi into UTF-8
function GetSectionContent(SectionFirstLine: PUtf8Char): RawUtf8; overload;
Retrieve the whole content of a section as a string
- SectionFirstLine may have been obtained by FindSectionFirstLine() function above
function GetSectionContent(const Content, SectionName: RawUtf8): RawUtf8; overload;
Retrieve the whole content of a section as a string
- use SectionFirstLine() then previous GetSectionContent()
procedure IncludeInt64(var Values, Included: TInt64DynArray; IncludedSortSize: integer = 32);
Ensure some 64-bit integer from Values[] will only contain Included[]
- Included is declared as var, since it will be sorted in-place during process if it contains more than IncludedSortSize items (i.e. if the sort is worth it)
procedure IncludeInteger(var Values, Included: TIntegerDynArray; IncludedSortSize: integer = 32);
Ensure some 32-bit integer from Values[] will only contain Included[]
- Included is declared as var, since it will be sorted in-place during process if it contains more than IncludedSortSize items (i.e. if the sort is worth it)
function IniToObject(const Ini: RawUtf8; Instance: TObject; const SectionName: RawUtf8 = 'Main'; DocVariantOptions: PDocVariantOptions = nil; Level: integer = 0): boolean;
Fill a class Instance properties from an .ini content
- the class property fields are searched in the supplied main SectionName
- nested objects and multi-line text values are searched in their own section, named from their section level and property (e.g. [mainprop.nested1.nested2])
- returns true if at least one property has been identified
procedure Int64ToUInt32(Values64: PInt64Array; Values32: PCardinalArray; Count: PtrInt);
Copy some Int64 values into an unsigned integer array
function IsHtmlContentTypeTextual(Headers: PUtf8Char): boolean;
Returns TRUE if the supplied HTML Headers contains 'Content-Type: text/...', 'Content-Type: application/json' or 'Content-Type: application/xml'
function IsWebSocketUpgrade(headers: PUtf8Char): boolean;
Search if the WebSocketUpgrade() header is present
- consider checking the hsrConnectionUpgrade flag instead
function MaxInt64(const Values: TInt64DynArray; ValuesCount: PtrInt; MaxStart: Int64 = -1): Int64;
Find the maximum 64-bit integer in Values[]
function MaxInteger(const Values: TIntegerDynArray; ValuesCount: PtrInt; MaxStart: integer = -1): integer;
Find the maximum 32-bit integer in Values[]
function MedianQuickSelect(const OnCompare: TOnValueGreater; n: integer; var TempBuffer: TSynTempBuffer): integer;
Compute the median of a serie of values, using "Quickselect"
- based on the algorithm described in "Numerical recipes in C", Second Edition
- expect the values information to be available from a comparison callback
- this version will use a temporary index list to exchange items order (supplied as a TSynTempBuffer), so won't change the supplied values themself
- see also function MedianQuickSelectInteger() for PIntegerArray values
- returns the index of the median Value
function MedianQuickSelectInteger(Values: PIntegerArray; n: integer): integer;
Compute the median of an integer serie of values, using "Quickselect"
- based on the algorithm described in "Numerical recipes in C", Second Edition, translated from Nicolas Devillard's C code: http://ndevilla.free.fr/median/median
- warning: the supplied integer array is modified in-place during the process, and won't be fully sorted on output (this is no QuickSort alternative)
procedure NotifySortedIntegerChanges(old, new: PIntegerArray; oldn, newn: PtrInt; const added, deleted: TOnNotifySortedIntegerChange; const sender);
Compares two 32-bit signed sorted integer arrays, and call event handlers to notify the corresponding modifications in an O(n) time
- items in both old[] and new[] arrays are required to be sorted
procedure ObjArraySort(var aValue; Compare: TDynArraySortCompare; CountPointer: PInteger = nil);
Sort any TObjArray with a given comparison function
function ObjectCompare(A, B: PObject; Count: PtrInt; CaseInsensitive: boolean = false): integer; overload;
Comparison of published properties of several TObject instances, using RTTI
function ObjectCompare(A, B: TObject; CaseInSensitive: boolean): integer; overload;
Comparison of two TObject published properties, using RTTI
function ObjectEquals(A, B: TObject): boolean;
Case-sensitive comparison of two TObject published properties, using RTTI
function ObjectEqualsI(A, B: TObject): boolean;
Case-insensitive comparison of two TObject published properties, using RTTI
function ObjectToIni(const Instance: TObject; const SectionName: RawUtf8 = 'Main'; Options: TTextWriterWriteObjectOptions = [woEnumSetsAsText, woRawBlobAsBase64, woHumanReadableEnumSetAsComment]; Level: integer = 0): RawUtf8;
Serialize a class Instance properties into an .ini content
- the class property fields are written in the supplied main SectionName
- nested objects and multi-line text values are written in their own section, named from their section level and property (e.g. [mainprop.nested1.nested2])
procedure QuickSortIndexedPUtf8Char(Values: PPUtf8CharArray; Count: integer; var SortedIndexes: TCardinalDynArray; CaseSensitive: boolean = false);
Deprecated TRawUtf8MethodList should be replaced by a TSynDictionary sort a dynamic array of PUtf8Char items, via an external array of indexes
- you can use FastFindIndexedPUtf8Char() for fast O(log(n)) binary search
function RecordEquals(const RecA, RecB; TypeInfo: PRttiInfo; PRecSize: PInteger = nil; CaseInSensitive: boolean = false): boolean;
Check equality of two records by content
- will handle packed records, with binaries (byte, word, integer...) and string types properties
- will use binary-level comparison: it could fail to match two floating-point values because of rounding issues (Currency won't have this problem)
- is a wrapper around BinaryEquals(rkRecordTypes)
function RecordLoad(var Rec; Source: PAnsiChar; TypeInfo: PRttiInfo; Len: PInteger = nil; SourceMax: PAnsiChar = nil; TryCustomVariants: PDocVariantOptions = nil): PAnsiChar; overload;
Fill a record content from a memory buffer as saved by RecordSave()
- return nil if the Source buffer is incorrect
- in case of success, return the memory buffer pointer just after the read content, and set the Rec size, in bytes, into Len reference variable
- will use a proprietary binary format, with some variable-length encoding of the string length - note that if you change the type definition, any previously-serialized content will fail, maybe triggering unexpected GPF: you may use TypeInfoToHash() if you share this binary data accross executables
- you should provide in SourceMax the first byte after the Source memory buffer, which will be used to avoid any unexpected buffer overflow - clearly mandatory when decoding the content from any external process (e.g. a maybe-forged client) - with no performance penalty
- is a wrapper around BinaryLoad(rkRecordTypes)
function RecordLoad(var Rec; const Source: RawByteString; TypeInfo: PRttiInfo; TryCustomVariants: PDocVariantOptions = nil): boolean; overload;
Fill a record content from a memory buffer as saved by RecordSave()
- will use the Source length to detect and avoid any buffer overlow
- returns false if the Source buffer was incorrect, true on success
- is a wrapper around BinaryLoad(rkRecordTypes)
function RecordLoadBase64(Source: PAnsiChar; Len: PtrInt; var Rec; TypeInfo: PRttiInfo; UriCompatible: boolean = false; TryCustomVariants: PDocVariantOptions = nil): boolean;
Read a record content from a Base64 encoded content
- expects RecordSaveBase64() format, with a left-sided binary CRC32C
- is a wrapper around BinaryLoadBase64(rkRecordTypes)
procedure RecordSave(const Rec; var Dest: TSynTempBuffer; TypeInfo: PRttiInfo); overload;
Save a record content into a destination memory buffer
- caller should make Dest.Done once finished with Dest.buf/Dest.len buffer
- is a wrapper around BinarySave(rkRecordTypes)
function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: PRttiInfo; out Len: integer): PAnsiChar; overload; deprecated;
Save a record content into a destination memory buffer
- Dest must be at least RecordSaveLength() bytes long
- deprecated function - use overloaded BinarySave() functions instead
function RecordSave(const Rec; TypeInfo: PRttiInfo): RawByteString; overload;
Save a record content into a RawByteString
- will handle packed records, with binaries (byte, word, integer...) and string types properties (but not with internal raw pointers, of course)
- will use a proprietary binary format, with some variable-length encoding of the string length - note that if you change the type definition, any previously-serialized content will fail, maybe triggering unexpected GPF: you may use TypeInfoToHash() if you share this binary data accross executables
- warning: will encode RTL string fields as AnsiString (one byte per char) prior to Delphi 2009, and as UnicodeString (two bytes per char) since Delphi 2009: if you want to use this function between UNICODE and NOT UNICODE versions of Delphi, you should use some explicit types like RawUtf8, WinAnsiString, SynUnicode or even RawUnicode/WideString
- is a wrapper around BinarySave(rkRecordTypes)
function RecordSave(const Rec; Dest: PAnsiChar; TypeInfo: PRttiInfo): PAnsiChar; overload; deprecated;
Save a record content into a destination memory buffer
- Dest must be at least RecordSaveLength() bytes long
- deprecated function - use overloaded BinarySave() functions instead
function RecordSaveBase64(const Rec; TypeInfo: PRttiInfo; UriCompatible: boolean = false): RawUtf8;
Save a record content into a Base64 encoded UTF-8 text content
- will use RecordSave() format, with a left-sided binary CRC
- is a wrapper around BinarySaveBase64(rkRecordTypes)
function RecordSaveBytes(const Rec; TypeInfo: PRttiInfo): TBytes;
Save a record content into a TBytes dynamic array
- could be used as an alternative to RawByteString's RecordSave()
- is a wrapper around BinarySaveBytes(rkRecordTypes)
function RecordSaveLength(const Rec; TypeInfo: PRttiInfo; Len: PInteger = nil): integer; deprecated;
Compute the number of bytes needed to save a record content using the RecordSave() function
- deprecated function - use overloaded BinarySave() functions instead
procedure ReplaceSection(SectionFirstLine: PUtf8Char; var Content: RawUtf8; const NewSectionContent: RawUtf8); overload;
Replace a whole [Section] content by a new content
- create a new [Section] if none was existing
- SectionFirstLine may have been obtained by FindSectionFirstLine() function above
procedure ReplaceSection(var Content: RawUtf8; const SectionName, NewSectionContent: RawUtf8); overload;
Replace a whole [Section] content by a new content
- create a new [Section] if none was existing
procedure Reverse(const Values: TIntegerDynArray; ValuesCount: PtrInt; Reversed: PIntegerArray);
Fill already allocated Reversed[] so that Reversed[Values[i]]=i
function SumInteger(const Values: TIntegerDynArray; ValuesCount: PtrInt): integer;
Sum all 32-bit integers in Values[]
procedure UpdateIniEntry(var Content: RawUtf8; const Section, Name, Value: RawUtf8);
Update a Name= Value in a [Section] of a INI RawUtf8 Content
- this function scans and update the Content memory buffer, and is therefore very fast (no temporary TMemIniFile is created)
- if Section equals '', update the Name= value before any [Section]
procedure UpdateIniEntryFile(const FileName: TFileName; const Section, Name, Value: RawUtf8);
Update a Name= Value in a [Section] of a .INI file
- if Section equals '', update the Name= value before any [Section]
- use internally fast UpdateIniEntry() function above
function UpdateIniNameValue(var Content: RawUtf8; const Name, UpperName, NewValue: RawUtf8): boolean;
Replace a value from a given set of name=value lines
- expect UpperName as 'UPPERNAME=', otherwise returns false
- if no UPPERNAME= entry was found, then Name+NewValue is added to Content
- a typical use may be:
UpdateIniNameValue(headers,HEADER_CONTENT_TYPE,HEADER_CONTENT_TYPE_UPPER,contenttype);
function VariantHash(const value: variant; CaseInsensitive: boolean; Hasher: THasher = nil): cardinal;
Crc32c-based hash of a variant value
- complex string types will make up to 255 uppercase characters conversion if CaseInsensitive is true
- you can specify your own hashing function if crc32c is not what you expect
function _BC_SQWord(A, B: PInt64; Info: PRttiInfo; out Compared: integer): PtrInt;
Some low-level comparison methods used by mormot.core.json
GetDataFromJson: procedure(Data: pointer; var Json: PUtf8Char; EndOfObject: PUtf8Char; Rtti: TRttiCustom; CustomVariantOptions: PDocVariantOptions; Tolerant: boolean; Interning: TRawUtf8InterningAbstract);
Low-level JSON unserialization function
- defined in this unit to avoid circular reference with mormot.core.json, but to publish the TDynArray.LoadFromJson overloaded methods
- this unit will just set a wrapper raising an ERttiException
- link mormot.core.json.pas to have a working implementation
- rather call LoadJson() from mormot.core.json than this low-level function
RTTI_BINARYLOAD: TRttiBinaryLoads;
Lookup table for binary persistence of any RTTI type value
- for efficient retrieval from binary of managed and unmanaged types
RTTI_BINARYSAVE: TRttiBinarySaves;
Lookup table for binary persistence of any RTTI type value
- for efficient persistence into binary of managed and unmanaged types
RTTI_COMPARE: TRttiComparers;
Lookup table for comparison of any RTTI type value
- for efficient search or sorting of managed and unmanaged types
- RTTI_COMPARE[false] for case-sensitive comparison
- RTTI_COMPARE[true] for case-insensitive comparison
RTTI_FLOAT_COMPARE: array[TRttiFloat] of TRttiCompare;
Lookup table for comparison of floating-point RTTI type values
- slightly faster alternative to RTTI_COMPARE[rkFloat]
RTTI_ORD_COMPARE: array[TRttiOrd] of TRttiCompare;
Lookup table for comparison of ordinal RTTI type values
- slightly faster alternative to RTTI_COMPARE[rkOrdinalTypes]