Current version of the main framework units target only Win32 and
Win64 systems.

It allows to make easy self-hosting of mORMot servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since mORMot CPU and RAM expectations are much lower than a regular
IIS-WCF-MSSQL-.Net stack.
But in a Service-Oriented Architecture (SOA), you would probably need
to create clients for platforms outside the Windows world, especially
mobile devices.

A set of cross-platform client units is therefore available in the
CrossPlatform sub-folder of the source code repository. It allows
writing any client in modern object pascal language, for:

Any version of Delphi, on any platform (Mac OSX, or any
mobile supported devices);

Current version of the main framework units target only Win32 and
Win64 systems.

It allows to make easy self-hosting of mORMot servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since mORMot CPU and RAM expectations are much lower than a regular
IIS-WCF-MSSQL-.Net stack.
But in a Service-Oriented Architecture (SOA), you would probably need
to create clients for platforms outside the Windows world, especially
mobile devices.

A set of cross-platform client units is therefore available in the
CrossPlatform sub-folder of the source code repository. It allows
writing any client in modern object pascal language, for:

Any version of Delphi, on any platform (Mac OSX, or any
mobile supported devices);

Current version of the main framework units target only Win32 and
Win64 systems.

It allows to make easy self-hosting of mORMot servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since mORMot CPU and RAM expectations are much lower than a regular
IIS-WCF-MSSQL-.Net stack.
But in a Service-Oriented Architecture (SOA), you would probably need
to create clients for platforms outside the Windows world, especially
mobile devices.

A set of cross-platform client units is therefore available in the
CrossPlatform sub-folder of the source code repository. It allows
writing any client in modern object pascal language, for:

Any version of Delphi, on any platform (Mac OSX, or any
mobile supported devices);

Current version of the main framework units target only Win32 and
Win64 systems.

It allows to make easy self-hosting of mORMot servers for local
business applications in any corporation, or pay cheap hosting in the Cloud,
since mORMot CPU and RAM expectations are much lower than a regular
IIS-WCF-MSSQL-.Net stack.
But in a Service-Oriented Architecture (SOA), you would probably need
to create clients for platforms outside the Windows world, especially
mobile devices.

A set of cross-platform client units is therefore available in the
CrossPlatform sub-folder of the source code repository. It allows
writing any client in modern object pascal language, for:

Any version of Delphi, on any platform (Mac OSX, or any
mobile supported devices);

Since Delphi 2010, the compiler generates additional RTTI at compilation, so
that all record fields are described, and available at
runtime.
By the way, this enhanced RTTI is one of the reasons why executables did grow
so much in newer versions of the compiler.

Our SynCommons.pas unit is now able to use this enhanced
information, and let any record be serialized via
RecordLoad() and RecordSave() functions, and all
internal JSON marshalling process.

In short, you have nothing to do.
Just use your record as parameters, and, with Delphi 2010 and up,
they will be serialized as valid JSON objects.

read of one 180 MB JSON file (with on-the-fly adaptation to fit a record
layout),

named access to all rows and columns of a 1 MB JSON table, extracted from a
SQL request (with comparison with our ORM performance).

On average and in details, mORMot is the fastest in almost all
scenarios (with an amazing performance for table/ORM processing),
dwsJSON performs very well (better than SuperObject), and
DBXJSON is the slowest (by far, but XE6 version is faster than
XE4).

With revision 1.18 of the framework, we just introduced two new custom types
of variants:

TDocVariant kind of variant;

TBSONVariant kind of variant.

The second custom type (which handles MongoDB-specific extensions -
like ObjectID or other specific types like dates or binary) will
be presented later, when dealing with MongoDB support in
mORMot, together with the BSON kind of content. BSON /
MongoDB support is implemented in the SynMongoDB.pas
unit.

We will now focus on TDocVariant itself, which is a generic
container of JSON-like objects or arrays.
This custom variant type is implemented in SynCommons.pas unit, so
is ready to be used everywhere in your code, even without any link to the
mORMot ORM kernel, or MongoDB.

TDocVariant documents

TDocVariant implements a custom variant type which can be used
to store any JSON/BSON document-based content, i.e. either:

Name/value pairs, for object-oriented documents;

An array of values (including nested documents), for array-oriented
documents;

Any combination of the two, by nesting TDocVariant
instances.

Here are the main features of this custom variant type:

DOM approach of any object or array documents;

Perfect storage for dynamic value-objects content, with a
schema-less approach (as you may be used to in scripting languages
like Python or JavaScript);

Allow nested documents, with no depth limitation but the available
memory;

Assignment can be either per-value (default, safest but slower
when containing a lot of nested data), or per-reference (immediate
reference-counted assignment);

Very fast JSON serialization / un-serialization with support of
MongoDB-like extended syntax;

Access to properties in code, via late-binding (including almost no speed
penalty due to our VCL hack as already detailed);

Direct access to the internal variant names and values
arrays from code, by trans-typing into a TDocVariantData
record;

Instance life-time is managed by the compiler (like any other
variant type), without the need to use interfaces or
explicit try..finally blocks;

Optimized to use as little memory and CPU resource as possible (in contrast
to most other libraries, it does not allocate one class instance
per node, but rely on pre-allocated arrays);

Opened to extension of any content storage - for instance, it will
perfectly integrate with BSON serialization and custom MongoDB types
(ObjectID, RegEx...), to be used in conjunction with MongoDB
servers;

Designed to work with our mORMot ORM: any TSQLRecord
instance containing such variant custom types as published
properties will be recognized by the ORM core, and work as expected with any
database back-end (storing the content as JSON in a TEXT column);

Designed to work with our mORMot SOA: any interface-based
service is able to consume or publish such kind of content, as
variant kind of parameters;

Fully integrated with the Delphi IDE: any variant instance
will be displayed as JSON in the IDE debugger, making it very convenient to
work with.

To create instances of such variant, you can use some
easy-to-remember functions:

Yes, I know this article title is a huge moment of trolling for most Delphi
developer.
But object could be legend... - wait for it - ... dary!

You perhaps already noticed by several blog posts
here that I still like the good old (and deprecated) object
type, in addition to the common heap-allocated class type.
Plain record with methods does not match the object-oriented
approach of object, since it does not feature inheritance.

When you take a look at modern strongly-typed languages, targeting
concurrent programming (you know, multi-thread/multi-core execution), you will
see that the objects may be allocated in several ways, to facilitate execution
flow.

The Rust language for instance is
pretty interesting. It has optional task-local Garbage Collection and safe
pointer types with region analysis.

To some extent, it is very similar to what object allows in the
Delphi world, and why I'm still using/loving it!

Even if mORMot will be more easily used in a project designed from
scratch, it fits very well the purpose of evolving any existing Delphi project,
or even creating the server side part of an AJAX application.

One benefit of such a framework is to facilitate the transition from a
Client-Server architecture to a N-Tier layered pattern.

By default, during interface-based service call,
any record parameter or function result will be serialized with
our proprietary binary (and optimized layout) - i.e. RecordLoad
and RecordSave functions - then encoded in Base-64, to be
stored as plain text within the JSON stream.

But custom record JSON serialization can be defined, as with any
class - see this article - or
dynamic array - see this
article.

By default, only "standard" dynamic arrays (like
TIntegerDynArray) are serialized as true JSON array: other not
known kind of arrays are serialized as binary content, within a Base64
encoding.

This is a very efficient solution for a pure Delphi application, since it
will be fast and always works, but won't be easy to deal with from an AJAX
client.

Applications can now supply a custom JSON serialization for any other
dynamic array, via the TTextWriter.RegisterCustomJSONSerializer()
class method.
Two callbacks are to be supplied for a dynamic array type information, in order
to handle proper serialization and un-serialization of the JSON array.

Among all trolling subject in forums, you'll find out the
great Garbage Collection theme.

Fashion languages rely on it. At the core of the .Net and Java framework,
and all scripting languages (like JavaScript, Perl, Python or Ruby), you'll
find a Garbage Collector. New developers, just released from schools, do
learn about handling memory only in theory, and just can't understand how is
memory allocated - we all have seen such rookies involved in Delphi code
maintenance, leaking memory as much as they type. In fact, most of them did not
understood how a computer works. I warned you this will be a trolling
subject.

And, in Delphi, there is no such collector. We handle memory in
several ways:

Creating static variables - e.g. on the stack, inside a class
or globally;

Creating objects with class instances allocated on heap - in
at least three ways: with a try..finally Free block, with a
TComponent ownership model in the VCL, or by using
an interface (which creates an hidden try..finally
Free block);

It is a bit complex, but it is also deadly powerful. You have several memory
allocation models at hand, which can be very handy if you want to tune your
performance and let program scale. Just like manual recycling at home will save
the planet. Some programmers will tell you that it's a waste of cell brain,
typing and time. Linux kernel gurus would not say so, I'm afraid.

Then came the big Apple company, which presented its new ARC model
(introduced in Mac OS X 10.7 Lion) as a huge benefit for
Objective-C in comparison with the Garbage Collection model. And
let's face it: this ARC just sounds like the Delphi memory model.

How to make your software run fast, especially in a
multi-threaded architecture?

We tried to remove the Memory Manager scaling problems in our SynScaleMM. It worked as expected in a
multi-threaded server environment. Scaling is much better than FastMM4, for
some critical tests. But it's not ready for production yet...

To be honest, the Memory Manager is perhaps not the bigger bottleneck
in Multi-Threaded applications.

Here are some (not dogmatic, just from experiment and knowledge of low-level
Delphi RTL) advice if you want to write FAST multi-threaded application in
Delphi.

The SQlite3 engine has ability to create Virtual Tables from code.
From the perspective of an SQL statement, the virtual table object looks like
any other table or view. But behind the scenes, queries from and updates to a
virtual table invoke callback methods on the virtual table object instead of
reading and writing to the database file.

The virtual table mechanism allows an application to publish interfaces that
are accessible from SQL statements as if they were tables. SQL statements can
in general do anything to a virtual table that they can do to a real table,
with the following exceptions:
- One cannot create a trigger on a virtual table.
- One cannot create additional indices on a virtual table. (Virtual tables can
have indices but that must be built into the virtual table implementation.
Indices cannot be added separately using CREATE INDEX
statements.)
- One cannot run ALTER TABLE ... ADD COLUMN commands against a
virtual table.
- Particular virtual table implementations might impose additional constraints.
For example, some virtual implementations might provide read-only tables. Or
some virtual table implementations might allow INSERT or
DELETE but not UPDATE. Or some virtual table
implementations might limit the kinds of UPDATEs that can be
made.

Example of virtual tables, already included in the SQLite3 engine,
are FTS or
RTREE tables. A
custom virtual table might represent in-memory data structures (like
TSQLVirtualTableJSON, TSQLVirtualTableBinary). Or it might
represent a view of data on disk that is not in the SQLite3 format
(e.g. TSQLVirtualTableLog). Or the application might compute the
content of the virtual table on demand.

Thanks to the generic implementation of Virtual Table in SQLite3,
you can use such tables in your SQL statement, and even safely execute a
SELECT statement with JOIN or custom functions,
mixing normal SQLite3 tables and any other Virtual Table.

A dedicated mechanism has been added to the framework, beginning with
revision 1.13, in order to easily add such virtual tables with pure Delphi
code, just by inheriting some classes.

The SynCommons unit has been enhanced:
- new BinToBase64 and Base64ToBin conversion
functions;
- new low-level RTTI functions for handling record types: RecordEquals,
RecordSave, RecordSaveLength, RecordLoad;
- new TDynArray object, which is a wrapper around any dynamic
array.

With TDynArray, you can access any dynamic array (like
TIntegerDynArray = array of integer) using TList-like
properties and methods, e.g. Count, Add, Insert, Delete, Clear, IndexOf,
Find, Sort and some new methods like LoadFromStream, SaveToStream,
LoadFrom and SaveTo which allow fast binary serialization
of any dynamic array, even containing strings or records - a
CreateOrderedIndex method is also available to create individual
index according to the dynamic array content. You can also serialize the array
content into JSON, if you wish.

What I like with dynamic arrays is that they are reference-counted, don't
need any Create/try..finally...Free code, and are well handled by
the Delphi compiler.
They are no replacement to a TCollection nor a
TList (which are the standard and efficient way of storing
class instances), but they are very handy way of having a list of content or a
dictionary at hand, with no class nor properties definition.
You can look at them like Python's list, tuples (via records handling) and
dictionaries (via Find method), in pure Delphi. Our new methods
(about searching and serialization) allow most usage of those script-level
structures in your Delphi code.