base91le 0.1.0

little-endian base91 encoding format that supports padding
Documentation
use std::io;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NoCapacity(u8);

impl std::fmt::Display for NoCapacity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "No enough capacity")
    }
}

impl std::error::Error for NoCapacity {}

#[derive(Debug, Clone, Copy)]
pub struct RingBuf<const CAP: usize> {
    buffer: [u8; CAP],
    tail: usize,
    len: usize,
}

impl<const CAP: usize> Default for RingBuf<CAP> {
    fn default() -> Self {
        Self::new()
    }
}

impl<const CAP: usize> RingBuf<CAP> {
    pub const CAPACITY: usize = CAP;

    pub const fn new() -> Self {
        Self {
            buffer: [0; CAP],
            tail: 0,
            len: 0,
        }
    }

    pub const fn as_slices(&self) -> (&[u8], &[u8]) {
        if self.is_contiguous() {
            unsafe {
                (
                    std::slice::from_raw_parts(&raw const self.buffer[self.tail], self.len),
                    std::slice::from_raw_parts(&raw const self.buffer[self.tail], 0),
                )
            }
        } else {
            let (mid, right) = self.buffer.split_at(self.tail);
            let (left, _) = mid.split_at(self.head());
            (right, left)
        }
    }

    pub const fn as_mut_slices(&mut self) -> (&mut [u8], &mut [u8]) {
        if self.is_contiguous() {
            unsafe {
                (
                    std::slice::from_raw_parts_mut(&raw mut self.buffer[self.tail], self.len),
                    std::slice::from_raw_parts_mut(&raw mut self.buffer[self.tail], 0),
                )
            }
        } else {
            let head = self.head();
            let (mid, right) = self.buffer.split_at_mut(self.tail);
            let (left, _) = mid.split_at_mut(head);
            (right, left)
        }
    }

    pub const fn push_back(&mut self, byte: u8) -> Result<(), NoCapacity> {
        if self.is_full() {
            Err(NoCapacity(byte))
        } else {
            self.buffer[self.head()] = byte;
            self.len += 1;
            Ok(())
        }
    }

    pub const fn push_front(&mut self, byte: u8) -> Result<(), NoCapacity> {
        if self.is_full() {
            Err(NoCapacity(byte))
        } else {
            self.tail = Self::wrapping_sub(self.tail, 1);
            self.buffer[self.tail] = byte;
            self.len += 1;
            Ok(())
        }
    }

    pub const fn pop_back(&mut self) -> Option<u8> {
        if self.is_empty() {
            return None;
        }
        self.len -= 1;
        let head = self.buffer[self.head()];
        Some(head)
    }

    pub fn pop_n_back<const N: usize>(&mut self) -> Option<[u8; N]> {
        if self.len < N {
            return None;
        }
        let head = self.head();
        let start = Self::wrapping_sub(head, N);
        let mut chunk = [0; N];
        if start > head {
            chunk[..CAP - start].copy_from_slice(&self.buffer[start..CAP]);
            chunk[CAP - start..].copy_from_slice(&self.buffer[..head]);
        } else {
            chunk.copy_from_slice(&self.buffer[start..head]);
        };
        self.len -= N;
        Some(chunk)
    }

    pub fn pop_front(&mut self) -> Option<u8> {
        if self.is_empty() {
            return None;
        }
        let tail = self.buffer[self.tail];
        self.tail = Self::wrapping_add(self.tail, 1);
        self.len -= 1;
        Some(tail)
    }

    pub fn pop_n_front<const N: usize>(&mut self) -> Option<[u8; N]> {
        if self.len < N {
            return None;
        }
        let end = Self::wrapping_add(self.tail, N);
        let mut chunk = [0; N];
        if self.tail > end {
            chunk[..CAP - self.tail].copy_from_slice(&self.buffer[self.tail..CAP]);
            chunk[CAP - self.tail..].copy_from_slice(&self.buffer[..end]);
        } else {
            chunk.copy_from_slice(&self.buffer[self.tail..end]);
        }
        self.tail = end;
        self.len -= N;
        Some(chunk)
    }

    pub const fn get(&self, index: usize) -> Option<&u8> {
        if index < self.len {
            let idx = Self::wrapping_add(self.tail, index);
            Some(&self.buffer[idx])
        } else {
            None
        }
    }

    pub const fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
        if index < self.len {
            let idx = Self::wrapping_add(self.tail, index);
            Some(&mut self.buffer[idx])
        } else {
            None
        }
    }

    pub fn copy_to_writer(&mut self, writer: &mut (impl io::Write + ?Sized)) -> io::Result<usize> {
        let (front, back) = self.as_slices();
        let bufs = [io::IoSlice::new(front), io::IoSlice::new(back)];
        let n = writer.write_vectored(&bufs)?;
        self.tail = Self::wrapping_add(self.tail, n);
        self.len -= n;
        Ok(n)
    }

    pub fn copy_from_reader(&mut self, reader: &mut (impl io::Read + ?Sized)) -> io::Result<usize> {
        let head = self.head();
        let n = if head > self.tail {
            reader.read(&mut self.buffer[self.tail..head])?
        } else {
            let (back, rem) = self.buffer.split_at_mut(head);
            let (_, front) = rem.split_at_mut(self.tail);
            let mut bufs = [io::IoSliceMut::new(front), io::IoSliceMut::new(back)];
            reader.read_vectored(&mut bufs)?
        };
        self.len += n;
        Ok(n)
    }

    const fn wrapping_add(tail: usize, rhs: usize) -> usize {
        (tail + rhs) % CAP
    }

    const fn wrapping_sub(tail: usize, rhs: usize) -> usize {
        (tail + CAP - rhs) % CAP
    }

    const fn head(&self) -> usize {
        Self::wrapping_add(self.tail, self.len)
    }

    pub const fn is_full(&self) -> bool {
        self.len == CAP
    }

    pub const fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub const fn is_contiguous(&self) -> bool {
        self.len + self.tail < CAP
    }

    pub const fn len(&self) -> usize {
        self.len
    }

    pub const fn capacity(&self) -> usize {
        CAP
    }
}

impl<const CAP: usize> io::Read for RingBuf<CAP> {
    #[inline]
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
        let (ref mut front, ref mut back) = self.as_slices();
        let mut n = front.read(buf)?;
        if front.is_empty() {
            buf = &mut buf[n..];
            n += back.read(buf)?;
        }
        self.tail = Self::wrapping_add(self.tail, n);
        self.len -= n;
        if self.len == 0 {
            self.tail = 0
        }
        Ok(n)
    }
}

impl<const CAP: usize> io::Write for RingBuf<CAP> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        if self.len == CAP {
            return Ok(0);
        }
        let head = self.head();
        let mut bytes = if head >= self.tail {
            &mut self.buffer[head..]
        } else {
            &mut self.buffer[head..self.tail]
        };
        let n = bytes.write(buf)?;
        self.len += n;
        Ok(n)
    }

    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}