verit 0.2.0

Exavian Veritate — zero-copy, self-describing, schema-evolvable binary serialization, safe on untrusted bytes, no unsafe, byte-identical across independent implementations.
Documentation
//! The `.vertc` container over real Veritate messages: many messages packed
//! into one at-rest image, then read back zero-copy (the `mmap`-in-place path).
//! Each record is handed straight to `Message::parse` out of the container's own
//! bytes — no per-message copy.
//!
//! The container is superseded by the `.verit` file (see `file.rs` and
//! ADR-0002) and removed in 0.3.0. These tests keep it honest until then.
#![allow(deprecated)]

use verit::{
    container::{Container, ContainerWriter},
    dump_json, encode, Dt, Message, Resolver, SchemaBuilder, SchemaMode, Value,
};

fn point_schema() -> verit::Schema {
    SchemaBuilder::new()
        .add_dense_struct("Point", vec![(1, "x", Dt::F64), (2, "y", Dt::F64)])
        .build("Point")
        .unwrap()
}

#[test]
fn container_holds_many_messages_read_zero_copy() {
    let schema = point_schema();
    // Build a container of 50 distinct Point messages (inline schema so each is
    // self-describing and `dump_json`-able straight from the container bytes).
    let mut w = ContainerWriter::new();
    let mut expected = Vec::new();
    for i in 0..50u32 {
        let (x, y) = (i as f64 * 1.5, -(i as f64));
        let bytes = encode(
            &schema,
            &Value::Struct(vec![(1, Value::F64(x)), (2, Value::F64(y))]),
            SchemaMode::Inline,
        )
        .unwrap();
        w.add(&bytes);
        expected.push((x, y));
    }
    let file = w.finish();

    // Reopen from the raw image (as if `mmap`-ed): validate once, then random
    // access. The slices `get` returns borrow `file` — no copy.
    let container = Container::parse(&file).unwrap();
    assert_eq!(container.len(), 50);

    let resolver = Resolver::identity(&schema).unwrap();
    for i in [0usize, 7, 23, 49] {
        let record: &[u8] = container.get(i).unwrap();
        // The record slice lives inside the container image.
        let base = file.as_ptr() as usize;
        let rec = record.as_ptr() as usize;
        assert!(
            rec >= base && rec < base + file.len(),
            "record borrows image"
        );

        let msg = Message::parse(record).unwrap();
        let root = msg.root(&resolver).unwrap();
        let (x, y) = expected[i];
        assert_eq!(root.get_f64(1).unwrap(), Some(x));
        assert_eq!(root.get_f64(2).unwrap(), Some(y));
    }

    // Self-describing: dump the last record with only its bytes.
    let json = dump_json(container.get(49).unwrap()).unwrap();
    assert_eq!(json, r#"{"x":73.5,"y":-49}"#);
}

#[test]
fn container_records_start_eight_aligned_for_mmap() {
    // Messages of varied length so padding is exercised; every record offset
    // must be 8-aligned so a page-aligned map keeps message internals aligned.
    let schema = point_schema();
    let mut w = ContainerWriter::new();
    for i in 0..8u32 {
        let bytes = encode(
            &schema,
            &Value::Struct(vec![(1, Value::F64(i as f64)), (2, Value::F64(0.0))]),
            if i % 2 == 0 {
                SchemaMode::Inline
            } else {
                SchemaMode::HashOnly
            },
        )
        .unwrap();
        w.add(&bytes);
    }
    let file = w.finish();
    let container = Container::parse(&file).unwrap();
    for i in 0..container.len() {
        let rec = container.get(i).unwrap();
        let offset = rec.as_ptr() as usize - file.as_ptr() as usize;
        assert_eq!(offset % 8, 0, "record {i} offset {offset} not 8-aligned");
    }
}