rust-mqtt 0.5.1

MQTT client for embedded and non-embedded environments
Documentation
use crate::{
    fmt::{const_debug_assert, debug_assert, debug_assert_eq},
    types::TooLargeToEncode,
};

/// MQTT's variable byte integer encoding. The value has to be less than
/// [`VarByteInt::MAX_ENCODABLE`] (`268_435_455`). Exceeding this ultimately leads to
/// panics or malformed packets.
///
/// Used for packet length and some properties.
///
/// Use its [`TryFrom`] ([`u32`]) and [`From`] ([`u16`], [`u8`]) implementations to
/// construct a value.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct VarByteInt(u32);

impl VarByteInt {
    /// The maximum encodable value using the variable byte integer encoding according to
    /// <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901011>.
    pub const MAX_ENCODABLE: u32 = 268_435_455;

    /// Creates a variable byte integer by checking for the maximum value of
    /// [`VarByteInt::MAX_ENCODABLE`].
    /// For a version accepting [`u16`] and [`u8`], use [`From::from`].
    #[must_use]
    pub const fn new(value: u32) -> Option<Self> {
        if value > Self::MAX_ENCODABLE {
            None
        } else {
            Some(Self(value))
        }
    }

    /// Creates a variable byte integer without checking for the maximum value of
    /// [`VarByteInt::MAX_ENCODABLE`].
    /// For a fallible version, use [`VarByteInt::new`].
    /// For an infallible version accepting [`u16`] and [`u8`], use [`From::from`].
    ///
    /// # Invariants
    /// The value parameter must be less than or equal to [`VarByteInt::MAX_ENCODABLE`].
    ///
    /// # Panics
    /// Panics in debug builds if `value` exceeds [`VarByteInt::MAX_ENCODABLE`].
    #[must_use]
    pub const fn new_unchecked(value: u32) -> Self {
        const_debug_assert!(
            value <= Self::MAX_ENCODABLE,
            "the value exceeds MAX_ENCODABLE"
        );

        Self(value)
    }

    /// Returns the inner value.
    #[must_use]
    pub const fn value(&self) -> u32 {
        self.0
    }

    /// Returns [`Self::value`] as [`usize`].
    #[must_use]
    pub const fn size(&self) -> usize {
        self.0 as usize
    }

    /// Decodes a variable byte integer from a slice.
    ///
    /// # Invariants
    /// The slice must contain a correctly encoded variable byte integer and
    /// have exactly the length of that encoding.
    pub(crate) fn from_slice_unchecked(slice: &[u8]) -> Self {
        let mut multiplier = 1;
        let mut value = 0;

        debug_assert!(
            !slice.is_empty() && slice.len() <= 4,
            "encodings are always 1..=4 bytes long, {} is invalid",
            slice.len()
        );

        debug_assert_eq!(
            slice.last().unwrap() & 128,
            0,
            "the last byte of the encoding must not have bit 7 set"
        );

        for b in slice {
            value += u32::from(b & 0x7F) * multiplier;
            multiplier *= 128;
            if b & 128 == 0 {
                break;
            }
        }

        Self::new_unchecked(value)
    }
}

impl TryFrom<u32> for VarByteInt {
    type Error = TooLargeToEncode;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        Self::new(value).ok_or(TooLargeToEncode)
    }
}
impl From<u16> for VarByteInt {
    fn from(value: u16) -> Self {
        Self(u32::from(value))
    }
}
impl From<u8> for VarByteInt {
    fn from(value: u8) -> Self {
        Self(u32::from(value))
    }
}