use crate::error::{Error, Result};
pub const MAX_PREFIX_INPUT_BYTES: usize = 64 * 1024 * 1024;
macro_rules! define_depth_prefix {
($name:ident, $open:pat, $close:pat, $doc:literal) => {
#[doc = $doc]
#[must_use]
pub fn $name(bytes: &[u8]) -> Result<Vec<u32>> {
depth_prefix(bytes, |byte| match byte {
$open => 1,
$close => -1,
_ => 0,
})
}
};
}
define_depth_prefix!(
brace_depth_prefix,
b'{',
b'}',
"Compute brace `{`/`}` depth at each byte offset."
);
define_depth_prefix!(
nested_depth_prefix,
b'{' | b'(',
b'}' | b')',
"Compute combined brace and parenthesis nesting depth at each byte offset."
);
fn depth_prefix(bytes: &[u8], depth_delta: impl Fn(u8) -> i8) -> Result<Vec<u32>> {
validate_prefix_input(bytes.len())?;
let mut depths = Vec::with_capacity(bytes.len().max(1));
let mut depth = 0u32;
for &byte in bytes {
depths.push(depth);
match depth_delta(byte) {
1 => depth = depth.saturating_add(1),
-1 => depth = depth.saturating_sub(1),
_ => {}
}
}
if depths.is_empty() {
depths.push(0);
}
Ok(depths)
}
#[must_use]
pub fn newline_prefix_sum(bytes: &[u8]) -> Result<Vec<u32>> {
validate_prefix_input(bytes.len())?;
let capacity = bytes.len().checked_add(1).ok_or_else(|| Error::Prefix {
message: "newline prefix capacity overflowed usize. Fix: split the input before prefixing."
.to_string(),
})?;
let mut sums = Vec::with_capacity(capacity);
let mut count = 0u32;
sums.push(0);
for &byte in bytes {
if byte == b'\n' {
count = count.saturating_add(1);
}
sums.push(count);
}
Ok(sums)
}
#[must_use]
pub fn validate_prefix_input(byte_len: usize) -> Result<()> {
if byte_len > MAX_PREFIX_INPUT_BYTES {
return Err(Error::Prefix {
message: format!(
"prefix input is {byte_len} bytes, exceeding {MAX_PREFIX_INPUT_BYTES}. Fix: split the file before prefix construction."
),
});
}
Ok(())
}