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:
| Quantity | Value |
|---|---|
| Largest positive count | 2^63 - 1 = 9,223,372,036,854,775,807 ticks |
| Ticks per second | 10,000,000 |
| Largest positive duration | Approximately 922,337,203,685 seconds, or 29,227.7 Gregorian years |
| Instant range from the Unix epoch | Approximately 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:
- The Butlerian Jihad: approximately 13,000 CE in The Dune Encyclopedia.
- The Horus Heresy: 30,005 to 30,014 CE (
005.M31to014.M31).
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.
| Calculation | Value |
|---|---|
| Probe's peak speed | 191.2 km/s = 191,200 m/s |
| Time interval | 1 tick = 0.0000001 s |
| Distance = speed × time | 191,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
i64orf64only. - 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.
Rust conversion
Rust uses std::time::Duration and std::time::SystemTime.
Durationhas no sign. A negative tick count converts to zero, or toNonewhen a negative value has a meaning: a negativelock_timeoutwaits without limit.- Conversion to ticks truncates sub-tick nanoseconds toward zero.
- A
Durationbeyond thei64tick range saturates. - A
SystemTimeisUNIX_EPOCHplus or minus the tick count. An instant outside the host range causesinvalid_argumentwith codetime_out_of_range. Noneshows an absent instant.
Value types
Each value type has one meaning in every language. Types below use the selected language's spelling.
| Value | Type | Unit | Range | Absent |
|---|---|---|---|---|
| document | Document | None | Available memory | A null handle |
| format | Format | None | JSON, DUST, EDN, TRON | Never absent |
| node_id | NodeId | Count | 0 through 2147483646 | 4294967295 |
| transaction | i64 | Count | Signed 64-bit integer | Zero selects the latest snapshot |
| entity | i64 | Count | Signed 64-bit integer | Never absent |
| instant | Option<SystemTime> | 100 ns ticks | Signed 64-bit ticks (host ranges differ) | Zero when no commit was written |
| duration | Duration or Option<Duration> | 100 ns ticks | Signed 64-bit ticks (host ranges differ) | Each field defines its zero or negative sentinel |
| number | Number::I64 or Number::F64 | None | i64 or finite f64 | Never absent |
| bytes | &[u8] | Bytes | Available memory | An 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.
| Kind | Meaning |
|---|---|
none | The operation returns no value. |
version | An ABI version number. |
text | UTF-8 text, or binary bytes for TRON. |
integer | A signed 64-bit integer. |
real | A finite 64-bit floating-point number. |
count | A count that is not negative. |
boolean | True or false. |
name | A string: a key, title, path, kind or pattern. |
value | A host value. The host mapping gives its document form. |
source | A stored definition path or an inline definition document. |
Binding mechanics
Bindings may include these arguments in addition to native operation arguments:
| Argument | Purpose |
|---|---|
| Allocator | Owns copied output and diagnostics. |
| Cancellation context | Controls cancellation. |
| Loaded library | Selects 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
let token = std::fs::read_to_string("license.token")?;
stardust::activate(token.trim())?;
let result = stardust::license()?;{
"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
let input = stardust::NodeInput { kind: Kind::NUMBER, number: Number::I64(41), ..Default::default() };
let mut builder = stardust::builder()?;
builder.add(input)?;
let result = input;{
"parent": null,
"kind": "number",
"boolean": false,
"number": 41,
"key": "",
"text": ""
}database/merge_patch
seed_text holds this document:
{
"#_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"
}
}
let patch = stardust::parse(seed_text, Format::JSON)?;
let result = db.merge_patch(&patch, None)?;{
"status": "committed",
"transaction": 4,
"facts": 12,
"committed_at": 17923367684900903,
"ids": {
"#_ada": 1,
"#_bob": 2,
"#_cy": 3
}
}database/mutation
birthday_text holds this document:
{
"query": {
"find": [
"?e"
],
"where": [
[
"?e",
"name",
"Ada"
]
]
},
"patch": {
"?e": {
"age": 37
}
}
}
let source = stardust::parse(birthday_text, Format::JSON)?;
let mutation = db.mutation(&source)?;
let result = mutation.run(None, &RunOptions::default(), None)?;{
"status": "committed",
"transaction": 5,
"facts": 1,
"committed_at": 17923367687017263,
"ids": {}
}database/patch_definition
definition_text holds this document:
{
"parameters": {
"?role": "engineer"
},
"find": [
"?name"
],
"where": [
[
"?e",
"role",
"?role"
],
[
"?e",
"name",
"?name"
]
],
"orderBy": [
"?name"
]
}
let definition = stardust::parse(definition_text, Format::JSON)?;
let result = db.patch_definition("queries", "people/by_role", &definition)?;{
"status": "committed",
"transaction": 3,
"facts": 0,
"committed_at": 17923367687677576,
"ids": {}
}document/read
input_text holds this document:
{
"x": 41
}
let input = stardust::parse(input_text, Format::JSON)?;
let result = input.read(input.root()?)?;{
"kind": "object",
"boolean": false,
"parent": null,
"position": 0,
"first_child": 1,
"next_sibling": null,
"key": "",
"text": ""
}document/read_many
input_text holds this document:
{
"x": 41
}
let input = stardust::parse(input_text, Format::JSON)?;
let views = input.read_many(input.root()?, 1)?;
let result = views.into_iter().next().ok_or("missing node view")?;{
"kind": "object",
"boolean": false,
"parent": null,
"position": 0,
"first_child": 1,
"next_sibling": null,
"key": "",
"text": ""
}Read the process license
let result = stardust::license()?;{
"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:
{
"query": {
"find": [
"?e"
],
"where": [
[
"?e",
"name",
"Ada"
]
]
},
"patch": {
"?e": {
"age": 37
}
}
}
let source = stardust::parse(birthday_text, Format::JSON)?;
let mutation = db.mutation(&source)?;
let result = mutation.run(None, &RunOptions::default(), None)?;
mutation.close();{
"status": "committed",
"transaction": 5,
"facts": 1,
"committed_at": 17923367690249588,
"ids": {}
}mutation/run
birthday_text holds this document:
{
"query": {
"find": [
"?e"
],
"where": [
[
"?e",
"name",
"Ada"
]
]
},
"patch": {
"?e": {
"age": 37
}
}
}
let source = stardust::parse(birthday_text, Format::JSON)?;
let mutation = db.mutation(&source)?;
let result = mutation.run(None, &RunOptions::default(), None)?;{
"status": "committed",
"transaction": 5,
"facts": 1,
"committed_at": 17923367691058177,
"ids": {}
}Open with explicit native options
let options = stardust::Options { page_size: 0, ..Default::default() };
let _database = stardust::open(database_path, &options)?;
let result = options;{
"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
let options = stardust::Options { page_size: 0, lock_timeout: None, ..Default::default() };
let _database = stardust::open(database_path, &options)?;
let result = options;{
"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:
{
"find": [
"?name"
],
"where": [
[
"?e",
"name",
"?name"
]
],
"orderBy": [
"?name"
]
}
boundary_text holds this document:
9223372036854775807
let definition = stardust::parse(query_text, Format::JSON)?;
let query = db.query(&definition)?;
let boundary = stardust::parse(boundary_text, Format::JSON)?;
let seconds = boundary.i64(boundary.root()?)? as u64;
let options = RunOptions { page_size: 2, max_cost: std::time::Duration::from_secs(seconds), ..Default::default() };
let _rows = query.run(None, &options, None)?;
let result = options;{
"page_size": 2,
"max_cost": 9223372036854775807,
"as_of": 0
}Truncate a positive sub-tick duration
query_text holds this document:
{
"find": [
"?name"
],
"where": [
[
"?e",
"name",
"?name"
]
],
"orderBy": [
"?name"
]
}
boundary_text holds this document:
99
let definition = stardust::parse(query_text, Format::JSON)?;
let query = db.query(&definition)?;
let boundary = stardust::parse(boundary_text, Format::JSON)?;
let nanos = boundary.i64(boundary.root()?)? as u64;
let options = RunOptions { page_size: 2, max_cost: std::time::Duration::from_nanos(nanos), ..Default::default() };
let _rows = query.run(None, &options, None)?;
let result = options;{
"page_size": 2,
"max_cost": 0,
"as_of": 0
}Run with typed query options
query_text holds this document:
{
"find": [
"?name"
],
"where": [
[
"?e",
"name",
"?name"
]
],
"orderBy": [
"?name"
]
}
let definition = stardust::parse(query_text, Format::JSON)?;
let query = db.query(&definition)?;
let options = RunOptions { page_size: 2, max_cost: std::time::Duration::from_secs(1), ..Default::default() };
let _rows = query.run(None, &options, None)?;
let result = options;{
"page_size": 2,
"max_cost": 10000000,
"as_of": 0
}Read the complete initial subscription event
query_text holds this document:
{
"find": [
"?name"
],
"where": [
[
"?e",
"name",
"?name"
]
],
"orderBy": [
"?name"
]
}
let definition = stardust::parse(query_text, Format::JSON)?;
let query = db.query(&definition)?;
let mut subscription = query.subscribe(None, &RunOptions::default())?;
let event = subscription.next().await?.ok_or("no initial event")?;
let result = event;{
"sequence": 1,
"superseded": 0,
"transaction": 4,
"result": [
{
"name": "Ada"
},
{
"name": "Bob"
},
{
"name": "Cy"
}
]
}