use core::mem::size_of;
pub const MIN_BUFFER_SIZE: usize = 512;
pub const MIN_ALIGNMENT: usize = 4;
pub const fn align_size(size: usize, up: bool) -> usize {
let adjusted = if up {
size + (MIN_ALIGNMENT as usize) - 1
} else {
size
};
adjusted & !((1usize << MIN_ALIGNMENT) - 1)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BufferError {
CapacityOverflow,
}
pub trait ResizableBuffer {
fn get_buffer(&self) -> &[u8];
fn get_buffer_mut(&mut self) -> &mut [u8];
fn begin_to_grow(&mut self) -> Result<&mut [u8], BufferError>;
fn commit(&mut self);
fn rollback(&mut self);
}
#[derive(Debug)]
pub struct HeapResizableBuffer {
buffer: Vec<u8>,
new_buffer: Option<Vec<u8>>,
max_size_step: usize,
}
impl HeapResizableBuffer {
pub fn new(initial_size: usize, max_size_step: usize) -> Self {
let initial = align_size(initial_size, true).max(MIN_BUFFER_SIZE);
let step = align_size(max_size_step, false).max(MIN_BUFFER_SIZE);
Self {
buffer: vec![0u8; initial],
new_buffer: None,
max_size_step: step,
}
}
pub fn capacity(&self) -> usize {
self.buffer.len()
}
pub fn max_size_step(&self) -> usize {
self.max_size_step
}
}
impl Default for HeapResizableBuffer {
fn default() -> Self {
Self::new(4096, 1024 * 1024)
}
}
impl ResizableBuffer for HeapResizableBuffer {
fn get_buffer(&self) -> &[u8] {
&self.buffer
}
fn get_buffer_mut(&mut self) -> &mut [u8] {
&mut self.buffer
}
fn begin_to_grow(&mut self) -> Result<&mut [u8], BufferError> {
let old_len = self.buffer.len();
let step = old_len.min(self.max_size_step);
let new_len = old_len
.checked_add(step)
.ok_or(BufferError::CapacityOverflow)?;
let mut nb = vec![0u8; new_len];
nb[..old_len].copy_from_slice(&self.buffer);
self.new_buffer = Some(nb);
Ok(self.new_buffer.as_mut().expect("just set"))
}
fn commit(&mut self) {
if let Some(nb) = self.new_buffer.take() {
self.buffer = nb;
}
}
fn rollback(&mut self) {
self.new_buffer = None;
}
}
pub struct ResizableBufferSink<'a, Unit> {
buffer: &'a mut dyn ResizableBuffer,
written_bytes: usize,
_unit: core::marker::PhantomData<Unit>,
}
impl<'a, Unit> ResizableBufferSink<'a, Unit> {
pub fn new(buffer: &'a mut dyn ResizableBuffer) -> Self {
Self {
buffer,
written_bytes: 0,
_unit: core::marker::PhantomData,
}
}
pub fn encoded_bytes(&self) -> &[u8] {
let buf = self.buffer.get_buffer();
let start = buf.len() - self.written_bytes;
&buf[start..]
}
pub fn reset(&mut self) {
self.written_bytes = 0;
}
pub fn len(&self) -> usize {
self.written_bytes
}
pub fn is_empty(&self) -> bool {
self.written_bytes == 0
}
}
impl<'a> ResizableBufferSink<'a, u8> {
pub fn write_u8(&mut self, unit: u8) {
self.ensure_space(1);
let buf = self.buffer.get_buffer_mut();
let pos = buf.len() - self.written_bytes - 1;
buf[pos] = unit;
self.written_bytes += 1;
}
}
impl<'a> ResizableBufferSink<'a, u32> {
pub fn write_u32(&mut self, unit: u32) {
self.ensure_space(4);
let bytes = unit.to_le_bytes();
let buf = self.buffer.get_buffer_mut();
let pos = buf.len() - self.written_bytes - 4;
buf[pos..pos + 4].copy_from_slice(&bytes);
self.written_bytes += 4;
}
}
impl<'a, Unit> ResizableBufferSink<'a, Unit> {
fn ensure_space(&mut self, n: usize) {
let buf_len = self.buffer.get_buffer().len();
if self.written_bytes + n > buf_len {
self.enlarge(n);
}
}
fn enlarge(&mut self, needed: usize) {
let content = self.encoded_bytes().to_vec();
let new_units = self.buffer.begin_to_grow().expect("buffer growth");
let new_len = new_units.len();
assert!(
content.len() + needed <= new_len,
"new buffer must fit existing content plus needed bytes"
);
let new_content_start = new_len - content.len();
new_units[new_content_start..].copy_from_slice(&content);
self.buffer.commit();
self.written_bytes = content.len();
}
}
pub fn unit_byte_size<Unit>() -> usize {
size_of::<Unit>()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_align_size() {
assert_eq!(align_size(512, true), 512);
assert_eq!(align_size(513, true), 512);
assert_eq!(align_size(520, true), 512);
assert_eq!(align_size(528, true), 528);
assert_eq!(align_size(1000, false), 992);
}
#[test]
fn test_heap_buffer_initial_size() {
let b = HeapResizableBuffer::new(0, 1024 * 1024);
assert_eq!(b.capacity(), 512);
let b2 = HeapResizableBuffer::new(100, 1024 * 1024);
assert_eq!(b2.capacity(), 512);
let b3 = HeapResizableBuffer::new(4096, 1024 * 1024);
assert_eq!(b3.capacity(), 4096);
}
#[test]
fn test_growth_policy() {
let mut b = HeapResizableBuffer::new(512, 256);
let new = b.begin_to_grow().expect("grow");
assert_eq!(new.len(), 512 + 512);
b.commit();
assert_eq!(b.capacity(), 1024);
let new2 = b.begin_to_grow().expect("grow2");
assert_eq!(new2.len(), 1024 + 512);
b.rollback();
assert_eq!(b.capacity(), 1024, "rollback must restore");
}
#[test]
fn test_capped_growth_step() {
let mut b = HeapResizableBuffer::new(4096, 256);
let new = b.begin_to_grow().expect("grow");
assert_eq!(new.len(), 4608);
b.commit();
assert_eq!(b.capacity(), 4608);
}
#[test]
fn test_u8_sink_writes_backward() {
let mut buffer = HeapResizableBuffer::new(512, 256);
let mut sink = ResizableBufferSink::<u8>::new(&mut buffer);
sink.write_u8(0xAB);
sink.write_u8(0xCD);
assert_eq!(sink.len(), 2);
assert_eq!(sink.encoded_bytes(), &[0xCD, 0xAB]);
}
#[test]
fn test_u8_sink_growth_preserves_content() {
let mut buffer = HeapResizableBuffer::new(8, 4);
let mut sink = ResizableBufferSink::<u8>::new(&mut buffer);
for i in 0..10u8 {
sink.write_u8(i);
}
assert_eq!(sink.len(), 10);
let enc = sink.encoded_bytes();
for i in 0..10u8 {
assert_eq!(enc[(9 - i) as usize], i, "mismatch at {}", i);
}
}
#[test]
fn test_u32_sink_bytes() {
let mut buffer = HeapResizableBuffer::new(64, 16);
let mut sink = ResizableBufferSink::<u32>::new(&mut buffer);
sink.write_u32(0x01020304);
sink.write_u32(0x05060708);
let bytes = sink.encoded_bytes();
assert_eq!(&bytes[0..4], &[0x08, 0x07, 0x06, 0x05]);
assert_eq!(&bytes[4..8], &[0x04, 0x03, 0x02, 0x01]);
}
#[test]
fn test_sink_reset() {
let mut buffer = HeapResizableBuffer::new(512, 256);
let mut sink = ResizableBufferSink::<u8>::new(&mut buffer);
sink.write_u8(0xAB);
assert_eq!(sink.len(), 1);
sink.reset();
assert_eq!(sink.len(), 0);
}
#[test]
fn test_buffer_overflow_error() {
let mut b = HeapResizableBuffer::new(0, 0);
assert_eq!(b.capacity(), 512);
let _ = b.begin_to_grow().expect("growth succeeds");
b.commit();
assert!(b.capacity() > 512);
}
}