Skip to main content

rust_mqtt/types/
int.rs

1use crate::{
2    fmt::{const_debug_assert, debug_assert, debug_assert_eq},
3    types::TooLargeToEncode,
4};
5
6/// MQTT's variable byte integer encoding. The value has to be less than
7/// [`VarByteInt::MAX_ENCODABLE`] (`268_435_455`). Exceeding this ultimately leads to
8/// panics or malformed packets.
9///
10/// Used for packet length and some properties.
11///
12/// Use its [`TryFrom`] ([`u32`]) and [`From`] ([`u16`], [`u8`]) implementations to
13/// construct a value.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub struct VarByteInt(u32);
17
18impl VarByteInt {
19    /// The maximum encodable value using the variable byte integer encoding according to
20    /// <https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901011>.
21    pub const MAX_ENCODABLE: u32 = 268_435_455;
22
23    /// Creates a variable byte integer by checking for the maximum value of
24    /// [`VarByteInt::MAX_ENCODABLE`].
25    /// For a version accepting [`u16`] and [`u8`], use [`From::from`].
26    #[must_use]
27    pub const fn new(value: u32) -> Option<Self> {
28        if value > Self::MAX_ENCODABLE {
29            None
30        } else {
31            Some(Self(value))
32        }
33    }
34
35    /// Creates a variable byte integer without checking for the maximum value of
36    /// [`VarByteInt::MAX_ENCODABLE`].
37    /// For a fallible version, use [`VarByteInt::new`].
38    /// For an infallible version accepting [`u16`] and [`u8`], use [`From::from`].
39    ///
40    /// # Invariants
41    /// The value parameter must be less than or equal to [`VarByteInt::MAX_ENCODABLE`].
42    ///
43    /// # Panics
44    /// Panics in debug builds if `value` exceeds [`VarByteInt::MAX_ENCODABLE`].
45    #[must_use]
46    pub const fn new_unchecked(value: u32) -> Self {
47        const_debug_assert!(
48            value <= Self::MAX_ENCODABLE,
49            "the value exceeds MAX_ENCODABLE"
50        );
51
52        Self(value)
53    }
54
55    /// Returns the inner value.
56    #[must_use]
57    pub const fn value(&self) -> u32 {
58        self.0
59    }
60
61    /// Returns [`Self::value`] as [`usize`].
62    #[must_use]
63    pub const fn size(&self) -> usize {
64        self.0 as usize
65    }
66
67    /// Decodes a variable byte integer from a slice.
68    ///
69    /// # Invariants
70    /// The slice must contain a correctly encoded variable byte integer and
71    /// have exactly the length of that encoding.
72    pub(crate) fn from_slice_unchecked(slice: &[u8]) -> Self {
73        let mut multiplier = 1;
74        let mut value = 0;
75
76        debug_assert!(
77            !slice.is_empty() && slice.len() <= 4,
78            "encodings are always 1..=4 bytes long, {} is invalid",
79            slice.len()
80        );
81
82        debug_assert_eq!(
83            slice.last().unwrap() & 128,
84            0,
85            "the last byte of the encoding must not have bit 7 set"
86        );
87
88        for b in slice {
89            value += u32::from(b & 0x7F) * multiplier;
90            multiplier *= 128;
91            if b & 128 == 0 {
92                break;
93            }
94        }
95
96        Self::new_unchecked(value)
97    }
98}
99
100impl TryFrom<u32> for VarByteInt {
101    type Error = TooLargeToEncode;
102
103    fn try_from(value: u32) -> Result<Self, Self::Error> {
104        Self::new(value).ok_or(TooLargeToEncode)
105    }
106}
107impl From<u16> for VarByteInt {
108    fn from(value: u16) -> Self {
109        Self(u32::from(value))
110    }
111}
112impl From<u8> for VarByteInt {
113    fn from(value: u8) -> Self {
114        Self(u32::from(value))
115    }
116}