use std::time::Instant;
use verit::{dump_json, encode, Dt, Message, Resolver, SchemaBuilder, SchemaMode, Value};
fn main() {
let schema_v1 = SchemaBuilder::new()
.add_enum("Level", vec![(0, "Debug"), (1, "Info"), (2, "Error")])
.add_struct(
"LogEvent",
vec![
(1, "service", Dt::Str),
(2, "level", Dt::named("Level")),
(3, "message", Dt::Str),
(4, "latency_us", Dt::U32),
],
)
.build("LogEvent")
.unwrap();
let event = Value::Struct(vec![
(1, Value::str("checkout")),
(2, Value::Enum(2)),
(3, Value::str("payment gateway timed out")),
(4, Value::U32(2_500_000)),
]);
let inline = encode(&schema_v1, &event, SchemaMode::Inline).unwrap();
let hash_only = encode(&schema_v1, &event, SchemaMode::HashOnly).unwrap();
println!("=== Veritate: zero-copy + self-describing + schema-evolvable ===\n");
println!(
"message size: {} bytes self-contained, {} bytes with schema amortized \
(schema id {:#034x} travels in every message)",
inline.len(),
hash_only.len(),
schema_v1.id()
);
println!("\n[1] self-describing: dump_json(bytes) with no schema object in scope:");
println!(" {}", dump_json(&inline).unwrap());
let resolver = Resolver::identity(&schema_v1).unwrap();
let msg = Message::parse(&hash_only).unwrap();
let root = msg.root(&resolver).unwrap();
let service = root.get_str(1).unwrap().unwrap();
let buf_range = hash_only.as_ptr() as usize..hash_only.as_ptr() as usize + hash_only.len();
println!(
"\n[2] zero-copy: get_str returned &str at {:p},",
service.as_ptr()
);
println!(
" which lies inside the message buffer {:p}..{:#x} — no copy, no allocation",
hash_only.as_ptr(),
buf_range.end
);
let iterations = 10_000_000u32;
let start = Instant::now();
let mut acc = 0u64;
for _ in 0..iterations {
acc = acc.wrapping_add(std::hint::black_box(
root.get_u32(4).unwrap().unwrap() as u64
));
}
let elapsed = start.elapsed();
println!(
" {} field reads in {:.1?} ({:.2} ns/read, acc {})",
iterations,
elapsed,
elapsed.as_nanos() as f64 / iterations as f64,
acc
);
let schema_v2 = SchemaBuilder::new()
.add_enum("Level", vec![(0, "Debug"), (1, "Info"), (2, "Error")])
.add_struct(
"LogEvent",
vec![
(1, "service_name", Dt::Str), (2, "level", Dt::named("Level")),
(3, "message", Dt::Str),
(4, "latency_us", Dt::U64), (5, "trace_id", Dt::Str), ],
)
.build("LogEvent")
.unwrap();
println!("\n[3] evolution: v2 renames field 1, widens field 4 to u64, adds field 5");
let up = Resolver::new(&schema_v1, &schema_v2).unwrap(); let root2 = msg.root(&up).unwrap();
println!(
" v2 reader on v1 data: service_name={:?}, latency_us(u64)={:?}, trace_id={:?}",
root2.get_str(1).unwrap(),
root2.get_u64(4).unwrap(),
root2.get_str(5).unwrap()
);
let event_v2 = Value::Struct(vec![
(1, Value::str("checkout")),
(4, Value::U64(1_200)),
(5, Value::str("abc123")),
]);
let bytes_v2 = encode(&schema_v2, &event_v2, SchemaMode::HashOnly).unwrap();
let down = Resolver::new(&schema_v2, &schema_v1);
match down {
Err(e) => println!(
" v1 reader on v2 data: refused at plan time ({e}) — narrowing never \
corrupts silently",
),
Ok(r) => {
let m2 = Message::parse(&bytes_v2).unwrap();
let r2 = m2.root(&r).unwrap();
println!(
" v1 reader on v2 data: service={:?}",
r2.get_str(1).unwrap()
);
}
}
let schema_v2_compat = SchemaBuilder::new()
.add_enum("Level", vec![(0, "Debug"), (1, "Info"), (2, "Error")])
.add_struct(
"LogEvent",
vec![
(1, "service_name", Dt::Str),
(2, "level", Dt::named("Level")),
(3, "message", Dt::Str),
(4, "latency_us", Dt::U32),
(5, "trace_id", Dt::Str),
],
)
.build("LogEvent")
.unwrap();
let event_c = Value::Struct(vec![
(1, Value::str("checkout")),
(4, Value::U32(1_200)),
(5, Value::str("abc123")),
]);
let bytes_c = encode(&schema_v2_compat, &event_c, SchemaMode::HashOnly).unwrap();
let back = Resolver::new(&schema_v2_compat, &schema_v1).unwrap();
let mc = Message::parse(&bytes_c).unwrap();
let rc = mc.root(&back).unwrap();
println!(
" v1 reader on add-only v2 data: service={:?}, latency_us={:?} (unknown \
field 5 costs nothing — its bytes are never visited)",
rc.get_str(1).unwrap(),
rc.get_u32(4).unwrap()
);
}