vunsigned-varint 0.1.2

no_std compatible implementation of the unsigned-varint format
Documentation
use num_traits::{CheckedShl, FromPrimitive, PrimInt, Unsigned};
use thiserror::Error;

/// Decodes an unsigned integer from a given buffer, returning the integer and the amount of bytes read.
///
/// Note that this implementation protects against memory attacks by limiting the number of
/// processed bytes to the number of bytes needed to represent the max value of `N`.
/// If you want to be more strict, please limit the buffer length.
/// The unsigned-varint spec recommends a limit of 9 Bytes.
///
/// # Example
///
/// ```
/// # use vunsigned_varint::decode;
/// let (num, read) = decode::<u16>(&[0x80, 0x80, 0x01, 0xFF]).unwrap();
/// assert_eq!(num, 0x4000);
/// assert_eq!(read, 3);
/// ```
pub fn decode<N>(buffer: &[u8]) -> Result<(N, usize), Error>
where
    N: PrimInt + Unsigned + FromPrimitive + CheckedShl,
{
    let mut num = N::zero();
    if buffer.is_empty() {
        return Err(Error::EmptyBuffer);
    }
    let byte_num = (core::mem::size_of::<N>() * 8).div_ceil(7);
    for (i, byte) in buffer.iter().take(byte_num).copied().enumerate() {
        num = num
            | N::from_u8(byte & 0x7F)
                .expect("Anything should be able to represent a byte")
                .checked_shl(7 * i as u32)
                .ok_or(Error::Overflow)?;
        if byte & 0x80 == 0 {
            return Ok((num, i + 1));
        }
    }
    Err(Error::UnexpectedEof)
}

/// Errors that can occur decoding an unsigned varint.
#[derive(Debug, Error, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Error {
    /// buffer is empty
    #[error("buffer is empty")]
    EmptyBuffer,
    /// unexpected end of buffer before end of varint
    #[error("unexpected end of buffer before end of varint")]
    UnexpectedEof,
    /// decoded value does not fit into the target integer type
    #[error("decoded value does not fit into the target integer type")]
    Overflow,
}

impl Error {
    /// Convenience Wrapper around `Error::into`.
    #[cfg(feature = "std")]
    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
    #[must_use]
    pub fn into_io_error(self) -> std::io::Error {
        self.into()
    }
}

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl From<Error> for std::io::Error {
    fn from(error: Error) -> Self {
        let kind = match error {
            Error::EmptyBuffer | Error::UnexpectedEof => std::io::ErrorKind::UnexpectedEof,
            Error::Overflow => std::io::ErrorKind::InvalidData,
        };
        Self::new(kind, error)
    }
}

#[cfg(test)]
mod test {
    use crate::decode;

    #[test]
    fn d1() {
        let num: u8 = decode(&[0x01]).unwrap().0;
        assert_eq!(num, 0x01_u8);
    }

    #[test]
    fn d7f() {
        let num: u8 = decode(&[0x7f]).unwrap().0;
        assert_eq!(num, 0x7f_u8);
    }

    #[test]
    fn d80() {
        let num: u8 = decode(&[0x80, 0x01]).unwrap().0;
        assert_eq!(num, 0x80_u8);
    }

    #[test]
    fn dff() {
        let num: u8 = decode(&[0xff, 0x01]).unwrap().0;
        assert_eq!(num, 0xff_u8);
    }

    #[test]
    fn d12c() {
        let num: u16 = decode(&[0xac, 0x02]).unwrap().0;
        assert_eq!(num, 0x12c_u16);
    }

    #[test]
    fn d4000() {
        let num: u16 = decode(&[0x80, 0x80, 0x01]).unwrap().0;
        assert_eq!(num, 0x4000_u16);
    }
}