Skip to main content

Crate ontomark_ngg1

Crate ontomark_ngg1 

Source
Expand description

ontomark-ngg1: the NGG1 binary graph-tier format, in one place.

NGG1 is the compact, memory-mappable graph encoding that a publishing pipeline ships to a browser explorer: a node table, a CSR adjacency, one type byte per edge and a string table, all little-endian. This crate owns the format end to end: the constants, the record types, the encoder and the zero-copy Ngg1 reader. It has no dependencies, uses only std and contains no unsafe code.

§Provenance

  • The reader, the NGG1_* constants and worked_example come from vowl-wasm 0.1.2 (src/ngg1.rs, MIT, Copyright (c) 2025-2026 DreamLab-AI). Every public name and signature of that module is kept, so vowl-wasm can replace it with pub use ontomark_ngg1::*;.
  • The encoder, Csr, build_csr and the short constant names (MAGIC, VERSION, …) come from VisionClaw’s crates/vault/src/build/ngg1.rs at 1044b0722, via ontomark-compile.

See the NOTICE file shipped with the crate.

§Byte layout

All integers and floats are little-endian. Every section starts on a 4-byte boundary, so a consumer may view row_ptr, col_idx and the string offsets as u32 arrays over a 4-aligned buffer. N is the node count, E the edge count and S = 2 * N the string count.

SectionStarts atSize (bytes)Content
header032see the header table below
nodesoff_nodes (32)24 * NN node records
adjacencyoff_adjacency4 * (N + 1) + 4 * Erow_ptr[N + 1]: u32, then col_idx[E]: u32
edge typesoff_edge_typesE plus 0 to 3 zero bytesedge_type[E]: u8, padded to a multiple of 4
stringsoff_strings8 + 4 * S + blob_lencount: u32, blob_len: u32, offsets[S]: u32, blob

The offsets are off_nodes = 32, off_adjacency = 32 + 24 * N, off_edge_types = off_adjacency + 4 * (N + 1) + 4 * E and off_strings = off_edge_types + E + pad4(E), where pad4(E) is (4 - E % 4) % 4. The file ends with the last byte of the blob.

Header (32 bytes):

OffsetTypeField
0[u8; 4]magic, ASCII NGG1 (0x3147_474E as a little-endian u32)
4u16version, currently 1
6u16padding, zero
8u32node count N
12u32edge count E
16u32off_nodes
20u32off_adjacency
24u32off_edge_types
28u32off_strings

Node record (24-byte stride; the original brief’s “20” is arithmetically impossible once degree is 4-aligned):

OffsetTypeField
0u32stable external id, shared across tiers
4f32seed x position
8f32seed y position
12u16domain id, or DOMAIN_NONE
14u16category id, or CATEGORY_NONE
16u8flags (FLAG_*)
17[u8; 3]padding, zero
20u32degree in the full graph (ranking key)

Strings: the blob is the concatenation of every string’s UTF-8 bytes with no separators. offsets[s] is the start of string s; string s ends at offsets[s + 1], and the last string ends at blob_len. Node i owns strings 2 * i (label) and 2 * i + 1 (IRI).

Domains and categories are only identifiers here: how many exist, and what they are called, is a property of the producing taxonomy, not of the format. A graph with no domains or no categories is valid.

§Encoding and decoding

use ontomark_ngg1::{build_csr, encode, GraphNode, Ngg1, CATEGORY_NONE, EDGE_SUBCLASS};

let node = |id: u32, label: &str| GraphNode {
    id,
    x: id as f32,
    y: 0.0,
    domain: 0,
    category: CATEGORY_NONE,
    flags: 0,
    degree: 1,
    label: label.to_owned(),
    iri: format!("https://example.org/{label}"),
};
let nodes = vec![node(0, "Animal"), node(1, "Dog")];
let csr = build_csr(2, &[(1, 0, EDGE_SUBCLASS)]);
let bytes = encode(&nodes, &csr)?;

let graph = Ngg1::parse(&bytes)?;
assert_eq!((graph.node_count(), graph.edge_count()), (2, 1));
assert_eq!(graph.label(1), "Dog");
assert_eq!(graph.edges(), (vec![1], vec![0], vec![EDGE_SUBCLASS]));

§Malformed input

Ngg1::try_parse validates the whole buffer once (magic, version, alignment, every section against the buffer length, CSR monotonicity, edge targets, string offsets) and returns a typed Ngg1Fault. Every accessor on a successfully parsed reader is then panic-free for in-range indices.

Structs§

Csr
Compressed sparse row adjacency.
Graph
A decoded tier: nodes plus adjacency. Produced by Ngg1::to_graph and written by Graph::encode.
GraphNode
One graph node as stored in a tier, in local-index order.
Ngg1
A parsed, borrowed NGG1 tier.
Ngg1Error
Parse error for an NGG1 buffer. Displays a stable, greppable reason.
NodeRecord
A single decoded node record. All fields are copied out; the record carries no borrow.

Enums§

EncodeError
Why encode refused its input.
Ngg1Fault
The typed reason an NGG1 buffer was rejected.

Constants§

CATEGORY_NONE
Category id for an uncategorised node.
DOMAIN_NONE
Domain id for a node with no resolvable domain.
EDGE_RELATION
edge_type value for an objectProperty relation.
EDGE_SUBCLASS
edge_type value for a subClassOf edge, the taxonomy backbone.
FLAG_BRIDGE
Node flag: the node ships at least one cross-domain bridges-to edge.
FLAG_CATEGORY_ROOT
Node flag: the node is a taxonomy category root.
FLAG_DOMAIN_ROOT
Node flag: the node is a declared domain root.
FLAG_HAS_PAGE
Node flag: the node has an authored page behind it.
FLAG_INDIVIDUAL
Node flag: the node is an OWL individual, not a class.
FLAG_META
Node flag: a client-minted collapsed-category metanode. Never written by a producer; the TypeScript explorer sets it on synthetic nodes.
HEADER_SIZE
Header length in bytes; the same value as NGG1_HEADER_SIZE.
MAGIC
The magic as the four bytes at the start of every file, NGG1.
NGG1_HEADER_SIZE
Fixed header size in bytes.
NGG1_MAGIC
NGG1 as a little-endian u32: 0x3147_474E (bytes 4E 47 47 31).
NGG1_NODE_STRIDE
Node record stride in bytes. 24, not 20.
NGG1_VERSION
Format version this crate reads and writes.
NODE_STRIDE
Node record stride in bytes; the same value as NGG1_NODE_STRIDE.
VERSION
Format version; the same value as NGG1_VERSION.

Functions§

build_csr
Build CSR from directed (src_local, tgt_local, edge_type) triples.
encode
Serialise one tier to NGG1 bytes.
worked_example
The FORMAT-NGG1 section 7 worked example, byte-exact: 3 nodes, 2 edges, 183 bytes. It is the Rust twin of the ngg1-3n2e.bin golden fixtures, and makes a handy known-good buffer.