sharkyflac 0.1.0

A pure rust FLAC decoder and encoder
Documentation
#![allow(clippy::must_use_candidate)]

use std::fmt::{Debug, Write};
use std::ops::{Deref, DerefMut};
use std::str;

use dizzy::DstNewtype;

#[derive(PartialEq, DstNewtype)]
#[dizzy(invariant = str::is_ascii)]
#[dizzy(constructor = pub const from_str, getter = pub const as_str)]
#[dizzy(derive(Debug, Deref, IntoBoxed, CloneBoxed))]
#[repr(transparent)]
pub struct AsciiStr(str);

impl AsciiStr {
    #[inline]
    pub fn from_bytes(bytes: &[u8]) -> Option<&Self> {
        str::from_utf8(bytes).ok().and_then(Self::from_str)
    }

    /// # Safety
    ///
    /// - `bytes` must contain only ASCII characters.
    #[inline]
    pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
        unsafe { Self::from_str_unchecked(str::from_utf8_unchecked(bytes)) }
    }

    /// # Safety
    ///
    /// - `src` must contain only ASCII characters.
    #[inline]
    pub const unsafe fn from_str_unchecked(src: &str) -> &Self {
        unsafe { std::mem::transmute(src) }
    }
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[must_use]
pub struct AsciiArray<const CAP: usize> {
    array: [u8; CAP],
    len:   usize,
}

impl<const CAP: usize> Deref for AsciiArray<CAP> {
    type Target = [u8];

    fn deref(&self) -> &Self::Target {
        &self.array[0..self.len]
    }
}

impl<const CAP: usize> AsRef<AsciiStr> for AsciiArray<CAP> {
    #[inline]
    fn as_ref(&self) -> &AsciiStr {
        self.as_ascii_str()
    }
}

impl<const CAP: usize> AsRef<str> for AsciiArray<CAP> {
    #[inline]
    fn as_ref(&self) -> &str {
        self.as_ascii_str().as_str()
    }
}

impl<const CAP: usize> std::fmt::Display for AsciiArray<CAP> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.as_str())
    }
}

impl<const CAP: usize> std::fmt::Debug for AsciiArray<CAP> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_char('"')?;
        for ch in self.escape_ascii() {
            f.write_char(ch as char)?;
        }
        f.write_char('"')
    }
}

#[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("Cannot store Non-Ascii data in an a AsciiArray")]
pub struct NonAscii;

impl<const N: usize> TryFrom<[u8; N]> for AsciiArray<N> {
    type Error = NonAscii;

    #[inline]
    fn try_from(array: [u8; N]) -> Result<Self, Self::Error> {
        Self::from_array(array, N)
    }
}

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

impl<const CAP: usize> AsciiArray<CAP> {
    pub const fn len(&self) -> usize {
        self.len
    }

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

    #[inline]
    pub const fn new() -> Self {
        Self {
            array: [0u8; CAP],
            len:   0,
        }
    }

    #[inline]
    pub const fn new_filled(byte: u8) -> Self {
        let mut x = Self::new();
        x.fill(byte);
        x
    }

    #[inline]
    pub const fn from_array(array: [u8; CAP], len: usize) -> Result<Self, NonAscii> {
        assert!(len <= CAP);

        if array.is_ascii() {
            Ok(Self { array, len })
        } else {
            Err(NonAscii)
        }
    }

    pub const fn get(&self, idx: usize) -> u8 {
        assert!(idx < self.len);
        self.array[idx]
    }

    #[inline]
    pub const fn fill(&mut self, ch: u8) {
        assert!(ch.is_ascii());

        let mut i = 0;

        while i < self.array.len() {
            self.array[i] = ch;
            i += 1;
        }
    }

    #[inline]
    pub const fn push(&mut self, ch: u8) {
        assert!(ch.is_ascii());
        assert!(self.len < CAP);

        self.array[self.len] = ch;
        self.len += 1;
    }

    #[inline]
    pub const fn pop(&mut self) -> Option<u8> {
        if self.len == 0 {
            None
        } else {
            self.len -= 1;
            Some(self.array[self.len])
        }
    }

    /// Trim arbatraty bytes from the end of the string.
    ///
    /// ```
    /// use sharkyflac::ascii_str::AsciiArray;
    ///
    /// let mut foo = AsciiArray::<16>::copy_from_str("bar\0\0\0\0").unwrap();
    /// assert!(foo.ends_with(b"\0"));
    ///
    /// foo.rtrim(b"\0");
    /// assert!(!foo.ends_with(b"\0"));
    /// ```
    pub fn rtrim(&mut self, needle: impl AsRef<[u8]>) {
        let needle = needle.as_ref();
        if needle.is_empty() {
            return;
        }

        while self.array[..self.len].ends_with(needle) {
            self.len = self.len.saturating_sub(needle.len());
        }
    }

    pub fn copy_from_str(s: &str) -> Option<Self> {
        if s.len() > CAP {
            return None;
        }

        let ascii = AsciiStr::from_str(s)?.as_bytes();
        let mut array = [0u8; CAP];
        array[..ascii.len()].copy_from_slice(ascii);

        Some(Self {
            array,
            len: ascii.len(),
        })
    }

    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
        if !bytes.is_ascii() || bytes.len() > CAP {
            return None;
        }
        let mut array = [0u8; CAP];
        array[0..bytes.len()].copy_from_slice(bytes);

        Some(Self {
            array,
            len: bytes.len(),
        })
    }

    #[inline]
    pub fn as_str(&self) -> &str {
        self.as_ascii_str().as_str()
    }

    #[inline]
    pub fn as_ascii_str(&self) -> &AsciiStr {
        self.array
            .is_ascii()
            .then(|| unsafe { self.as_ascii_str_unchecked() })
            .expect("`AsciiArray` contains only ASCII characters")
    }

    /// # Safety
    ///
    /// The [`AsciiArray`] must contain only ASCII characters. The only case in
    /// which this isn't the case is if it was created or modified with
    /// `unsafe` functions.
    #[inline]
    pub unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr {
        unsafe { AsciiStr::from_bytes_unchecked(&self.array[0..self.len]) }
    }
}

pub struct ArrayList<T, const C: usize> {
    array: [T; C],
    len:   usize,
}

impl<T: Debug, const C: usize> Debug for ArrayList<T, C> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ArrayList")
            .field("array", &&self.array[0..self.len])
            .field("len", &self.len)
            .finish()
    }
}

impl<T: Default + Clone + Copy, const C: usize> Default for ArrayList<T, C> {
    fn default() -> Self {
        Self {
            array: [T::default(); C],
            len:   0,
        }
    }
}

impl<T: Default + Clone + Copy, const C: usize> ArrayList<T, C> {
    #[inline]
    pub fn new_with_length(len: usize) -> Self {
        assert!(len <= C);

        Self {
            array: [T::default(); C],
            len,
        }
    }
}

impl<T: Default + Clone, const C: usize> ArrayList<T, C> {
    /// # Panics
    ///
    /// Will panic if the length of the slice is more than the remaning
    /// capacity.
    pub fn extend_from_slice(&mut self, slice: &[T]) {
        if slice.is_empty() {
            return;
        }
        assert!(slice.len() + self.len <= C);

        self.array[self.len..(self.len + slice.len())].clone_from_slice(slice);
        self.len += slice.len();
    }
}

impl<T, const C: usize> AsRef<[T]> for ArrayList<T, C> {
    #[inline]
    fn as_ref(&self) -> &[T] {
        &self.array[0..self.len]
    }
}

impl<T, const C: usize> AsMut<[T]> for ArrayList<T, C> {
    fn as_mut(&mut self) -> &mut [T] {
        &mut self.array[0..self.len]
    }
}
impl<T, const C: usize> Deref for ArrayList<T, C> {
    type Target = [T];

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.array[0..self.len]
    }
}
impl<T, const C: usize> DerefMut for ArrayList<T, C> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.array[0..self.len]
    }
}

impl<T, const C: usize> ArrayList<T, C> {
    #[inline]
    pub fn push(&mut self, item: T) {
        assert!(self.len < C);

        self.array[self.len] = item;
        self.len += 1;
    }

    #[inline]
    pub const fn set_len(&mut self, new_len: usize) {
        assert!(new_len <= C);
        self.len = new_len;
    }
}