pub struct MemoryBounds;
impl MemoryBounds {
pub const fn stack_buffer_size(capacity: usize) -> usize {
capacity + core::mem::size_of::<usize>() }
pub const fn borrowed_parse_estimate(
num_nodes: usize,
num_containers: usize,
avg_container_size: usize,
) -> usize {
let node_overhead = num_nodes * 24;
let container_overhead = num_containers * avg_container_size * 16;
node_overhead + container_overhead
}
pub const fn max_safe_depth(stack_bytes: usize, bytes_per_frame: usize) -> usize {
if bytes_per_frame == 0 {
return 0;
}
(stack_bytes / 2) / bytes_per_frame
}
}
pub type FixedSizeBuffer<const N: usize> = crate::StackBuffer<N>;
#[macro_export]
macro_rules! assert_buffer_size {
($size:expr, $min:expr) => {
const _: () = assert!($size >= $min, "Buffer size too small");
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn memory_bounds_stack_buffer() {
const SIZE: usize = MemoryBounds::stack_buffer_size(256);
assert_eq!(SIZE, 256 + core::mem::size_of::<usize>());
}
#[test]
fn memory_bounds_estimate() {
let estimate = MemoryBounds::borrowed_parse_estimate(10, 2, 5);
assert_eq!(estimate, 10 * 24 + 2 * 5 * 16);
}
#[test]
fn max_safe_depth_calculation() {
const STACK_SIZE: usize = 8192; const FRAME_SIZE: usize = 128;
let depth = MemoryBounds::max_safe_depth(STACK_SIZE, FRAME_SIZE);
assert!(depth >= 16);
assert!(depth <= 64);
}
#[test]
fn fixed_size_buffer_type() {
let _buf: FixedSizeBuffer<256> = FixedSizeBuffer::new();
assert_eq!(FixedSizeBuffer::<256>::new().capacity(), 256);
}
#[test]
fn stack_buffer_size_zero_capacity() {
const SIZE: usize = MemoryBounds::stack_buffer_size(0);
assert_eq!(SIZE, core::mem::size_of::<usize>());
}
#[test]
fn stack_buffer_size_one_byte() {
const SIZE: usize = MemoryBounds::stack_buffer_size(1);
assert_eq!(SIZE, 1 + core::mem::size_of::<usize>());
}
#[test]
fn stack_buffer_size_large_capacity() {
const SIZE: usize = MemoryBounds::stack_buffer_size(65536);
assert_eq!(SIZE, 65536 + core::mem::size_of::<usize>());
}
#[test]
fn stack_buffer_size_is_const_evaluable() {
const _: usize = MemoryBounds::stack_buffer_size(128);
}
#[test]
fn stack_buffer_size_grows_linearly_with_capacity() {
const A: usize = MemoryBounds::stack_buffer_size(100);
const B: usize = MemoryBounds::stack_buffer_size(200);
assert_eq!(B - A, 100);
}
#[test]
fn borrowed_parse_estimate_all_zeros() {
assert_eq!(MemoryBounds::borrowed_parse_estimate(0, 0, 0), 0);
}
#[test]
fn borrowed_parse_estimate_only_nodes_no_containers() {
let est = MemoryBounds::borrowed_parse_estimate(5, 0, 0);
assert_eq!(est, 5 * 24);
}
#[test]
fn borrowed_parse_estimate_only_containers_no_nodes() {
let est = MemoryBounds::borrowed_parse_estimate(0, 3, 10);
assert_eq!(est, 3 * 10 * 16);
}
#[test]
fn borrowed_parse_estimate_single_node() {
let est = MemoryBounds::borrowed_parse_estimate(1, 0, 0);
assert_eq!(est, 24);
}
#[test]
fn borrowed_parse_estimate_scales_with_nodes() {
let est_a = MemoryBounds::borrowed_parse_estimate(10, 0, 0);
let est_b = MemoryBounds::borrowed_parse_estimate(20, 0, 0);
assert_eq!(est_b, est_a * 2);
}
#[test]
fn borrowed_parse_estimate_scales_with_containers() {
let est_a = MemoryBounds::borrowed_parse_estimate(0, 1, 10);
let est_b = MemoryBounds::borrowed_parse_estimate(0, 2, 10);
assert_eq!(est_b, est_a * 2);
}
#[test]
fn borrowed_parse_estimate_is_const_evaluable() {
const _: usize = MemoryBounds::borrowed_parse_estimate(5, 2, 3);
}
#[test]
fn max_safe_depth_zero_frame_size_returns_zero() {
assert_eq!(MemoryBounds::max_safe_depth(8192, 0), 0);
}
#[test]
fn max_safe_depth_zero_stack_returns_zero() {
assert_eq!(MemoryBounds::max_safe_depth(0, 128), 0);
}
#[test]
fn max_safe_depth_respects_fifty_percent_safety_margin() {
assert_eq!(MemoryBounds::max_safe_depth(200, 10), 10);
}
#[test]
fn max_safe_depth_large_frame_limits_depth() {
let depth = MemoryBounds::max_safe_depth(8192, 4096);
assert_eq!(depth, 1);
}
#[test]
fn max_safe_depth_exact_fit() {
assert_eq!(MemoryBounds::max_safe_depth(100, 10), 5);
}
#[test]
fn max_safe_depth_is_const_evaluable() {
const _: usize = MemoryBounds::max_safe_depth(8192, 128);
}
#[test]
fn fixed_size_buffer_is_empty_on_creation() {
let buf = FixedSizeBuffer::<64>::new();
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);
}
#[test]
fn fixed_size_buffer_capacity_matches_const_param() {
assert_eq!(FixedSizeBuffer::<1>::new().capacity(), 1);
assert_eq!(FixedSizeBuffer::<512>::new().capacity(), 512);
assert_eq!(FixedSizeBuffer::<1024>::new().capacity(), 1024);
}
#[test]
fn fixed_size_buffer_push_and_read() {
let mut buf = FixedSizeBuffer::<4>::new();
assert!(buf.push(b'i'));
assert!(buf.push(b'4'));
assert!(buf.push(b'2'));
assert!(buf.push(b'e'));
assert_eq!(buf.as_slice(), b"i42e");
}
#[test]
fn fixed_size_buffer_push_when_full_returns_false() {
let mut buf = FixedSizeBuffer::<2>::new();
assert!(buf.push(b'a'));
assert!(buf.push(b'b'));
assert!(!buf.push(b'c'));
assert_eq!(buf.len(), 2);
}
#[test]
fn fixed_size_buffer_clear_resets_length() {
let mut buf = FixedSizeBuffer::<8>::new();
buf.push(b'x');
buf.clear();
assert!(buf.is_empty());
}
#[test]
fn fixed_size_buffer_from_slice_works() {
let buf = FixedSizeBuffer::<5>::from_slice(b"hello").unwrap();
assert_eq!(buf.as_slice(), b"hello");
}
#[test]
fn fixed_size_buffer_from_slice_too_large_returns_none() {
assert!(FixedSizeBuffer::<3>::from_slice(b"abcd").is_none());
}
}