Skip to main content

Module value

Module value 

Source
Expand description

Value-word core: every DSL value is a fixed-size word (a TypeDescriptor vtable pointer plus a 64-bit ValuePayload) interpreted through that vtable.

There is exactly one representation for every type. Payloads that fit in 64 bits (integers, floats, booleans, handles, and host scalars annotated #[oxdock_type(inline)]) ride directly in the payload; everything else rides behind a thin pointer to either an owned Box<T> (exclusive heaps: STRING, PATH, DURATION, PIPE, most host types) or a shared Arc<T> (shared heaps: LIST, MAP, and host types annotated #[oxdock_type(shared)]). The vtable owns the lifecycle (clone, drop) and operations (eq, fmt), so Clone/Drop/PartialEq/Display on Value delegate instead of matching. There are no dynamic trait objects anywhere in this path: every hook is a monomorphic function pointer reached directly, with no table lookup and no lock.

Descriptors are canonical singletons: each #[oxdock_type] struct gets one &'static TypeDescriptor (built at compile time, shared by every word of that type), so words carry their own vtable and no registry of any kind exists. The twelve startup types (INT, FLOAT, STRING, BOOL, LIST, MAP, PATH, DURATION, PIPE, HANDLE, SEMAPHORE, PERMIT) are ordinary Rust structs annotated with #[oxdock_type], exactly as host types are. Name directories (which descriptor answers for "TAG") live per execution state in oxdock-core, never here: this module knows types, not names.

Ownership discipline (load-bearing, Miri-verified in crates/oxdock-core/tests/miri_value_words.rs):

  • Exclusive heap Values own their box exactly once. clone allocates a new box; drop frees it. No sharing, no aliasing. Because each box holds a concrete sized T, its pointer is thin: no double-boxing, no fat pointer casts, no metadata to lose.
  • Shared heap Values (LIST, MAP) co-own an Arc<T> buffer. clone bumps the strong count in O(1) with no allocation; drop releases one count and frees only the final word’s drop. Because the DSL exposes no interior mutability, aliases, or reference syntax, container graphs are strictly acyclic trees, so refcounting reclaims deterministically with no tracing collector. Mutable access goes only through Value::read_heap_mut, which detaches (clones the buffer) whenever the strong count exceeds 1, so a writer always exclusively owns a private buffer and clones never observe each other’s writes. Deriving &mut from a payload any other way is unsound.
  • Pointer casts are always Box::into_raw / Box::from_raw (exclusive) or Arc::into_raw / Arc::from_raw plus Arc::increment_strong_count (shared) round trips on the same concrete payload type, which preserves provenance. Inline words never touch the pointer domain; heap words never touch the integer domain.
  • Minting a word with a descriptor built for a different Rust type misdirects the vtable and is unsound. The mint_* constructors document this contract; hosts mint through the payload type’s own OxDockType::descriptor(), which cannot mismatch by construction.

Structs§

SemaphoreState
Shared admission-control counter backing SEMAPHORE words. Clones share the backend, so every word naming one semaphore observes the same count. Lock-free atomics throughout: no mutex exists to poison, so Drop paths stay infallible on every path including unwinding. No waiter ever sleeps on the counter, so the non-blocking acquire adds zero wedge surface.
TypeDescriptor
Vtable for one type: lifecycle plus operations. All hooks are plain function pointers (never closures) so descriptors stay Copy and the global table hands them out by value. Every hook documents the payload domain it expects; calling one with a foreign payload is unsound, and every call site is a single choke point reviewed with the layout.
Value

Traits§

OxDockType
Export hook for a DSL payload type, implemented by #[oxdock_type] on the payload struct itself. The canonical descriptor singleton backs every word of the type; user code never names a generated symbol.

Functions§

clone_boxed
Heap clone: deep-copy the box.
clone_copy
Inline clone: payloads are plain bytes.
clone_shared
Shared-heap clone: bump the Arc strong count, sharing the buffer. O(1) with no allocation.
drop_boxed
Heap drop: free the box.
drop_noop
Inline drop: nothing owns anything.
drop_shared
Shared-heap drop: release one Arc strong count, freeing the buffer only when the final word drops.
eq_boxed
Heap eq: compare the boxed values.
eq_inline
Inline eq: reconstruct both sides and compare.
eq_shared
Shared-heap eq: compare the shared values.
fmt_boxed
Heap fmt: render the boxed value.
fmt_inline
Inline fmt: reconstruct and render.
fmt_shared
Shared-heap fmt: render the shared value.
load_inline
Reconstruct a Copy value from an inline payload.
startup_descriptors
Canonical descriptors of the twelve startup types, in a fixed order, for seeding per-state name directories and static rendering (docs-gen). Each entry is the payload struct’s own singleton: no table, no lock.
store_inline
Copy a Copy value’s bytes into a payload. Panics when T exceeds 64 bits: such types must use the heap path.
type_anchor
Anchor of a type’s reference section, derived from its name the way the Markdown slugger derives it from the doc title.
unshare_boxed
Exclusive-heap unshare: the box is already uniquely owned, so the payload is returned unchanged with no allocation.
unshare_inline
Inline unshare: inline words hold bytes, not a heap buffer, so there is nothing to hand out mutably. Panics: reaching this hook means Value::read_heap_mut was called with an inline descriptor, a caller bug (mirrors store_inline’s size assert).
unshare_shared
Shared-heap unshare: detach on write. When the strong count is 1 the payload is returned unchanged (in-place, no allocation); otherwise the buffer is cloned, the word is rewritten to the private buffer, and the other clones keep the original. Either way the returned pointer addresses a buffer this word uniquely owns.

Unions§

ValuePayload
Payload half of a Value word: either the value’s bytes inline or a thin pointer to an owned Box<T> (exclusive heaps) or a shared Arc<T> (shared heaps), as the type’s descriptor dictates. Inline and pointer domains never mix for a given TypeDescriptor.