repositories / smith
smith
There are many coding harnesses - but this one is fast
owned by admin
smith-core/src/content_codec.rs
Raw//! SMH-SPEC-SPEC0001: Sessions, typed entry content.
//!
//! Version 1 typed entry content. All integers are little endian, strings and
//! JSON spans have u32 byte lengths; JSON alone uses CBOR. No Rust layout is persisted.
use crate::cbor::{Encoder, Header, Write};
use crate::session::{COMPACTION_KIND, EntryContent, EntryContentRef, EntryKind};
use serde::ser::{SerializeSeq, SerializeStruct};
use serde::{Serialize, Serializer};
use smith::error::{Result, SmithError};
use smith::id::{MessageId, ToolCallId};
use smith::message::{ContentBlock, Message, Role};
use smith::structured::{AccessError, Kind, StructuredData, StructuredValue, ToolArguments};
fn invalid(reason: &str) -> SmithError {
SmithError::Session {
code: smith::error::SessionCode::EntryDecode,
message: reason.into(),
}
}
struct Cursor<'a> {
bytes: &'a [u8],
}
impl<'a> Cursor<'a> {
fn take(&mut self, count: usize) -> Result<&'a [u8]> {
let (head, rest) = self
.bytes
.split_at_checked(count)
.ok_or_else(|| invalid("truncated content span"))?;
self.bytes = rest;
Ok(head)
}
fn byte(&mut self) -> Result<u8> {
Ok(self.take(1)?[0])
}
fn u32(&mut self) -> Result<u32> {
Ok(u32::from_le_bytes(
self.take(4)?
.try_into()
.map_err(|_| invalid("truncated u32"))?,
))
}
fn i64(&mut self) -> Result<i64> {
Ok(i64::from_le_bytes(
self.take(8)?
.try_into()
.map_err(|_| invalid("truncated i64"))?,
))
}
fn id(&mut self) -> Result<u128> {
Ok(u128::from_le_bytes(
self.take(16)?
.try_into()
.map_err(|_| invalid("truncated ID"))?,
))
}
fn span(&mut self) -> Result<&'a [u8]> {
let n = self.u32()? as usize;
self.take(n)
}
fn text(&mut self) -> Result<&'a str> {
std::str::from_utf8(self.span()?).map_err(|_| invalid("invalid UTF-8"))
}
fn boolean(&mut self) -> Result<bool> {
match self.byte()? {
0 => Ok(false),
1 => Ok(true),
_ => Err(invalid("invalid boolean")),
}
}
fn json(&mut self) -> Result<JsonView<'a>> {
let bytes = self.span()?;
crate::json_scan::scan(bytes, None)?;
Ok(JsonView(bytes))
}
fn end(&self) -> Result<()> {
if self.bytes.is_empty() {
Ok(())
} else {
Err(invalid("trailing content bytes"))
}
}
}
fn put_span(out: &mut Vec<u8>, bytes: &[u8]) -> Result<()> {
let len = u32::try_from(bytes.len()).map_err(|_| invalid("content span exceeds u32"))?;
out.extend_from_slice(&len.to_le_bytes());
out.extend_from_slice(bytes);
Ok(())
}
#[derive(Clone, Copy, Debug)]
struct SpanLimit;
/// CBOR sink limited to one length-prefixed JSON span.
struct BoundedOutput<'a> {
out: &'a mut Vec<u8>,
start: usize,
max: usize,
}
impl<'a> BoundedOutput<'a> {
fn new(out: &'a mut Vec<u8>, max: usize) -> Self {
Self {
start: out.len(),
out,
max: max.min(u32::MAX as usize),
}
}
const fn len(&self) -> usize {
self.out.len() - self.start
}
}
impl Write for BoundedOutput<'_> {
type Error = SpanLimit;
fn write_all(&mut self, bytes: &[u8]) -> std::result::Result<(), Self::Error> {
let Some(len) = self.len().checked_add(bytes.len()) else {
return Err(SpanLimit);
};
if len > self.max {
return Err(SpanLimit);
}
self.out.extend_from_slice(bytes);
Ok(())
}
fn flush(&mut self) -> std::result::Result<(), Self::Error> {
Ok(())
}
}
fn put_json<'a, Value: StructuredValue<'a>>(out: &mut Vec<u8>, value: Value) -> Result<()> {
put_json_bounded(out, value, u32::MAX as usize)
}
fn put_json_bounded<'a, Value: StructuredValue<'a>>(
out: &mut Vec<u8>,
value: Value,
max: usize,
) -> Result<()> {
let start = out.len();
out.extend_from_slice(&[0; 4]);
let result = {
let sink = BoundedOutput::new(out, max);
let mut encoder = Encoder::from(sink);
encode_json(value, &mut encoder)
};
if result.is_err() {
out.truncate(start);
return Err(invalid("content span exceeds u32"));
}
let len = out.len() - start - 4;
assert!(
len <= max,
"bounded encoder wrote beyond its declared limit"
);
let Ok(len) = u32::try_from(len) else {
out.truncate(start);
return Err(invalid("content span exceeds u32"));
};
out[start..start + 4].copy_from_slice(&len.to_le_bytes());
Ok(())
}
fn encode_json<'a, Value: StructuredValue<'a>>(
value: Value,
encoder: &mut Encoder<BoundedOutput<'_>>,
) -> std::result::Result<(), SpanLimit> {
match value.kind().map_err(|_| SpanLimit)? {
Kind::Null => encoder.push(Header::Simple(22)),
Kind::Bool(value) => encoder.push(Header::Simple(if value { 21 } else { 20 })),
Kind::Signed(value) if value < 0 => {
encoder.push(Header::Negative((-1 - value).cast_unsigned()))
}
Kind::Signed(value) => encoder.push(Header::Positive(value.cast_unsigned())),
Kind::Unsigned(value) => encoder.push(Header::Positive(value)),
Kind::Float(value) => encoder.push(Header::Float(value)),
Kind::String(value) => encoder.text(value, None),
Kind::Array => {
let elements = value.elements().map_err(|_| SpanLimit)?.ok_or(SpanLimit)?;
encoder.push(Header::Array(Some(elements.len())))?;
for element in elements {
encode_json(element.map_err(|_| SpanLimit)?, encoder)?;
}
Ok(())
}
Kind::Object => {
let fields = value.fields().map_err(|_| SpanLimit)?.ok_or(SpanLimit)?;
encoder.push(Header::Map(Some(fields.len())))?;
for field in fields {
let (key, value) = field.map_err(|_| SpanLimit)?;
encoder.text(key, None)?;
encode_json(value, encoder)?;
}
Ok(())
}
}
}
pub(crate) fn encode(content: &EntryContentRef<'_>, out: &mut Vec<u8>) -> Result<()> {
match *content {
EntryContentRef::Message(message) => {
out.push(0);
out.extend_from_slice(&message.id.as_u128().to_le_bytes());
out.push(match message.role {
Role::User => 0,
Role::Assistant => 1,
Role::System => 2,
Role::Tool => 3,
});
out.extend_from_slice(&message.timestamp_ms.to_le_bytes());
out.extend_from_slice(
&u32::try_from(message.blocks.len())
.map_err(|_| invalid("too many blocks"))?
.to_le_bytes(),
);
for block in &message.blocks {
match block {
ContentBlock::Text(text) => {
out.push(0);
put_span(out, text.as_bytes())?;
}
ContentBlock::Thinking(text) => {
out.push(1);
put_span(out, text.as_bytes())?;
}
ContentBlock::ToolUse {
name,
input,
call_id,
} => {
out.push(2);
put_span(out, name.as_bytes())?;
put_json(out, input.view())?;
out.extend_from_slice(&call_id.as_u128().to_le_bytes());
}
ContentBlock::ToolResult {
call_id,
ok,
output,
} => {
out.push(3);
out.extend_from_slice(&call_id.as_u128().to_le_bytes());
out.push(u8::from(*ok));
put_span(out, output.as_bytes())?;
}
}
}
}
EntryContentRef::ToolCall {
name,
input,
call_id,
} => {
out.push(1);
put_span(out, name.as_bytes())?;
put_json(out, input.view())?;
out.extend_from_slice(&call_id.as_u128().to_le_bytes());
}
EntryContentRef::ToolResult {
ok,
output,
call_id,
} => {
out.push(2);
out.push(u8::from(ok));
put_span(out, output.as_bytes())?;
out.extend_from_slice(&call_id.as_u128().to_le_bytes());
}
EntryContentRef::Meta { kind, detail } => {
crate::metadata::validate(kind, detail.view())
.map_err(|_| invalid("invalid metadata"))?;
out.push(3);
put_span(out, kind.as_bytes())?;
put_json(out, detail.view())?;
}
}
Ok(())
}
/// Validated CBOR JSON span, decoded only at the consumer boundary.
#[derive(Clone, Copy, Debug)]
pub struct JsonView<'a>(pub(crate) &'a [u8]);
impl JsonView<'_> {
fn owned(self) -> Result<StructuredData> {
StructuredData::from_serializable(&self).map_err(|error| invalid(&error.to_string()))
}
/// Whether any JSON string value contains `pattern`.
///
/// # Panics
/// Only if the private validated-span invariant is broken.
#[must_use]
pub fn contains(self, pattern: &str) -> bool {
let found = crate::json_scan::scan(self.0, Some(pattern));
assert!(
found.is_ok(),
"validated JSON span remains canonical during string search"
);
found.is_ok_and(|found| found)
}
}
impl<'a> StructuredValue<'a> for JsonView<'a> {
type Elements = crate::json_scan::Elements<'a>;
type Fields = crate::json_scan::Fields<'a>;
fn kind(self) -> std::result::Result<Kind<'a>, AccessError> {
crate::json_scan::kind(self.0).map_err(|_| AccessError::Malformed)
}
fn elements(self) -> std::result::Result<Option<Self::Elements>, AccessError> {
crate::json_scan::elements(self.0).map_err(|_| AccessError::Malformed)
}
fn fields(self) -> std::result::Result<Option<Self::Fields>, AccessError> {
crate::json_scan::fields(self.0).map_err(|_| AccessError::Malformed)
}
}
impl Serialize for JsonView<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
crate::json_scan::serialize(self.0, serializer)
}
}
/// A validated block borrowing text directly from an entry content span.
#[derive(Clone, Copy, Debug)]
pub enum BlockView<'a> {
/// Plain text.
Text(&'a str),
/// Internal reasoning.
Thinking(&'a str),
/// Structured tool request.
ToolUse {
/// Tool name.
name: &'a str,
/// Structured input.
input: JsonView<'a>,
/// Pairing ID.
call_id: ToolCallId,
},
/// Tool output.
ToolResult {
/// Pairing ID.
call_id: ToolCallId,
/// Success flag.
ok: bool,
/// Recorded output.
output: &'a str,
},
}
impl Serialize for BlockView<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
#[derive(Serialize)]
struct Use<'a> {
name: &'a str,
input: JsonView<'a>,
call_id: ToolCallId,
}
#[derive(Serialize)]
struct Result<'a> {
call_id: ToolCallId,
ok: bool,
output: &'a str,
}
let mut outer = serializer.serialize_struct("ContentBlock", 2)?;
match *self {
Self::Text(t) => {
outer.serialize_field("type", "Text")?;
outer.serialize_field("data", t)?;
}
Self::Thinking(t) => {
outer.serialize_field("type", "Thinking")?;
outer.serialize_field("data", t)?;
}
Self::ToolUse {
name,
input,
call_id,
} => {
outer.serialize_field("type", "ToolUse")?;
outer.serialize_field(
"data",
&Use {
name,
input,
call_id,
},
)?;
}
Self::ToolResult {
call_id,
ok,
output,
} => {
outer.serialize_field("type", "ToolResult")?;
outer.serialize_field(
"data",
&Result {
call_id,
ok,
output,
},
)?;
}
}
outer.end()
}
}
fn block<'a>(cursor: &mut Cursor<'a>) -> Result<BlockView<'a>> {
Ok(match cursor.byte()? {
0 => BlockView::Text(cursor.text()?),
1 => BlockView::Thinking(cursor.text()?),
2 => BlockView::ToolUse {
name: cursor.text()?,
input: cursor.json()?,
call_id: ToolCallId::from_u128(cursor.id()?),
},
3 => BlockView::ToolResult {
call_id: ToolCallId::from_u128(cursor.id()?),
ok: cursor.boolean()?,
output: cursor.text()?,
},
_ => return Err(invalid("invalid block tag")),
})
}
/// Iterator over blocks in a previously validated message span.
#[derive(Clone, Debug)]
pub struct Blocks<'a> {
cursor: &'a [u8],
left: u32,
}
impl<'a> Iterator for Blocks<'a> {
type Item = BlockView<'a>;
#[expect(
clippy::expect_used,
reason = "Blocks only holds spans fully traversed by decode"
)]
fn next(&mut self) -> Option<Self::Item> {
if self.left == 0 {
return None;
}
let mut cursor = Cursor { bytes: self.cursor };
let block = block(&mut cursor).expect("validated block span");
self.cursor = cursor.bytes;
self.left -= 1;
Some(block)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.left as usize, Some(self.left as usize))
}
}
impl ExactSizeIterator for Blocks<'_> {}
/// Validated message metadata with zero-copy block iteration.
#[derive(Clone, Debug)]
pub struct MessageView<'a> {
/// Stable message identifier.
pub id: MessageId,
/// Sender role.
pub role: Role,
/// Timestamp in Unix milliseconds.
pub timestamp_ms: i64,
blocks: Blocks<'a>,
}
impl<'a> MessageView<'a> {
/// Iterate message blocks without allocating.
pub fn blocks(&self) -> Blocks<'a> {
self.blocks.clone()
}
}
impl Serialize for MessageView<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
struct Sequence<'a>(Blocks<'a>);
impl Serialize for Sequence<'_> {
fn serialize<S: Serializer>(
&self,
serializer: S,
) -> std::result::Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
for block in self.0.clone() {
seq.serialize_element(&block)?;
}
seq.end()
}
}
let mut out = serializer.serialize_struct("Message", 4)?;
out.serialize_field("id", &self.id)?;
out.serialize_field("role", &self.role)?;
out.serialize_field("blocks", &Sequence(self.blocks()))?;
out.serialize_field("timestamp_ms", &self.timestamp_ms)?;
out.end()
}
}
/// Content decoded from bytes, distinct from the recording-time borrowed Rust view.
#[derive(Clone, Debug)]
pub enum EntryContentView<'a> {
/// Message with block spans.
Message(MessageView<'a>),
/// Tool invocation.
ToolCall {
/// Tool name.
name: &'a str,
/// Structured input.
input: JsonView<'a>,
/// Pairing ID.
call_id: ToolCallId,
},
/// Tool completion.
ToolResult {
/// Success flag.
ok: bool,
/// Recorded output.
output: &'a str,
/// Pairing ID.
call_id: ToolCallId,
},
/// Metadata event.
Meta {
/// Metadata kind.
kind: &'a str,
/// Structured detail.
detail: JsonView<'a>,
},
}
impl<'a> EntryContentView<'a> {
/// Validate every tag, length, nested value and trailing byte before returning a view.
///
/// # Errors
/// Returns `ENTRY_DECODE` for malformed content.
pub fn decode(bytes: &'a [u8]) -> Result<Self> {
let mut cursor = Cursor { bytes };
let value = match cursor.byte()? {
0 => {
let id = MessageId::from_u128(cursor.id()?);
let role = match cursor.byte()? {
0 => Role::User,
1 => Role::Assistant,
2 => Role::System,
3 => Role::Tool,
_ => return Err(invalid("invalid message role")),
};
let timestamp_ms = cursor.i64()?;
let count = cursor.u32()?;
let blocks = Blocks {
cursor: cursor.bytes,
left: count,
};
for _ in 0..count {
block(&mut cursor)?;
}
Self::Message(MessageView {
id,
role,
timestamp_ms,
blocks,
})
}
1 => Self::ToolCall {
name: cursor.text()?,
input: cursor.json()?,
call_id: ToolCallId::from_u128(cursor.id()?),
},
2 => Self::ToolResult {
ok: cursor.boolean()?,
output: cursor.text()?,
call_id: ToolCallId::from_u128(cursor.id()?),
},
3 => {
let kind = cursor.text()?;
let detail = cursor.json()?;
crate::metadata::validate(kind, detail).map_err(|_| invalid("invalid metadata"))?;
Self::Meta { kind, detail }
}
_ => return Err(invalid("invalid content tag")),
};
cursor.end()?;
Ok(value)
}
/// Traversal metadata derived from validated content.
#[must_use]
pub fn kind(&self) -> EntryKind {
match self {
Self::Message(m) => EntryKind::Message {
id: m.id,
role: m.role,
},
Self::ToolCall { call_id, .. } => EntryKind::ToolCall { call_id: *call_id },
Self::ToolResult { call_id, ok, .. } => EntryKind::ToolResult {
call_id: *call_id,
ok: *ok,
},
Self::Meta { kind, .. } => EntryKind::Meta {
compaction: *kind == COMPACTION_KIND,
},
}
}
/// Match against recorded string values (not keys).
#[must_use]
pub fn contains(&self, pattern: &str) -> bool {
match self {
Self::Message(m) => m.blocks().any(|b| match b {
BlockView::Text(s) | BlockView::Thinking(s) => s.contains(pattern),
BlockView::ToolUse { name, input, .. } => {
name.contains(pattern) || input.contains(pattern)
}
BlockView::ToolResult { output, .. } => output.contains(pattern),
}),
Self::ToolCall { name, input, .. } => name.contains(pattern) || input.contains(pattern),
Self::ToolResult { output, .. } => output.contains(pattern),
Self::Meta { kind, detail } => kind.contains(pattern) || detail.contains(pattern),
}
}
/// Materialize content for agent context, replay or API consumers.
///
/// # Errors
/// Returns `ENTRY_DECODE` if structured data cannot be materialized.
pub fn to_owned(&self) -> Result<EntryContent> {
Ok(match self {
Self::Message(m) => {
let mut blocks = Vec::with_capacity(m.blocks.len());
for block in m.blocks() {
blocks.push(match block {
BlockView::Text(text) => ContentBlock::Text(text.to_owned()),
BlockView::Thinking(text) => ContentBlock::Thinking(text.to_owned()),
BlockView::ToolUse {
name,
input,
call_id,
} => ContentBlock::ToolUse {
name: name.to_owned(),
input: ToolArguments::from_data(input.owned()?),
call_id,
},
BlockView::ToolResult {
call_id,
ok,
output,
} => ContentBlock::ToolResult {
call_id,
ok,
output: output.to_owned(),
},
});
}
EntryContent::Message(Message {
id: m.id,
role: m.role,
timestamp_ms: m.timestamp_ms,
blocks,
})
}
Self::ToolCall {
name,
input,
call_id,
} => EntryContent::ToolCall {
name: (*name).to_owned(),
input: ToolArguments::from_data(input.owned()?),
call_id: *call_id,
},
Self::ToolResult {
ok,
output,
call_id,
} => EntryContent::ToolResult {
ok: *ok,
output: (*output).to_owned(),
call_id: *call_id,
},
Self::Meta { kind, detail } => EntryContent::Meta {
kind: (*kind).to_owned(),
detail: detail.owned()?,
},
})
}
}
impl Serialize for EntryContentView<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
#[derive(Serialize)]
struct Invocation<'a> {
name: &'a str,
input: JsonView<'a>,
call_id: ToolCallId,
}
#[derive(Serialize)]
struct Result<'a> {
ok: bool,
output: &'a str,
call_id: ToolCallId,
}
#[derive(Serialize)]
struct Meta<'a> {
kind: &'a str,
detail: JsonView<'a>,
}
let mut out = serializer.serialize_struct("EntryContent", 2)?;
match self {
Self::Message(m) => {
out.serialize_field("kind", "Message")?;
out.serialize_field("data", m)?;
}
Self::ToolCall {
name,
input,
call_id,
} => {
out.serialize_field("kind", "ToolCall")?;
out.serialize_field(
"data",
&Invocation {
name,
input: *input,
call_id: *call_id,
},
)?;
}
Self::ToolResult {
ok,
output,
call_id,
} => {
out.serialize_field("kind", "ToolResult")?;
out.serialize_field(
"data",
&Result {
ok: *ok,
output,
call_id: *call_id,
},
)?;
}
Self::Meta { kind, detail } => {
out.serialize_field("kind", "Meta")?;
out.serialize_field(
"data",
&Meta {
kind,
detail: *detail,
},
)?;
}
}
out.end()
}
}
#[cfg(test)]
mod tests {
//! SMH-SPEC-SPEC0001: Sessions, typed content and bounded access.
use super::*;
use crate::session::EntryFrame;
use proptest::prelude::*;
use serde::Serialize;
use smith_alloc::{Counting, Scope, measure};
use std::sync::Mutex;
#[global_allocator]
static ALLOC: Counting = Counting;
static MEASURING: Mutex<()> = Mutex::new(());
#[test]
fn materialized_message_reserves_exact_validated_block_count() {
let mut message = Message::with_text(Role::User, "0");
for block in 1..7 {
message.add_block(ContentBlock::Text(block.to_string()));
}
let mut bytes = Vec::new();
encode(&EntryContentRef::Message(&message), &mut bytes).unwrap();
let EntryContent::Message(owned) = EntryContentView::decode(&bytes)
.unwrap()
.to_owned()
.unwrap()
else {
panic!("message kind must survive materialization");
};
assert_eq!(owned, message);
assert_eq!(owned.blocks.len(), 7);
assert_eq!(owned.blocks.capacity(), 7);
}
#[test]
fn borrowed_arguments_validate_and_traverse_without_allocations() {
let arguments = ToolArguments::from_json(
r#"{"path":"notes.txt","limit":18446744073709551615,"items":[null,true,-0.0]}"#,
)
.unwrap()
.canonicalized(64)
.unwrap();
let schema = smith::ToolSchema::object(
&[("path", "string"), ("limit", "integer"), ("items", "array")],
&["path"],
);
let mut bytes = Vec::new();
encode(
&EntryContentRef::ToolCall {
name: "read",
input: &arguments,
call_id: ToolCallId::from_u128(1),
},
&mut bytes,
)
.unwrap();
let EntryContentView::ToolCall { input, .. } = EntryContentView::decode(&bytes).unwrap()
else {
panic!("tool call must retain argument view");
};
let _serial = MEASURING
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let ((), cost) = measure(Scope::Session, || {
schema.validate(input).unwrap();
assert_eq!(input.fields().unwrap().unwrap().len(), 3);
let Kind::String(path) = input.field("path").unwrap().unwrap().kind().unwrap() else {
panic!("path must remain a string");
};
assert_eq!(path, "notes.txt");
let resident = (bytes.as_ptr() as usize)..(bytes.as_ptr() as usize + bytes.len());
assert!(resident.contains(&(path.as_ptr() as usize)));
assert_eq!(
input.field("limit").unwrap().unwrap().kind().unwrap(),
Kind::Unsigned(u64::MAX)
);
let mut items = input
.field("items")
.unwrap()
.unwrap()
.elements()
.unwrap()
.unwrap();
assert_eq!(items.len(), 3);
assert_eq!(items.next().unwrap().unwrap().kind().unwrap(), Kind::Null);
assert_eq!(items.len(), 2);
assert_eq!(
items.next().unwrap().unwrap().kind().unwrap(),
Kind::Bool(true)
);
let Kind::Float(zero) = items.next().unwrap().unwrap().kind().unwrap() else {
panic!("negative zero must remain a float");
};
assert_eq!(zero.to_bits(), (-0.0_f64).to_bits());
assert_eq!(items.len(), 0);
assert!(items.next().is_none());
});
assert_eq!(cost.allocations, 0);
assert_eq!(cost.bytes, 0);
assert_eq!(cost.peak, 0);
assert_eq!(std::mem::size_of::<JsonView<'_>>(), 16);
assert_eq!(std::mem::size_of::<crate::json_scan::Elements<'_>>(), 24);
assert_eq!(std::mem::size_of::<crate::json_scan::Fields<'_>>(), 40);
}
#[test]
fn json_encoding_uses_existing_output_capacity() {
let value =
StructuredData::from_serializable(&serde_json::json!({"b": [1, 2, 3], "aa": "value"}))
.unwrap();
let mut bytes = Vec::with_capacity(128);
let _serial = MEASURING
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let (result, cost) = measure(Scope::Session, || put_json(&mut bytes, value.view()));
result.unwrap();
assert_eq!(cost.allocations, 0);
assert_eq!(cost.bytes, 0);
}
#[test]
fn bounded_output_writes_short_and_exact_spans() {
let mut bytes = vec![9];
{
let mut sink = BoundedOutput::new(&mut bytes, 3);
sink.write_all(&[1]).unwrap();
sink.write_all(&[2, 3]).unwrap();
assert_eq!(sink.len(), 3);
}
assert_eq!(bytes, [9, 1, 2, 3]);
}
#[test]
fn bounded_output_rejects_over_limit_without_appending() {
let mut bytes = vec![9];
{
let mut sink = BoundedOutput::new(&mut bytes, 2);
assert!(sink.write_all(&[1, 2, 3]).is_err());
assert_eq!(sink.len(), 0);
}
assert_eq!(bytes, [9]);
}
#[test]
fn json_encoding_rolls_back_on_span_limit() {
let mut bytes = vec![9];
let value = StructuredData::from_json("\"x\"").unwrap();
assert!(put_json_bounded(&mut bytes, value.view(), 1).is_err());
assert_eq!(bytes, [9]);
}
#[test]
fn structured_value_wire_bytes_match_v4_golden_contract() {
let value = StructuredData::from_serializable(&serde_json::json!([
-1,
0,
23,
24,
255,
256,
65_535,
65_536,
i64::MIN,
u64::MAX,
-0.0,
1.5,
1.1,
{"b": 1, "aa": 2},
]))
.unwrap();
let mut bytes = Vec::new();
put_json(&mut bytes, value.view()).unwrap();
assert_eq!(
bytes,
[
60, 0, 0, 0, 0x8e, 0x20, 0x00, 0x17, 0x18, 0x18, 0x18, 0xff, 0x19, 0x01, 0x00,
0x19, 0xff, 0xff, 0x1a, 0x00, 0x01, 0x00, 0x00, 0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf9, 0x80,
0x00, 0xf9, 0x3e, 0x00, 0xfb, 0x3f, 0xf1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0xa2,
0x61, b'b', 0x01, 0x62, b'a', b'a', 0x02,
]
);
}
proptest! {
#[test]
fn encoded_json_scalars_round_trip_with_numeric_kind(value in prop_oneof![
any::<bool>().prop_map(serde_json::Value::Bool),
any::<i64>().prop_map(serde_json::Value::from),
any::<u64>().prop_map(serde_json::Value::from),
any::<f64>().prop_filter_map("finite JSON number", serde_json::Number::from_f64)
.prop_map(serde_json::Value::Number),
".{0,64}".prop_map(serde_json::Value::String),
]) {
let mut bytes = Vec::new();
let expected = value.clone();
let value = StructuredData::from_serializable(&value).unwrap();
put_json(&mut bytes, value.view()).unwrap();
let encoded = &bytes[4..];
prop_assert!(crate::json_scan::scan(encoded, None).is_ok());
prop_assert_eq!(
crate::json_scan::serialize(encoded, serde_json::value::Serializer).unwrap(),
expected,
);
}
}
#[test]
fn rejects_duplicate_json_keys_before_search_or_materialization() {
let mut bytes = vec![3, 1, 0, 0, 0, b'k', 7, 0, 0, 0];
bytes.extend_from_slice(&[0xa2, 0x61, b'a', 1, 0x61, b'a', 2]);
assert_eq!(
EntryContentView::decode(&bytes).unwrap_err().code(),
"ENTRY_DECODE"
);
}
#[test]
fn borrowed_known_metadata_validation_allocates_nothing() {
let tool = crate::metadata::encode(&crate::metadata::ToolMutationMetadata::new(
ToolCallId::from_u128(1),
smith::error::MutationState::Applied,
))
.unwrap();
let compaction = crate::metadata::encode(&crate::metadata::CompactionMetadata {
summary: "summary".to_string(),
complete: Some(true),
})
.unwrap();
let provider = StructuredData::from_json(
r#"{"phase":"outcome","status":0,"response_bytes":0,"outcome":"completed"}"#,
)
.unwrap();
for (kind, detail) in [
(COMPACTION_KIND, compaction),
(crate::session::TOOL_MUTATION_KIND, tool),
(crate::session::PROVIDER_HTTP_KIND, provider),
] {
let bytes = EntryFrame::new(
None,
&EntryContent::Meta {
kind: kind.to_string(),
detail,
},
)
.unwrap()
.content;
let EntryContentView::Meta { kind, detail } = EntryContentView::decode(&bytes).unwrap()
else {
panic!("metadata must retain a borrowed view");
};
let _serial = MEASURING
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let (result, cost) =
measure(Scope::Session, || crate::metadata::validate(kind, detail));
assert!(result.is_ok());
assert_eq!(cost.allocations, 0, "{kind} validation allocated");
assert_eq!(cost.bytes, 0, "{kind} validation allocated bytes");
}
}
#[test]
fn reserved_metadata_rejects_malformed_owned_and_borrowed_shapes() {
let malformed = StructuredData::from_json(r#"{"summary":false}"#).unwrap();
assert!(
EntryFrame::new(
None,
&EntryContent::Meta {
kind: COMPACTION_KIND.to_string(),
detail: malformed,
},
)
.is_err()
);
let bytes = [
3, 10, 0, 0, 0, b'c', b'o', b'm', b'p', b'a', b'c', b't', b'i', b'o', b'n', 10, 0, 0,
0, 0xa1, 0x67, b's', b'u', b'm', b'm', b'a', b'r', b'y', 0xf4,
];
assert!(EntryContentView::decode(&bytes).is_err());
}
#[test]
fn json_view_serialization_streams_without_materializing_json() {
let detail = StructuredData::from_serializable(&serde_json::Value::Array(vec![
serde_json::Value::from(-1),
serde_json::Value::from(u64::MAX),
serde_json::Value::Number(serde_json::Number::from_f64(-0.0).unwrap()),
serde_json::Value::Number(serde_json::Number::from_f64(1.1).unwrap()),
]))
.unwrap();
let expected = serde_json::to_value(&detail).unwrap();
let bytes = EntryFrame::new(
None,
&EntryContent::Meta {
kind: "event".into(),
detail,
},
)
.unwrap()
.content;
let EntryContentView::Meta { detail, .. } = EntryContentView::decode(&bytes).unwrap()
else {
unreachable!();
};
let mut output = Vec::with_capacity(128);
let _serial = MEASURING
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let (result, cost) = measure(Scope::Session, || {
let mut serializer = serde_json::Serializer::new(&mut output);
detail.serialize(&mut serializer)
});
result.unwrap();
assert_eq!(
cost.allocations, 0,
"streaming JSON allocated {} times",
cost.allocations
);
let values: serde_json::Value = serde_json::from_slice(&output).unwrap();
let values = values.as_array().unwrap();
assert_eq!(values[0].as_i64(), Some(-1));
assert_eq!(values[1].as_u64(), Some(u64::MAX));
assert!(values[2].as_f64().unwrap().is_sign_negative());
assert_eq!(values[3].as_f64(), Some(1.1));
let EntryContent::Meta { detail, .. } = EntryContentView::decode(&bytes)
.unwrap()
.to_owned()
.unwrap()
else {
unreachable!();
};
let detail = serde_json::to_value(&detail).unwrap();
assert_eq!(detail[0].as_i64(), Some(-1));
assert_eq!(detail[1].as_u64(), Some(u64::MAX));
assert!(detail[2].as_f64().unwrap().is_sign_negative());
assert_eq!(detail, expected);
}
}