Types and units

The document operations use typed values, and the database operations add time values. The tables use the type names of your selected language. Use it to find the unit, range, default, and absent value of a field.

Text uses UTF-8. This includes string values, object keys, and JSON, DUST, and EDN input and output. The document builders reject invalid UTF-8 strings and keys. TRON is binary, but its strings and keys also use UTF-8. Byte lengths count encoded bytes, rather than characters.

This matches JSON's UTF-8 requirement for interchange (RFC 8259, section 8.1).

Evaluation and database read results recursively omit object fields containing empty objects or arrays. This includes objects emptied by nested omissions. Null, empty strings, false, zero and array positions stay intact. Input documents and expanded programs preserve their contents.

  • Time and units defines the tick, its range and resolution, and the host conversion.
  • Value types defines documents, formats, node IDs, transactions, entities, instants, durations, numbers, and bytes.
  • Other kinds defines the remaining parameter and result kinds, such as text and counts.

Time and units

The ABI uses one time unit: the 100 ns tick, stored as an i64 count.

  • A duration is a signed tick count.
  • An instant is the tick count since the Unix epoch, 1970-01-01T00:00:00Z. It is in UTC, with no leap seconds and no time zone.
  • DUST durations and UTC values use the same unit, so database data and API parameters need no unit conversion.

Range

An i64 tick count holds approximately 29,227 years on each side of the Unix epoch:

QuantityValue
Largest positive count2^63 - 1 = 9,223,372,036,854,775,807 ticks
Ticks per second10,000,000
Largest positive durationApproximately 922,337,203,685 seconds, or 29,227.7 Gregorian years
Instant range from the Unix epochApproximately 27,258 BCE to 31,197 CE

This covers all written history, including cuneiform from approximately 3400 BCE.

Note

It also covers these fictional settings:

Resolution

One tick is 100 ns, or 0.0000001 s.

Note

At its peak speed, the Parker Solar Probe, the fastest human-made object, travels approximately 19 mm in one tick.

CalculationValue
Probe's peak speed191.2 km/s = 191,200 m/s
Time interval1 tick = 0.0000001 s
Distance = speed × time191,200 × 0.0000001 = 0.01912 m ≈ 19 mm

Thread wake-up jitter on common platforms is microseconds, so a smaller unit gives no useful precision. Windows also counts time in 100 ns units.

Order and time zones

  • Integer ticks have no floating-point rounding. ABI numbers are i64 or f64 only.
  • An instant in UTC ticks has no time zone or daylight-saving state. Each instant has one meaning, and integer comparison gives the time order.
  • The host applies time zones for presentation.
  • Each binding uses the duration and instant types of its language and converts at the call boundary.

C conversion

C passes durations and instants as int64_t tick counts. The caller converts them.

Value types

Each value type has one meaning in every language. Types below use the selected language's spelling.

ValueTypeUnitRangeAbsent
documentstardust_document*NoneAvailable memoryA null handle
formatstardust_source_formatNoneJSON, DUST, EDN, TRONNever absent
node_iduint32_tCount0 through 21474836464294967295
transactionint64_tCountSigned 64-bit integerZero selects the latest snapshot
entityint64_tCountSigned 64-bit integerNever absent
instantint64_t100 ns ticksSigned 64-bit ticks (host ranges differ)Zero when no commit was written
durationint64_t100 ns ticksSigned 64-bit ticks (host ranges differ)Each field defines its zero or negative sentinel
numberstardust_number_kind, int64_t, doubleNonei64 or finite f64Never absent
bytesstardust_bytesBytesAvailable memoryAn empty sequence

document

An owned native document preserves ordered members and duplicate keys.

format

The format selects a text codec or binary TRON.

node_id

A node ID selects a node within its owning document.

transaction

A transaction selects a committed database snapshot.

entity

An entity identifies an object in a database.

instant

An instant counts 100 ns ticks since the Unix epoch. An instant outside the host range reports time_out_of_range.

duration

A duration counts 100 ns ticks. Conversions beyond a host duration range saturate.

number

A number is a signed integer or finite floating-point value.

bytes

Bytes contain a borrowed byte sequence with an explicit length.

Other kinds

These kinds have no value type of their own. The signature of each operation gives the exact type. Parameter tables link handle and record kinds to the operation or record that defines them.

KindMeaning
noneThe operation returns no value.
versionAn ABI version number.
textUTF-8 text, or binary bytes for TRON.
integerA signed 64-bit integer.
realA finite 64-bit floating-point number.
countA count that is not negative.
booleanTrue or false.
nameA string: a key, title, path, kind or pattern.
valueA host value. The host mapping gives its document form.
sourceA stored definition path or an inline definition document.

Binding mechanics

Bindings may include these arguments in addition to native operation arguments:

ArgumentPurpose
AllocatorOwns copied output and diagnostics.
Cancellation contextControls cancellation.
Loaded librarySelects the native ABI library.

Optional wrappers such as pointers, Option and Maybe keep the wrapped type's contract.

Record examples

The generator captured each result from the native library.

Activate the license from a token file

c569 B
char token[4097];
FILE *file = fopen("license.token", "rb");
if (file == NULL) return STARDUST_INVALID_ARGUMENT;
size_t length = fread(token, 1, sizeof token, file);
fclose(file);
while (length > 0 && (token[length - 1] == '\n' || token[length - 1] == '\r')) length--;
stardust_bytes bytes = {(const uint8_t *)token, (int64_t)length};
stardust_license_view result = {.size = sizeof(stardust_license_view), .version = 1};
uint32_t status = stardust_activate(bytes);
if (status == STARDUST_OK) status = stardust_license(&result);
if (status != STARDUST_OK) return status;
Result / 234 B
{
  "active": true,
  "failure": "none",
  "support_end": 72580320000000000,
  "expires": null,
  "build": null,
  "refused_support_end": null,
  "subject": "test",
  "license_id": "test",
  "name": "",
  "license": "",
  "tier": ""
}

Add a typed integer node

c452 B
stardust_node_input input = {.parent=STARDUST_DOCUMENT_NO_NODE, .kind=STARDUST_NUMBER, .number=STARDUST_NUMBER_INTEGER, .integer=41};
stardust_document_builder *builder = NULL;
uint32_t status = stardust_document_builder_create(&builder);
if (status == STARDUST_OK) status = stardust_document_builder_add_many(builder, &input, 1);
stardust_document_builder_release(builder);
if (status != STARDUST_OK) return status;
stardust_node_input result = input;
Result / 103 B
{
  "parent": null,
  "kind": "number",
  "boolean": false,
  "number": 41,
  "key": "",
  "text": ""
}

database/merge_patch

seed_text holds this document:

json333 B
{
  "#_ada": {
    "name": "Ada",
    "role": "engineer",
    "age": 36
  },
  "#_bob": {
    "name": "Bob",
    "role": "engineer",
    "age": 41,
    "mentor": {
      "#link": "#_ada"
    }
  },
  "#_cy": {
    "name": "Cy",
    "role": "designer",
    "age": 29
  },
  "#_tx": {
    "author": "smoke",
    "reason": "seed"
  }
}
c336 B
stardust_document *patch = NULL;
stardust_result *result = NULL;
uint32_t status = stardust_parse(seed_text, STARDUST_JSON, &patch);
if (status == STARDUST_OK) status = stardust_db_merge_patch(db, patch, NULL, NULL, &result);
stardust_document_release(patch);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 161 B
{
  "status": "committed",
  "transaction": 4,
  "facts": 12,
  "committed_at": 17923367928294190,
  "ids": {
    "#_ada": 1,
    "#_bob": 2,
    "#_cy": 3
  }
}

database/mutation

birthday_text holds this document:

json186 B
{
  "query": {
    "find": [
      "?e"
    ],
    "where": [
      [
        "?e",
        "name",
        "Ada"
      ]
    ]
  },
  "patch": {
    "?e": {
      "age": 37
    }
  }
}
c615 B
stardust_document *definition = NULL;
stardust_db_mutation *mutation = NULL;
stardust_result *result = NULL;
uint32_t status = stardust_parse(birthday_text, STARDUST_JSON, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_mutation_open(db, &source, &mutation);
if (status == STARDUST_OK) status = stardust_db_mutation_run(mutation, NULL, NULL, NULL, &result);
stardust_db_mutation_release(mutation);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 111 B
{
  "status": "committed",
  "transaction": 5,
  "facts": 1,
  "committed_at": 17923367930692901,
  "ids": {}
}

database/patch_definition

definition_text holds this document:

json234 B
{
  "parameters": {
    "?role": "engineer"
  },
  "find": [
    "?name"
  ],
  "where": [
    [
      "?e",
      "role",
      "?role"
    ],
    [
      "?e",
      "name",
      "?name"
    ]
  ],
  "orderBy": [
    "?name"
  ]
}
c485 B
stardust_document *definition = NULL;
stardust_result *result = NULL;
stardust_bytes kind = {(const uint8_t *)"queries", 7};
stardust_bytes path = {(const uint8_t *)"people/by_role", 14};
uint32_t status = stardust_parse(definition_text, STARDUST_JSON, &definition);
if (status == STARDUST_OK) status = stardust_db_definition_patch(db, kind, path, definition, &result);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 111 B
{
  "status": "committed",
  "transaction": 3,
  "facts": 0,
  "committed_at": 17923367931574147,
  "ids": {}
}

document/read_many

input_text holds this document:

json14 B
{
  "x": 41
}
c527 B
stardust_document *source = NULL;
uint32_t root = 0;
stardust_node_view view = {0};
int64_t written = 0;
uint32_t status = stardust_parse(input_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_document_root(source, &root);
if (status == STARDUST_OK) status = stardust_document_read_many(source, root, 1, &view, &written);
stardust_document_release(source);
if (status != STARDUST_OK) return status;
// The root object has no key or text, so the view borrows nothing.
stardust_node_view result = view;
Result / 148 B
{
  "kind": "object",
  "boolean": false,
  "parent": null,
  "position": 0,
  "first_child": 1,
  "next_sibling": null,
  "key": "",
  "text": ""
}

document/read_many

input_text holds this document:

json14 B
{
  "x": 41
}
c527 B
stardust_document *source = NULL;
uint32_t root = 0;
stardust_node_view view = {0};
int64_t written = 0;
uint32_t status = stardust_parse(input_text, STARDUST_JSON, &source);
if (status == STARDUST_OK) status = stardust_document_root(source, &root);
if (status == STARDUST_OK) status = stardust_document_read_many(source, root, 1, &view, &written);
stardust_document_release(source);
if (status != STARDUST_OK) return status;
// The root object has no key or text, so the view borrows nothing.
stardust_node_view result = view;
Result / 148 B
{
  "kind": "object",
  "boolean": false,
  "parent": null,
  "position": 0,
  "first_child": 1,
  "next_sibling": null,
  "key": "",
  "text": ""
}

Read the process license

c172 B
stardust_license_view result = {.size = sizeof(stardust_license_view), .version = 1};
uint32_t status = stardust_license(&result);
if (status != STARDUST_OK) return status;
Result / 234 B
{
  "active": true,
  "failure": "none",
  "support_end": 72580320000000000,
  "expires": null,
  "build": null,
  "refused_support_end": null,
  "subject": "test",
  "license_id": "test",
  "name": "",
  "license": "",
  "tier": ""
}

mutation/close

birthday_text holds this document:

json186 B
{
  "query": {
    "find": [
      "?e"
    ],
    "where": [
      [
        "?e",
        "name",
        "Ada"
      ]
    ]
  },
  "patch": {
    "?e": {
      "age": 37
    }
  }
}
c615 B
stardust_document *definition = NULL;
stardust_db_mutation *mutation = NULL;
stardust_result *result = NULL;
uint32_t status = stardust_parse(birthday_text, STARDUST_JSON, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_mutation_open(db, &source, &mutation);
if (status == STARDUST_OK) status = stardust_db_mutation_run(mutation, NULL, NULL, NULL, &result);
stardust_db_mutation_release(mutation);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 111 B
{
  "status": "committed",
  "transaction": 5,
  "facts": 1,
  "committed_at": 17923367934714808,
  "ids": {}
}

mutation/run

birthday_text holds this document:

json186 B
{
  "query": {
    "find": [
      "?e"
    ],
    "where": [
      [
        "?e",
        "name",
        "Ada"
      ]
    ]
  },
  "patch": {
    "?e": {
      "age": 37
    }
  }
}
c615 B
stardust_document *definition = NULL;
stardust_db_mutation *mutation = NULL;
stardust_result *result = NULL;
uint32_t status = stardust_parse(birthday_text, STARDUST_JSON, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_mutation_open(db, &source, &mutation);
if (status == STARDUST_OK) status = stardust_db_mutation_run(mutation, NULL, NULL, NULL, &result);
stardust_db_mutation_release(mutation);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 111 B
{
  "status": "committed",
  "transaction": 5,
  "facts": 1,
  "committed_at": 17923367935512435,
  "ids": {}
}

Open with explicit native options

c405 B
stardust_db_options result = {.size=sizeof(result), .version=1, .create=1, .page_size=0};
stardust_db *database = NULL;
stardust_result *opening = NULL;
uint32_t status = stardust_db_open((stardust_bytes){(const uint8_t *)database_path, (int64_t)strlen(database_path)}, &result, &database, &opening);
stardust_result_release(opening);
stardust_db_close(database);
if (status != STARDUST_OK) return status;
Result / 194 B
{
  "create": true,
  "clear": false,
  "pin_memory": false,
  "page_size": 0,
  "lock_timeout": 0,
  "initial_map_bytes": 0,
  "commit_batch": 0,
  "plugins": null,
  "memory_budget_bytes": 0
}

Wait for the database lock without a limit

c429 B
stardust_db_options result = {.size=sizeof(result), .version=1, .create=1, .page_size=0, .lock_timeout_ticks=-1};
stardust_db *database = NULL;
stardust_result *opening = NULL;
uint32_t status = stardust_db_open((stardust_bytes){(const uint8_t *)database_path, (int64_t)strlen(database_path)}, &result, &database, &opening);
stardust_result_release(opening);
stardust_db_close(database);
if (status != STARDUST_OK) return status;
Result / 197 B
{
  "create": true,
  "clear": false,
  "pin_memory": false,
  "page_size": 0,
  "lock_timeout": null,
  "initial_map_bytes": 0,
  "commit_batch": 0,
  "plugins": null,
  "memory_budget_bytes": 0
}

Capture the host duration boundary

query_text holds this document:

json134 B
{
  "find": [
    "?name"
  ],
  "where": [
    [
      "?e",
      "name",
      "?name"
    ]
  ],
  "orderBy": [
    "?name"
  ]
}

boundary_text holds this document:

json20 B
9223372036854775807
c1.1 kB
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_document *boundary = NULL;
uint32_t boundary_root = 0;
int64_t ticks = 0;
uint32_t status = stardust_parse(boundary_text, STARDUST_JSON, &boundary);
if (status == STARDUST_OK) status = stardust_document_root(boundary, &boundary_root);
if (status == STARDUST_OK) status = stardust_document_i64(boundary, boundary_root, &ticks);
stardust_document_release(boundary);
if (status == STARDUST_OK) status = stardust_parse(query_text, STARDUST_JSON, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
stardust_db_query_options result = {.size=sizeof(result), .version=1, .page_size=2, .max_cost_ticks=ticks};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, &result, NULL, &rows);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) return status;
Result / 69 B
{
  "page_size": 2,
  "max_cost": 9223372036854775807,
  "as_of": 0
}

The C ABI takes complete 100-ns ticks

query_text holds this document:

json134 B
{
  "find": [
    "?name"
  ],
  "where": [
    [
      "?e",
      "name",
      "?name"
    ]
  ],
  "orderBy": [
    "?name"
  ]
}
c532 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_JSON, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
stardust_db_query_options result = {.size=sizeof(result), .version=1, .page_size=2, .max_cost_ticks=1};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) return status;
Result / 51 B
{
  "page_size": 2,
  "max_cost": 1,
  "as_of": 0
}

A negative configured duration is not an absent field

c90 B
stardust_db_query_options result = {.size=sizeof(result), .version=1, .max_cost_ticks=-1};
Result / 52 B
{
  "page_size": 0,
  "max_cost": -1,
  "as_of": 0
}

Run with typed query options

query_text holds this document:

json134 B
{
  "find": [
    "?name"
  ],
  "where": [
    [
      "?e",
      "name",
      "?name"
    ]
  ],
  "orderBy": [
    "?name"
  ]
}
c713 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_JSON, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
stardust_db_query_options result = {.size=sizeof(result), .version=1, .page_size=2, .max_cost_ticks=STARDUST_TICKS_PER_SECOND};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, &result, NULL, &rows);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) return status;
Result / 58 B
{
  "page_size": 2,
  "max_cost": 10000000,
  "as_of": 0
}

Read the complete initial subscription event

query_text holds this document:

json134 B
{
  "find": [
    "?name"
  ],
  "where": [
    [
      "?e",
      "name",
      "?name"
    ]
  ],
  "orderBy": [
    "?name"
  ]
}
c832 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_subscription *subscription = NULL;
stardust_db_live_event event = {0};
uint32_t status = stardust_parse(query_text, STARDUST_JSON, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_subscribe(query, NULL, NULL, &subscription);
if (status == STARDUST_OK) status = stardust_db_subscription_next(subscription, 0, &event);
stardust_db_subscription_release(subscription);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(event.result); return status;}
stardust_db_live_event result = event;
Result / 174 B
{
  "sequence": 1,
  "superseded": 0,
  "transaction": 4,
  "result": [
    {
      "name": "Ada"
    },
    {
      "name": "Bob"
    },
    {
      "name": "Cy"
    }
  ]
}