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}