# 4. Core Units (mormot.core.)
The Foundation Bricks
The mORMot 2 framework uses custom low-level types, classes, and functions instead of relying solely on the standard Delphi RTL. This design choice provides:
SynCommons.pas file. In mORMot 2, this has been split into 24 focused units in the mormot.core. namespace, following SOLID principles.
A global mormot.defines.inc include file appears in all framework units:
{$I mormot.defines.inc}
This defines key conditionals for portable and efficient code:
| Define | Purpose |
|---|---|
PUREMORMOT2 |
Disable mORMot 1.18 compatibility aliases (recommended for new code) |
FPC_X64MM |
Use custom x64 memory manager (FPC only, Linux/Windows) |
FPCMM_BOOST / FPCMM_SERVER |
Memory manager threading modes |
NEWRTTINOTUSED |
Exclude Delphi 2010+ enhanced RTTI (smaller EXE) |
USE_OPENSSL |
Enable OpenSSL integration (required on POSIX) |
mORMot 2 has 100% Unicode compatibility across all Delphi and FPC versions. From its core to its uppermost features, the framework is natively UTF-8, which is the de-facto character encoding for JSON, SQLite3, and most supported database engines.
The following string types are used throughout the framework:
| Type | Purpose | Location |
|---|---|---|
RawUtf8 |
Primary type for all internal data (UTF-8 encoded) | mormot.core.base |
RawByteString |
Binary byte storage | mormot.core.base |
WinAnsiString |
WinAnsi-encoded text (code page 1252) | mormot.core.base |
SynUnicode |
Fastest native Unicode (WideString pre-2009, UnicodeString after) |
mormot.core.base |
string |
Generic VCL/UI text (use only at presentation layer) | RTL |
RawUtf8 for all business logic and data processing. Convert to string only at the UI layer using Utf8ToString() / StringToUtf8() from mormot.core.unicode:
uses
mormot.core.base,
mormot.core.unicode;
var
utf8: RawUtf8;
display: string;
begin
utf8 := 'Hello UTF-8 World';
display := Utf8ToString(utf8); // Convert for UI display
utf8 := StringToUtf8(display); // Convert back for storage/processing
end;
The currency type is the standard Delphi type for monetary values, avoiding rounding errors with exact 4-decimal precision. It safely stores numbers in the range -922,337,203,685,477.5808 to 922,337,203,685,477.5807.
mORMot provides fast currency-to-text conversion functions in mormot.core.text that avoid FPU rounding issues:
uses
mormot.core.text;
var
c: currency;
s: RawUtf8;
begin
c := 123.45;
s := CurrencyToStr(c); // Fast, no FPU rounding
c := StrToCurrency(s); // Safe conversion back
end;
The Int64 binary representation of currency (i.e., value 10000) is used internally for maximum performance and precision.
| Type | Purpose |
|---|---|
PtrInt |
Signed pointer-size integer (32 or 64 bit) |
PtrUInt |
Unsigned pointer-size integer |
TID |
64-bit record ID (Int64) |
TDynArray (in mormot.core.data) provides TList-like functionality for any dynamic array:
uses
mormot.core.base,
mormot.core.data;
type
TIntegerArray = array of Integer;
var
arr: TIntegerArray;
da: TDynArray;
v: Integer;
begin
da.Init(TypeInfo(TIntegerArray), arr); // Associate wrapper with array
for v := 1 to 1000 do
da.Add(v); // TList-like Add method
da.Sort(SortDynArrayInteger); // In-place sorting
v := 500;
if da.Find(v) >= 0 then // Binary search (after sorting)
WriteLn('Found 500');
da.Delete(0); // Delete by index
WriteLn('Count: ', da.Count);
end;
For high-performance scenarios, use an external count variable to avoid reallocation on every Add/Delete:
var
arr: TIntegerArray;
da: TDynArray;
count: Integer;
begin
da.Init(TypeInfo(TIntegerArray), arr, @count); // External count
da.Capacity := 10000; // Pre-allocate memory
// Now Add/Delete modify 'count' without reallocating 'arr'
for i := 1 to 10000 do
da.Add(i); // Much faster with external count
end;
TDynArray supports both binary and JSON serialization:
var
binary: RawByteString;
json: RawUtf8;
begin
// Binary (fast, compact)
binary := da.SaveTo;
da.LoadFrom(binary);
// JSON (interoperable)
json := da.SaveToJson;
da.LoadFromJson(json);
end;
TDynArrayHashed adds O(1) hash-based lookup:
type
TNameValue = record
Name: RawUtf8;
Value: Integer;
end;
TNameValueArray = array of TNameValue;
var
arr: TNameValueArray;
hash: TDynArrayHashed;
added: Boolean;
idx: Integer;
begin
hash.Init(TypeInfo(TNameValueArray), arr); // Auto-detects RawUtf8 key
// Add or find existing
idx := hash.FindHashedForAdding('MyKey', added);
if added then
begin
arr[idx].Name := 'MyKey';
arr[idx].Value := 42;
end;
// Fast lookup
idx := hash.FindHashed('MyKey'); // O(1) instead of O(n)
end;
TSynDictionary (in mormot.core.data) is a thread-safe dictionary storing key-value pairs as two dynamic arrays:
uses
mormot.core.data;
var
dict: TSynDictionary;
begin
dict := TSynDictionary.Create(TypeInfo(TRawUtf8DynArray), TypeInfo(TIntegerDynArray));
try
dict.Add('key1', 100);
dict.Add('key2', 200);
if dict.Exists('key1') then
WriteLn('Value: ', dict.FindAndCopy('key1', v));
// Thread-safe by default (no external locking needed)
finally
dict.Free;
end;
end;
TDocVariant (in mormot.core.variants) is a custom variant type for storing JSON-like documents:
uses
mormot.core.variants;
var
doc: Variant;
begin
// Object document
doc := _Obj(['name', 'John', 'age', 30]);
// or from JSON
doc := _Json('{"name":"John","age":30}');
// Array document
doc := _Arr(['apple', 'banana', 'cherry']);
// or from JSON
doc := _Json('["apple","banana","cherry"]');
end;
var
doc: Variant;
begin
doc := _Json('{"name":"John","address":{"city":"NYC","zip":"10001"}}');
// Read properties via late-binding
WriteLn(doc.name); // 'John'
WriteLn(doc.address.city); // 'NYC'
// Modify properties
doc.name := 'Jane';
doc.address.state := 'NY'; // Add new property
// Convert to JSON
WriteLn(doc); // '{"name":"Jane","address":{"city":"NYC","zip":"10001","state":"NY"}}'
end;
For better performance, use direct transtyping:
var
doc: Variant;
begin
doc := _Json('{"a":1,"b":2,"c":3}');
// Safe access via _Safe()
with _Safe(doc)^ do
begin
WriteLn('Count: ', Count);
for i := 0 to Count - 1 do
WriteLn(Names[i], '=', Values[i]);
end;
// Typed property access
WriteLn(_Safe(doc)^.I['a']); // Integer access
WriteLn(_Safe(doc)^.U['b']); // RawUtf8 access
end;
By default, _Obj()/_Arr()/_Json() create per-value documents (deep copy on assignment):
var
v1, v2: Variant;
begin
v1 := _Obj(['name', 'John']);
v2 := v1; // Creates a copy
v2.name := 'Jane';
WriteLn(v1.name); // 'John' (unchanged)
WriteLn(v2.name); // 'Jane'
end;
Use _ObjFast()/_ArrFast()/_JsonFast() for per-reference documents (shared):
var
v1, v2: Variant;
begin
v1 := _ObjFast(['name', 'John']);
v2 := v1; // Reference, not copy
v2.name := 'Jane';
WriteLn(v1.name); // 'Jane' (both changed!)
WriteLn(v2.name); // 'Jane'
end;
Dates are encoded as ISO 8601 text (YYYY-MM-DDThh:mm:ss), which provides:
| Type | Resolution | Storage | Use Case |
|---|---|---|---|
TDateTime |
Seconds | TEXT (ISO 8601) | General purpose |
TDateTimeMS |
Milliseconds | TEXT (ISO 8601.sss) | High precision |
TTimeLog |
Seconds | INTEGER (bit-packed) | Fast comparison, compact storage |
TUnixTime |
Seconds | INTEGER | Unix timestamp since 1970 |
TUnixMSTime |
Milliseconds | INTEGER | JavaScript-compatible |
TTimeLog (in mormot.core.datetime) is a proprietary 64-bit format optimized for fast comparison and compact storage:
uses
mormot.core.datetime;
var
t: TTimeLog;
dt: TDateTime;
begin
t := TimeLogNow; // Current time
t := TimeLogFromDateTime(Now); // From TDateTime
dt := TimeLogToDateTime(t); // Back to TDateTime
// Direct comparison works (chronological order)
if t1 > t2 then
WriteLn('t1 is later');
// ISO 8601 conversion
WriteLn(TimeLogToIso8601(t, true)); // '2025-01-15T10:30:45'
end;
TSynTimeZone (in mormot.core.datetime) handles time zone conversions:
uses
mormot.core.datetime;
var
local: TDateTime;
begin
// Convert UTC to local time for a specific zone
local := TSynTimeZone.Default.UtcToLocal(NowUtc, 'Eastern Standard Time');
// Get current local time for a zone
local := TSynTimeZone.Default.NowToLocal('Pacific Standard Time');
end;
TSynLocker (in mormot.core.os) provides CPU cache-friendly critical sections:
uses
mormot.core.os;
var
Lock: TSynLocker;
Counter: Integer;
begin
Lock.Init;
try
// In thread code:
Lock.Lock;
try
Inc(Counter); // Protected access
finally
Lock.UnLock;
end;
finally
Lock.Done;
end;
end;
Use ProtectMethod for RAII-style protection:
procedure TMyClass.ThreadSafeMethod;
begin
fLock.ProtectMethod; // Returns IUnknown, auto-unlocks at method end
// ... protected code ...
end; // Automatically unlocks here
TSynLocker includes 7 variant slots for thread-safe value storage:
var
Lock: TSynLocker;
begin
Lock.Init;
// Thread-safe integer storage
Lock.LockedInt64[0] := 100;
Lock.LockedInt64Increment(0, 1); // Atomic increment
// Thread-safe string storage
Lock.LockedUtf8[1] := 'value';
// Thread-safe variant storage
Lock.Locked[2] := _Obj(['count', 42]);
end;
Inherit from these for built-in TSynLocker:
| Class | Inherits From |
|---|---|
TSynPersistentLock |
TSynPersistent |
TInterfacedObjectLocked |
TInterfacedObjectWithCustomCreate |
TObjectListLocked |
TObjectList |
TRawUtf8ListLocked |
TRawUtf8List |
| Unit | Purpose | Key Types |
|---|---|---|
mormot.core.base |
Foundation types, ASM stubs | RawUtf8, PtrInt, TDynArray basics |
mormot.core.os |
OS abstraction | TSynLocker, GetTickCount64, file/process APIs |
mormot.core.unicode |
Charset conversion | Utf8ToString, WinAnsiToUtf8 |
mormot.core.text |
Text processing | FormatUtf8, CSV parsing, currency |
mormot.core.datetime |
Date/time handling | TTimeLog, ISO 8601, TSynTimeZone |
| Unit | Purpose | Key Types |
|---|---|---|
mormot.core.rtti |
RTTI abstraction | TRttiCustom, PRttiInfo |
mormot.core.buffers |
Compression, streams | SynLZ, Base64, TBufferWriter |
mormot.core.data |
Data structures | TDynArray, TDynArrayHashed, TSynDictionary |
mormot.core.json |
JSON handling | TJsonWriter, GetJsonField |
mormot.core.variants |
Dynamic documents | TDocVariant, IDocDict, IDocList |
| Unit | Purpose | Key Types |
|---|---|---|
mormot.core.log |
Logging framework | TSynLog, ISynLog |
mormot.core.perf |
Performance monitoring | TSynMonitor, timing |
mormot.core.threads |
Threading utilities | TSynBackgroundThread, TSynParallelProcess |
mormot.core.search |
Search and filtering | Full-text search helpers |
mormot.core.test |
Testing framework | TSynTestCase |
mormot.core.mustache |
Template engine | TSynMustache |
mormot.core.interfaces |
Interface support | DI/IoC container |
mormot.core.zip |
ZIP compression | Archive handling |
Core units have strict dependencies (lower never depends on higher):
mormot.core.base (RTL types, ASM - no dependencies)
└─► mormot.core.os (OS abstraction)
└─► mormot.core.unicode (encoding)
└─► mormot.core.text (parsing)
└─► mormot.core.datetime (dates)
└─► mormot.core.rtti (RTTI)
└─► mormot.core.buffers (compression)
└─► mormot.core.data (TDynArray)
└─► mormot.core.json (JSON)
└─► [variants, log, threads, etc.]
Critical Rule: When modifying units, respect this hierarchy. Adding references that create circular dependencies will break compilation.
| mORMot 1 | mORMot 2 | Notes |
|---|---|---|
RawUTF8 |
RawUtf8 |
Case change only |
SynCommons.pas |
mormot.core. |
Split into 24 units |
FormatUTF8() |
FormatUtf8() |
Same function, case change |
TDocVariant |
TDocVariant |
Now in mormot.core.variants |
TSynLog |
TSynLog |
Now in mormot.core.log |
TDynArray |
TDynArray |
Now in mormot.core.data |
By default, mORMot 2 provides compatibility aliases. Define PUREMORMOT2 to disable them and use only new names.
uses
mormot.core.base, // Foundation
mormot.core.os, // OS abstraction
mormot.core.text, // Text utilities
mormot.core.json, // JSON
mormot.core.variants; // TDocVariant
Next Chapter: Object-Relational Mapping (TOrm, TOrmModel)
| Previous | Index | Next |
|---|---|---|
| Chapter 3: Meet mORMot 2 | Index | Chapter 5: Object-Relational Mapping |