ddn.var for a full-featured dynamic value type with richer semantics,
ddn.data.json5 for JSON5 parsing and serialization utilities.
Compact Dynamic Value Type
This module provides adam, a memory-efficient dynamic value type designed to match the semantics and API of ddn.var while minimizing memory footprint. It is suitable for applications where large numbers of dynamic values must be stored or transmitted with minimal overhead.
Features:
size_t word.bool, all D integer types, float, double, and dchar.adam[]) and objects (adam[string]) with JSON-like semantics.real → double, char/wchar → dchar to keep the tag space small.ddn.var operators and conversions; JSON-like
serialization via toString().
Differences from ddn.var:
adam intentionally does not support DATE, TIME, DATETIME,
SYSTIME, or DURATION types. This is a deliberate design choice to maintain compactness and simplicity. Applications requiring temporal values should use ddn.var instead, or encode temporal values as strings or numeric timestamps.
real is stored as double, and char/wchar are stored as dchar,
reducing the number of type tags needed.
adam copies string data on assignment (via .idup) to ensure
memory safety, whereas some operations may differ from var.
Example:
import ddn.adam;
// Scalar values
adam i = 42;
adam d = 3.14;
adam s = "hello";
// Arrays
adam arr = [adam(1), adam(2), adam(3)];
assert(arr.length == 3);
// Objects
adam obj;
obj["name"] = "alice";
obj["age"] = 30;
assert(obj["name"].as!string == "alice");
// JSON-like serialization (compact, strings quoted and escaped)
string json = obj.toString();Platform Notes:
adam.sizeof == 16.adam.sizeof is 12 bytes with typical ABIs (an
8-byte payload union plus the 4-byte tag word; platform alignment rules may pad this), and array/string lengths are limited to 28 bits.
ddn.var for a full-featured dynamic value type with richer semantics,
ddn.data.json5 for JSON5 parsing and serialization utilities.
ddn.adam — a compact dynamic value type.
This implementation aims to match ddn.var semantics and API shape as closely as possible, while keeping the value representation very small.
Representation:
_data)._lenTag) that packs the Type tag into the high bits and (for STRING and ARRAY) the length into the low bits.Type normalization (to keep the tag space small):
real values are stored as Type.DOUBLE (converted to double).char and wchar values are stored as Type.DCHAR (converted to dchar).On 64-bit platforms (size_t.sizeof == 8):
adam.sizeof == 16 (payload 8 bytes + tagged-length 8 bytes).On 32-bit platforms (size_t.sizeof == 4):
TAG_BITS = 4, array/string lengths are limited to 28 bits (i.e., max length 2^28 - 1).Important design rule:
private Data _dataprivate size_t _lenTagprivate TAG_BITSprivate size_t TAG_SHIFTprivate size_t LEN_MASKprivate size_t TAG_MASKprivate size_t LENGTH_BITSvoid _validateLength(size_t n) static @safeValidate that a length value fits in the tagged-length representation.void _setLengthBits(size_t n) nothrow @nogc pure @safeSet the length bits in the tagged-length word.Type type() @property const nothrow @nogc pure @safeGet the current type tag (property form like `var.type`).bool isNumeric() @property const nothrow @nogc pure @safe`true` if the value holds a numeric type (integer or floating).T as(T)() const @trustedConvert to target type `T`. Returns `T.init` on failure (matches `var.as!T` behavior).Nullable!T tryAs(T)() const @safe`tryAs!T` returns a `Nullable!T` that is null when conversion fails.adam opIndex(size_t index) ref @safeAccess an array element by index, returning a mutable reference.void opIndexAssign(T)(T rhs, size_t index) @safeAssign a value to an array element at the given index.adam opSlice(size_t start, size_t end) const @safeSlice an array and return a new ARRAY value (copies elements).void opOpAssign(string op, T)(T rhs) if (op == "~") @safeConcatenation assignment (`~=`): appends to arrays / concatenates strings, promoting NULL as documented for `append`.bool insert(T)(size_t index, T rhs) @safeInsert `rhs` at position `index` (0..length). Returns false if index is out of range.int opApply(scope int delegate(ref adam) dg)Foreach support over arrays.int opApply(scope int delegate(ref const(adam)) @safe dg) const @safeConst foreach support over arrays.ObjectBox * _ensureObjectBox(string op) @trustedPromote NULL -> OBJECT and ensure a box exists.void opIndexAssign(T)(T rhs, string key) @safeStore `rhs` under `key`, promoting NULL to an OBJECT on first write.adam opDispatch(string name)() ref @safeRoute unknown identifiers to map entries (autovivifying like opIndex(string)): `obj.foo = 1;` stores key `"foo"`, and `obj.foo` reads/creates it.const(adam) opDispatch(string name)() ref const @safeNon-inserting member lookup: throws `AdamKeyException` for missing names.adam[] values() @property const @safeThe object's values in key order (matching `keys`). Empty for NULL.int opApply(scope int delegate(const string, ref adam) dg)Foreach support over map entries.int opApply(scope int delegate(const string, ref const(adam)) @safe dg) const @safeConst foreach support over map entries.adam merge(const adam other, bool deepMerge = false) ref return @safeMerge another object into this one (like `var.merge`).adam path(const string[] pathParts) const @safeDeep-copy the value at `pathParts`, or NULL if any hop is missing.adam path(const string pathStr) const @safeDeep-copy the value at the dot-separated `pathStr`, or NULL if missing.this(T arg)Construct from another value `arg` (mirrors `var.this(T)`).TypeObjectBoxBase exception type for ddn.adam runtime misuse.
this(string msg, string file = __FILE__, size_t line = __LINE__)Thrown when an object key is missing for an operation that requires it.
this(string op, string key, string file = __FILE__, size_t line = __LINE__)Thrown when an array index is out of range for an operation that requires it.
this(string op, size_t index, size_t length, string file = __FILE__, size_t line = __LINE__)