use crate::error::{check_max, EncodingError};
use bytes::{BufMut, BytesMut};
pub(crate) fn with_u8_len(
buf: &mut BytesMut,
field: &'static str,
f: impl FnOnce(&mut BytesMut) -> Result<(), EncodingError>,
) -> Result<(), EncodingError> {
let at = buf.len();
buf.put_u8(0);
f(buf)?;
let len = buf.len() - at - 1;
check_max(field, len, u8::MAX as usize)?;
buf[at] = len as u8;
Ok(())
}
pub(crate) fn with_u16_len(
buf: &mut BytesMut,
field: &'static str,
f: impl FnOnce(&mut BytesMut) -> Result<(), EncodingError>,
) -> Result<(), EncodingError> {
let at = buf.len();
buf.put_u16(0);
f(buf)?;
let len = buf.len() - at - 2;
check_max(field, len, u16::MAX as usize)?;
buf[at..at + 2].copy_from_slice(&(len as u16).to_be_bytes());
Ok(())
}
pub(crate) fn put_u8_len_slice(
buf: &mut BytesMut,
field: &'static str,
slice: &[u8],
) -> Result<(), EncodingError> {
check_max(field, slice.len(), u8::MAX as usize)?;
buf.put_u8(slice.len() as u8);
buf.put_slice(slice);
Ok(())
}
pub(crate) fn put_u16_len_slice(
buf: &mut BytesMut,
field: &'static str,
slice: &[u8],
) -> Result<(), EncodingError> {
check_max(field, slice.len(), u16::MAX as usize)?;
buf.put_u16(slice.len() as u16);
buf.put_slice(slice);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::check_max;
#[test]
fn test_with_u8_len_basic() {
let mut buf = BytesMut::new();
with_u8_len(&mut buf, "test", |b| {
b.put_slice(&[1, 2, 3]);
Ok(())
})
.unwrap();
assert_eq!(&buf[..], &[3, 1, 2, 3]);
}
#[test]
fn test_with_u8_len_empty() {
let mut buf = BytesMut::new();
with_u8_len(&mut buf, "test", |_| Ok(())).unwrap();
assert_eq!(&buf[..], &[0]);
}
#[test]
fn test_with_u8_len_at_max() {
let mut buf = BytesMut::new();
with_u8_len(&mut buf, "test", |b| {
b.put_slice(&[0xAA; 255]);
Ok(())
})
.unwrap();
assert_eq!(buf[0], 255);
assert_eq!(buf.len(), 256);
}
#[test]
fn test_with_u8_len_overflow() {
let mut buf = BytesMut::new();
let err = with_u8_len(&mut buf, "test field", |b| {
b.put_slice(&[0xAA; 256]);
Ok(())
})
.unwrap_err();
assert_eq!(
err,
EncodingError::ValueTooLarge {
field: "test field",
actual: 256,
max: 255
}
);
}
#[test]
fn test_with_u16_len_basic() {
let mut buf = BytesMut::new();
buf.put_u8(0xFF); with_u16_len(&mut buf, "test", |b| {
b.put_slice(&[1, 2, 3]);
Ok(())
})
.unwrap();
assert_eq!(&buf[..], &[0xFF, 0, 3, 1, 2, 3]);
}
#[test]
fn test_with_u16_len_overflow() {
let mut buf = BytesMut::new();
let err = with_u16_len(&mut buf, "test field", |b| {
b.put_slice(&vec![0xAA; 65536]);
Ok(())
})
.unwrap_err();
assert_eq!(
err,
EncodingError::ValueTooLarge {
field: "test field",
actual: 65536,
max: 65535
}
);
}
#[test]
fn test_nested_prefixes() {
let mut buf = BytesMut::new();
with_u16_len(&mut buf, "outer", |b| {
with_u8_len(b, "inner", |b| {
b.put_slice(&[7, 8]);
Ok(())
})
})
.unwrap();
assert_eq!(&buf[..], &[0, 3, 2, 7, 8]);
}
#[test]
fn test_inner_error_propagates() {
let mut buf = BytesMut::new();
let err = with_u16_len(&mut buf, "outer", |_| {
Err(EncodingError::too_large("inner", 1, 0))
})
.unwrap_err();
assert_eq!(
err,
EncodingError::ValueTooLarge {
field: "inner",
actual: 1,
max: 0
}
);
}
#[test]
fn test_check_max_custom_bound() {
assert_eq!(check_max("f", 4095, 0x0FFF), Ok(4095));
assert!(check_max("f", 4096, 0x0FFF).is_err());
}
}