use crate::cancellation::AgentCancellation;
use serde_json::Value;
use std::io::{self, Write};
pub(crate) fn serialized_size(
value: &Value,
limit: usize,
cancellation: &AgentCancellation,
) -> anyhow::Result<usize> {
struct Counter<'a> {
bytes: usize,
limit: usize,
cancellation: &'a AgentCancellation,
}
impl Write for Counter<'_> {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
if self.cancellation.is_canceled() {
return Err(io::Error::other("Code Mode canceled during serialization"));
}
self.bytes = self.bytes.saturating_add(bytes.len());
if self.bytes > self.limit {
return Err(io::Error::other("Code Mode result limit exceeded"));
}
Ok(bytes.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
let mut counter = Counter {
bytes: 0,
limit,
cancellation,
};
let serialized = serde_json::to_writer(&mut counter, value);
cancellation.check()?;
if counter.bytes > limit {
return Err(super::ResourceLimitExceeded("result").into());
}
serialized?;
Ok(counter.bytes)
}
pub(super) fn parse_reservation(
text: &str,
cancellation: &AgentCancellation,
) -> anyhow::Result<usize> {
let mut bytes = text.len().saturating_mul(2).saturating_add(4096);
let mut quoted = false;
let mut escaped = false;
for (index, byte) in text.bytes().enumerate() {
if index % 4096 == 0 {
cancellation.check()?;
}
if quoted {
if escaped {
escaped = false;
} else if byte == b'\\' {
escaped = true;
} else if byte == b'"' {
quoted = false;
}
} else if byte == b'"' {
quoted = true;
} else if matches!(byte, b'[' | b'{' | b',' | b':') {
bytes = bytes.saturating_add(256);
}
}
Ok(bytes)
}
pub(crate) fn value_reservation(
value: &Value,
cancellation: &AgentCancellation,
) -> anyhow::Result<usize> {
cancellation.check()?;
let mut bytes = 256usize;
match value {
Value::String(text) => bytes = bytes.saturating_add(text.len().saturating_mul(2)),
Value::Array(values) => {
for value in values {
bytes = bytes.saturating_add(value_reservation(value, cancellation)?);
}
}
Value::Object(values) => {
for (key, value) in values {
bytes = bytes
.saturating_add(key.len().saturating_mul(2))
.saturating_add(value_reservation(value, cancellation)?);
}
}
_ => {}
}
Ok(bytes)
}