use core::mem::ManuallyDrop;
use std::{slice, vec, vec::Vec};
#[derive(Debug)]
pub struct ByteBuffer {
pub(super) ptr: *mut u8,
pub(super) len: usize,
capacity: usize,
is_static: bool,
}
unsafe impl Send for ByteBuffer {}
fn vec_into_raw_parts(vec: Vec<u8>) -> (*mut u8, usize, usize) {
let mut vec = ManuallyDrop::new(vec);
(vec.as_mut_ptr(), vec.len(), vec.capacity())
}
impl ByteBuffer {
pub fn new(initial_len: usize) -> Self {
let vec = vec![0x00_u8; initial_len];
let (ptr, len, capacity) = vec_into_raw_parts(vec);
Self {
ptr,
len,
capacity,
is_static: false,
}
}
pub fn new_static(buffer: &'static mut [u8], initial_len: usize) -> Self {
assert!(initial_len <= buffer.len());
buffer[..initial_len].fill(0x00_u8);
Self {
ptr: buffer.as_mut_ptr(),
len: initial_len,
capacity: buffer.len(),
is_static: true,
}
}
pub fn grow(&mut self, new_size: usize) {
assert!(new_size >= self.len());
match self.get_vec() {
Some(mut vec) => {
vec.resize(new_size, 0x00_u8);
let (ptr, len, capacity) = vec_into_raw_parts(vec);
self.ptr = ptr;
self.len = len;
self.capacity = capacity;
}
None => {
if self.capacity < new_size {
panic!("cannot grow a byte buffer backed by `&'static mut [u8]` beyond its capacity")
}
let len = self.len();
self.len = new_size;
self.data_mut()[len..new_size].fill(0x00_u8);
}
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn data(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.ptr, self.len) }
}
pub fn data_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.ptr, self.len) }
}
fn get_vec(&mut self) -> Option<Vec<u8>> {
if self.is_static {
return None;
}
Some(unsafe { Vec::from_raw_parts(self.ptr, self.len, self.capacity) })
}
}
impl Drop for ByteBuffer {
fn drop(&mut self) {
self.get_vec();
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_basic_allocation_deallocation() {
let buffer = ByteBuffer::new(10);
assert_eq!(buffer.len(), 10);
}
#[test]
fn test_basic_data_manipulation() {
let mut buffer = ByteBuffer::new(10);
assert_eq!(buffer.len(), 10);
let data = buffer.data(); assert_eq!(data, &[0; 10]);
let data = buffer.data_mut(); data[4] = 4; let data = buffer.data(); assert_eq!(data, &[0, 0, 0, 0, 4, 0, 0, 0, 0, 0]); }
#[test]
fn test_static_buffer_initialization() {
static mut BUF: [u8; 10] = [7; 10];
let buf = unsafe { &mut *core::ptr::addr_of_mut!(BUF) };
let mut buffer = ByteBuffer::new_static(buf, 5);
assert_eq!(buffer.len(), 5);
let data = buffer.data_mut();
data[0] = 1;
unsafe {
assert_eq!(BUF[0], 1);
}
}
#[test]
fn test_growing_buffer() {
let mut buffer = ByteBuffer::new(5);
buffer.grow(10);
assert_eq!(buffer.len(), 10);
assert_eq!(buffer.data(), &[0; 10]);
}
#[test]
fn test_growing_static() {
static mut BUF: [u8; 10] = [7; 10];
let buf = unsafe { &mut *core::ptr::addr_of_mut!(BUF) };
let mut buffer = ByteBuffer::new_static(buf, 5);
assert_eq!(buffer.len(), 5);
assert_eq!(buffer.data(), &[0; 5]);
buffer.grow(8);
assert_eq!(buffer.len(), 8);
assert_eq!(buffer.data(), &[0; 8]);
buffer.grow(10);
assert_eq!(buffer.len(), 10);
assert_eq!(buffer.data(), &[0; 10]);
}
#[test]
#[should_panic]
fn test_static_buffer_overflow() {
static mut BUF: [u8; 5] = [7; 5];
let buf = unsafe { &mut *core::ptr::addr_of_mut!(BUF) };
let mut buffer = ByteBuffer::new_static(buf, 5);
buffer.grow(10); }
}