One ABI, zero algorithms. vinary-tree-interop is the stable C ABI the
vinary-tree family uses to hand live resources — dictionaries and weighted
finite-state transducers — between independently built libraries and
language bindings. The Rust crate is #![no_std], dependency-free, and
contains layouts and constants only: no functions, allocator, or I/O. The
native distribution also includes an optional header-only C++20 facade; it
implements ownership and provider adaptation without adding a linked runtime.
Every function at the binary boundary remains a pointer inside a
provider-supplied vtable. Project crates own concrete algorithms, while this
repository owns the bytes they agree on and the language-neutral adapters for
those bytes.
That inversion is what makes the family modular: a consumer that reads only
include/vinary_tree_interop.h (or the
bit-identical Rust definitions in src/lib.rs) can consume
resources from any provider — another family repo, another version of a
family repo, or a third party — without linking against it and without
touching Rust's unstable dynamic-library ABI.
A resource is exactly two machine words:
typedef struct VtResource {
void* context; /* provider-defined state */
const struct VtResourceVTable* vtable; /* provider-owned, immutable */
} VtResource;Handing one across the boundary is retain / release) and versioned capability
discovery (query_interface), the portable core of COM's IUnknown.
Three laws do the heavy lifting (stated precisely, with their proofs and
pins, in the ABI reference):
-
Two-word law —
sizeof(VtResource)is exactly two pointers; asserted at compile time in Rust and C++ and at runtime on the JVM. -
Copy-is-not-retain — copying the two words is free and confers no
ownership; each owned copy is paid for with one
retainand settled with exactly onerelease. -
Snapshot capture is
$\mathcal{O}(1)$ — mutable dictionaries hand consumers an immutable, structurally shared revision at operation start; copying the dictionary or holding a long-lived read lock violates the interface contract. Batched paging (node_edges/state_arcs, 256 entries recommended) keeps boundary crossings to$\lceil \deg(v) / 256 \rceil$ per node expansion.
Interfaces are named by 16-byte identifiers compared byte-for-byte and
negotiated through query_interface with a consumer-supplied minimum
version:
| Interface ID | Version | Payloads | What it is |
|---|---|---|---|
vt.dictionary.v1 |
1 (VT_DICTIONARY_INTERFACE_VERSION) |
VtDictionaryEdge, VtOptionalU64 |
Immutable dictionary snapshots: root/final/transition queries plus batched edge paging, over byte, Unicode-scalar, or u64 label domains, with optional u64 values. |
vt.dict.visit.v1 |
1 (VT_DICTIONARY_VISIT_INTERFACE_VERSION) |
VtDictionaryEdge |
Optional fused finality and edge-page inspection for callback-based dictionary traversal. |
vt.dict.graph.v1 |
1 (VT_DICTIONARY_GRAPH_INTERFACE_VERSION) |
VtDictionaryGraphNode, VtDictionaryGraphEdge, VtDictionaryGraphView |
Optional compact immutable snapshot graph. A consumer validates the complete borrowed view once, retains its owner, and thereafter traverses dense node/edge arrays without provider callbacks or consumer cache publication. |
vt.dict.entry.v1 |
1 (VT_DICTIONARY_ENTRIES_INTERFACE_VERSION) |
VtDictionaryEntry, VtDictionaryEntryBatchLimits, VtDictionaryEntryBatchView, VtDictionaryEntriesInfo, VtDictionaryEntriesCursor |
Optional finite lexicographic entry stream over one immutable revision. Cursor-owned arena batches use an explicit generation lease; reduce provides the same bounded stream through a callback. |
vt.snapshot.id.1 |
1 (VT_SNAPSHOT_IDENTITY_INTERFACE_VERSION) |
VtSnapshotIdentity |
Optional process-local immutable producer/revision identity for safely sharing derived state across separately retained views of the same snapshot. |
vt.scalar-wfst.1 |
1 (VT_WFST_INTERFACE_VERSION) |
VtWfstArc |
Immutable scalar-weighted FSTs: start/finality/arc paging with f64 weights in one of seven declared semirings, epsilon labels encoded by flag (never by magic value), lazy and acyclic capability claims. |
vt.lattice.val.1 |
1 (VT_LATTICE_INTERFACE_VERSION) |
VtResource operands and results |
Immutable lattice values: join, meet, equality, canonical bytes, diagnostics, and bounded batch folds with explicit runtime-thread and reentrancy capabilities. |
vt.semiring.val1 |
1 (VT_SEMIRING_INTERFACE_VERSION) |
VtSemiringValue |
Host-defined semiring operation contexts with compact provider-scoped values, explicit clone/release, identities, addition, multiplication, equality, natural order, canonical bytes, diagnostics, and bounded folds. |
vt.semiring.div1 · vt.semiring.str1 |
1 each | VtSemiringValue |
Independently negotiated division/left-division and Kleene-closure capabilities; an undefined mathematical result is VT_STATUS_END, not a sentinel value. |
vt.semiring.num1 · vt.semiring.prp1 |
1 each | Scalars and law flags | Optional numerical projections/quantization/probability plus declared algebraic laws and uniform closure bounds. Consumers validate every claimed law before selecting a specialized algorithm. |
Base protocol version: VT_ABI_VERSION = 1. The full change rules — what
may be added, what forks an identity, and the four distinct version
counters — are the ABI evolution policy.
| Repository | Dictionary + optional graph | Scalar WFST | Lattice value | Dynamic semiring | Notes |
|---|---|---|---|---|---|
| llattice | — | — | defines source trait; host packages produce | — | The Rust leaf stays dependency-free; target runtimes use this ABI capability. |
| libdictenstein | produces | — | consumes for values | — | Dictionary resources publish the base interface; immutable DynamicDawg snapshots additionally publish compact graphs in all three unit domains. |
| liblevenshtein | consumes | — | — | — | Validates compact graphs at snapshot acquisition and routes every applicable automaton through the shared captured-graph traversal seam. |
| duallity | consumes base dictionary | produces | consumes | consumes where product weights require it | Builds and combines fuzzy and product automata. |
| lling-llang | — | produces + consumes | produces + consumes | produces + consumes | Publishes dynamic adapters without changing native monomorphized paths. |
| shared JavaScript runtime | hosts | hosts | hosts | hosts | The sanctioned all-of-family surface for Node, WASI, and browsers. |
| Document | Contents |
|---|---|
| docs/abi-reference.md | The annotated, literate walk of the entire header: every declaration quoted and explained, the refcount/paging/two-word/snapshot laws in display math, the seven semirings defined, and a complete minimal C provider that compiles under -std=c17 -Wall -Wextra -Werror. |
| docs/abi-evolution.md | The four version counters and their jurisdictions, additive-versus-breaking rules per construct, worked examples (add an op, add a weight domain, retire a flag), the decision table, and the current family compatibility matrix. |
| docs/security-model.md | The family trust model: zones, the panic/exception containment law with file:line evidence, threading-by-claim, the input-validation duty table grounded in confirmed findings, exhaustion vectors, WASI capability policy, and explicit non-goals. |
| docs/npm-coordinate-migration.md | The RC5 canonical npm identity, immutable RC4 compatibility policy, fail-closed publication sequence, and consumer migration. |
| bindings/cpp/README.md | C++20 RAII consumption and mutex-free host-provider adapters for scalar WFSTs, lattice values, and generic semirings, with complete examples and failure/threading contracts. |
| bindings/dotnet/README.md | C# and F# collection usage plus lock-free managed host providers for scalar WFSTs, lattice values, and generic semirings, including optional algebraic capabilities and executable examples. |
| docs/language-bindings/go-host-providers.md | Go consumers and cgo-backed customer providers for lattice values, immutable scalar WFSTs, and dynamic semirings, including exact ownership, non-blocking callback gates, bounded pages, and executable examples. |
| docs/language-bindings/javascript-host-providers.md | JavaScript and TypeScript lattice, semiring, and scalar-WFST contracts, including current native/browser/WASI execution support, bounded operations, rooted lifetimes, generational handles, and hostile-callback containment. |
Twelve language-native mirrors of the interop structs and constants live under
bindings/, so non-C ecosystems consume the ABI idiomatically:
.NET · Fortran · Go · Haskell · JavaScript · Julia · JVM · Lua · OCaml · Python · Raku · Swift
C++ additionally has a header-only
vinary_tree::interop facade for RAII resource
ownership and customer-implemented scalar WFST, lattice, and semiring
providers. It is packaged beside the canonical C header and requires no linked
runtime.
The C header is the stable authority. The Raku-based generator at
scripts/generate-bindings.raku derives the
Julia and Raku constants, enum declarations, interface identities, raw struct
layouts, and typed callable adapters. It also emits the machine-readable
bindings/generated/abi-capabilities.tsv
inventory with both languages' names and signatures, interface relationships,
parameter direction and ownership, and threading constraints. Its --check
mode rejects generated drift without editing the worktree; --self-test
proves the detector rejects synthetic Julia and Raku changes as well as a
handwritten Julia ccall signature outside the generated region. Native
fixture tests then compare every Julia and Raku raw layout to C sizeof and
exercise the generated callable adapters; the other language packages retain
their own compiler and host-language conformance gates.
The optional entries-v1 surface is canonical in the Rust crate, C header, and generator-owned bundled C-header mirrors. Its generated conformance fixture pins status, flag, operation-order, and LP64/ARM32 layout values. Higher-level language adapters that do not yet expose a typed entries cursor continue to negotiate only the interfaces they model; entries-v1 is never inferred from a partial hand-written definition.
Three test suites in tests/ are the normative documents' teeth;
CI runs them on every push (cargo test --locked -p vinary-tree-interop):
tests/layout_contract.rs— exact sizes, alignments, and field offsets for every published type on the 64-bit and 32-bit ARM tiers; the two-word law; byte-exact interface identifiers; theOption<extern "C" fn>null niche that makes NULL-slot vtables one ABI.tests/discriminant_pins.rs— every enum discriminant and flag bit pinned exactly and matched wildcard-free, so adding a variant fails compilation until the evolution policy is consulted; zeroed reserved-field defaults.tests/vtable_evolution.rs— thequery_interfacenegotiation surface against a hand-rolled provider, including the additive-evolution proof: a future, larger vtable remains consumable through its v1 prefix, discovered viastruct_size.
| Fact | Value |
|---|---|
| Version | 4.0.0-rc.6 |
| Rust | edition 2021, rust-version 1.95, #![no_std] |
| Dependencies | none |
| License | Apache-2.0 |
Every published adapter uses the same retained-resource laws while mapping ownership and failures into its host language:
The generated dictionary_entries_v1.tsv fixture pins entries-v1 identifiers, statuses, flags, operation order, and LP64/ARM32 layouts for adapter conformance.
| Language/runtime | Distribution | Guide |
|---|---|---|
| C | Native header and CMake/pkg-config package | This README and docs/abi-reference.md |
| C++20+ | Header-only native facade and CMake/pkg-config package | bindings/cpp/README.md |
| Python 3.10+ | PyPI package vinary-tree-interop |
bindings/python/README.md |
| Java 22+, Kotlin, and Scala | Maven coordinate io.vinarytree:vinary-tree-interop |
bindings/jvm/README.md |
| JavaScript, TypeScript, and ClojureScript on Node.js, browsers, or WASI | npm package @vinary-tree/vinary-tree-interop |
bindings/javascript/README.md |
| Go 1.25+ with cgo | Go module github.com/vinary-tree/vinary-tree-interop/bindings/go/v4 |
bindings/go/README.md |
| Swift 6+ through Swift Package Manager | SwiftPM product VinaryTreeInterop |
bindings/swift/README.md |
| Fortran 2018 through fpm | fpm package vinary-tree-interop (final-version candidate during RC) |
bindings/fortran/README.md |
| OCaml 5 through dune/opam | opam package vinary-tree-interop |
bindings/ocaml/README.md |
| GHC through Cabal | Hackage package vinary-tree-interop (final-version candidate during RC) |
bindings/haskell/README.md |
| Lua 5.4+ | C adapter header bundled by dependent LuaRocks packages |
bindings/lua/README.md |
The adapters are intentionally policy-free. Concrete dictionary and automaton APIs live in their project packages; these guides explain only resource representation, discovery, ownership, and safe handoff.