internet 0.1.0

Network library for rust
Documentation
//! Variable-length integer encoding following [Section 16].
//!
//! Variable-length integer are encoding used to compress unsigned integers.
//!
//! Encoding is provided for the [Variable-Length Integer].
//!
//! [Section 16]: https://datatracker.ietf.org/doc/html/rfc9000#section-16
//! [Variable-Length Integer]: VariableLengthInteger

use crate::{
    Buf,
    BufError::{self},
    BufMut, BufResult, Codec, Cursor,
};

/// A Variable-Length Integer following [Section 16].
///
/// Encoding used to compress unsigned integers.
///
/// [Section 16]: https://datatracker.ietf.org/doc/html/rfc9000#section-16
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct VariableLengthInteger(pub u64);

impl VariableLengthInteger {
    /// Maximal possible value
    pub const MAX: u64 = (1 << 62) - 1;
    /// Minimal possible value
    pub const MIN: u64 = 0;
    /// Empty value
    pub const ZERO: Self = VariableLengthInteger(0);
    ///
    pub const MAX_VARINT: Self = VariableLengthInteger(Self::MAX);

    ///
    pub fn new(value: u64) -> BufResult<Self> {
        if value > Self::MAX {
            Err(BufError::UnexpectedValue)
        } else {
            Ok(VariableLengthInteger(value))
        }
    }

    ///
    pub const fn new_const(value: u64) -> Self {
        if value > Self::MAX {
            panic!("Variable-length integer value exceeded 2^62 - 1")
        } else {
            VariableLengthInteger(value)
        }
    }

    ///
    pub const fn into_inner(self) -> u64 {
        self.0
    }

    ///
    pub fn encoded_length(&self) -> usize {
        if self.0 <= 63 {
            1
        } else if self.0 <= 16_383 {
            2
        } else if self.0 <= 1_073_741_823 {
            4
        } else {
            8
        }
    }
}

impl TryFrom<u64> for VariableLengthInteger {
    type Error = BufError;

    fn try_from(val: u64) -> Result<Self, Self::Error> {
        VariableLengthInteger::new(val)
    }
}

impl From<VariableLengthInteger> for u64 {
    fn from(v: VariableLengthInteger) -> Self {
        v.0
    }
}

impl From<u8> for VariableLengthInteger {
    fn from(val: u8) -> Self {
        VariableLengthInteger(val as u64)
    }
}

impl From<u16> for VariableLengthInteger {
    fn from(val: u16) -> Self {
        VariableLengthInteger(val as u64)
    }
}

impl From<u32> for VariableLengthInteger {
    fn from(val: u32) -> Self {
        VariableLengthInteger(val as u64)
    }
}

#[cfg(feature = "std")]
impl std::ops::Deref for VariableLengthInteger {
    type Target = u64;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[cfg(feature = "std")]
impl std::fmt::Display for VariableLengthInteger {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl Codec for VariableLengthInteger {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        let len = self.encoded_length();

        match len {
            1 => (self.0 as u8).encode(writer, ()),
            2 => {
                let val = ((0b01u16) << 14) | (self.0 as u16);
                (&val.to_be_bytes()).encode(writer, ())
            }
            4 => {
                let val = ((0b10u32) << 30) | (self.0 as u32);
                (&val.to_be_bytes()).encode(writer, ())
            }
            8 => {
                let val = ((0b11u64) << 62) | self.0;
                (&val.to_be_bytes()).encode(writer, ())
            }
            _ => Err(BufError::UnexpectedValue),
        }
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        let first_byte = reader.peek_u8()?;
        let tag = first_byte >> 6;
        let len = 1 << tag;

        match len {
            1 => {
                reader.advance(1)?;
                Ok(VariableLengthInteger((first_byte & 0x3F) as u64))
            }
            2 => {
                let mut bytes = reader.read_array::<2>()?;
                bytes[0] &= 0x3F;
                Ok(VariableLengthInteger(u16::from_be_bytes(bytes) as u64))
            }
            4 => {
                let mut bytes = reader.read_array::<4>()?;
                bytes[0] &= 0x3F;
                Ok(VariableLengthInteger(u32::from_be_bytes(bytes) as u64))
            }
            8 => {
                let mut bytes = reader.read_array::<8>()?;
                bytes[0] &= 0x3F;
                Ok(VariableLengthInteger(u64::from_be_bytes(bytes)))
            }
            _ => Err(BufError::UnexpectedValue),
        }
    }
}

///
pub type VarInt = VariableLengthInteger;

#[cfg(test)]
mod tests {
    use crate::{Codec, Cursor, ietf::quicv1::VariableLengthInteger};

    #[test]
    fn variable_length_integer() {
        let bytes = &[0x25];
        let varint = VariableLengthInteger::decode(&mut Cursor::new(bytes), ()).unwrap();
        let expected_value = 37;
        assert_eq!(varint.into_inner(), expected_value);

        let bytes = &[0x7b, 0x40];
        let varint = VariableLengthInteger::decode(&mut Cursor::new(bytes), ()).unwrap();
        let expected_value = 15168;
        assert_eq!(varint.into_inner(), expected_value);

        let bytes = &[0x9d, 0x7f, 0x3e, 0x7d];
        let varint = VariableLengthInteger::decode(&mut Cursor::new(bytes), ()).unwrap();
        let expected_value = 494_878_333;
        assert_eq!(varint.into_inner(), expected_value);

        let bytes = &[0xc2, 0x19, 0x7c, 0x5e, 0x2f, 0x3b, 0x5a, 0x4d];
        let varint = VariableLengthInteger::decode(&mut Cursor::new(bytes), ()).unwrap();
        let expected_value = 151_288_806_454_811_213;
        assert_eq!(varint.into_inner(), expected_value);
    }

    #[test]
    fn rfc_roundtrip() {
        let examples: &[u64] = &[37, 15_168, 494_878_333, 151_288_806_454_811_213];
        let expected_bytes: &[&[u8]] = &[
            &[0x25],
            &[0x7b, 0x40],
            &[0x9d, 0x7f, 0x3e, 0x7d],
            &[0xc2, 0x19, 0x7c, 0x5e, 0x2f, 0x3b, 0x5a, 0x4d],
        ];

        for (val, expected) in examples.iter().zip(expected_bytes.iter()) {
            let varint = VariableLengthInteger::new(*val).unwrap();
            let mut buf = [0u8; 8];
            varint.encode(&mut Cursor::new(&mut buf), ()).unwrap();
            let len = varint.encoded_length();
            assert_eq!(&buf[..len], *expected);
        }
    }
}