pub trait Buffer {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn as_slice(&self) -> &[u8];
fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<(), BufferFull>;
fn drain(&mut self, amount: usize);
}
#[derive(Debug, thiserror::Error)]
#[error("buffer is too small to hold the pending data")]
pub struct BufferFull;
#[derive(Debug)]
pub struct FixedCapacityBuffer<const N: usize> {
data: [u8; N],
len: usize,
}
impl<const N: usize> FixedCapacityBuffer<N> {
pub fn new() -> Self {
Self {
data: [0; N],
len: 0,
}
}
}
impl<const N: usize> Default for FixedCapacityBuffer<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> Buffer for FixedCapacityBuffer<N> {
fn len(&self) -> usize {
self.len
}
fn as_slice(&self) -> &[u8] {
&self.data[..self.len]
}
fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<(), BufferFull> {
let end = self
.len
.checked_add(bytes.len())
.filter(|&end| end <= N)
.ok_or(BufferFull)?;
self.data[self.len..end].copy_from_slice(bytes);
self.len = end;
Ok(())
}
fn drain(&mut self, amount: usize) {
self.data.copy_within(amount..self.len, 0);
self.len -= amount;
}
}
#[cfg(feature = "alloc")]
pub use alloc::DynamicBuffer;
#[cfg(feature = "alloc")]
mod alloc {
use {super::*, ::alloc::vec::Vec};
#[derive(Debug, Default)]
pub struct DynamicBuffer {
data: Vec<u8>,
}
impl DynamicBuffer {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
}
}
}
impl Buffer for DynamicBuffer {
fn len(&self) -> usize {
self.data.len()
}
fn as_slice(&self) -> &[u8] {
&self.data
}
fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<(), BufferFull> {
self.data.extend_from_slice(bytes);
Ok(())
}
fn drain(&mut self, amount: usize) {
self.data.drain(..amount);
}
}
}
pub struct Bounded<B, const N: usize> {
buffer: B,
}
impl<B, const N: usize> Bounded<B, N>
where
B: Buffer,
{
pub fn new(buffer: B) -> Self {
Self { buffer }
}
}
impl<B, const N: usize> Buffer for Bounded<B, N>
where
B: Buffer,
{
fn len(&self) -> usize {
self.buffer.len()
}
fn as_slice(&self) -> &[u8] {
self.buffer.as_slice()
}
fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<(), BufferFull> {
if self.buffer.len() + bytes.len() > N {
return Err(BufferFull);
}
self.buffer.extend_from_slice(bytes)
}
fn drain(&mut self, amount: usize) {
self.buffer.drain(amount);
}
}
#[cfg(test)]
mod tests {
use {super::*, core::assert_matches};
#[test]
fn fixed_capacity_buffer_rejects_overflow() {
let mut buffer = FixedCapacityBuffer::<4>::new();
buffer.extend_from_slice(&[1, 2]).unwrap();
assert_matches!(buffer.extend_from_slice(&[3, 4, 5]), Err(BufferFull));
assert_eq!(buffer.as_slice(), &[1, 2]);
}
#[test]
fn fixed_capacity_buffer_drains_from_the_front() {
let mut buffer = FixedCapacityBuffer::<8>::new();
buffer.extend_from_slice(&[1, 2, 3, 4]).unwrap();
buffer.drain(2);
assert_eq!(buffer.as_slice(), &[3, 4]);
buffer.extend_from_slice(&[5, 6]).unwrap();
assert_eq!(buffer.as_slice(), &[3, 4, 5, 6]);
}
#[cfg(feature = "alloc")]
#[test]
fn dynamic_buffer_grows_to_fit() {
let mut buffer = DynamicBuffer::new();
buffer.extend_from_slice(&[0; 1024]).unwrap();
assert_eq!(buffer.len(), 1024);
}
}