mqtt5_protocol/encoding/
variable_int.rs

1//! BeBytes-compatible variable length integer implementation for MQTT
2//!
3//! This module provides a variable length integer type that integrates with `BeBytes` 2.3.0
4//! for automatic serialization/deserialization with size expressions support.
5
6use crate::error::{MqttError, Result};
7use bebytes::BeBytes;
8use bytes::{Buf, BufMut};
9use std::fmt;
10
11/// Maximum value that can be encoded as a variable byte integer (268,435,455)
12pub const VARIABLE_INT_MAX: u32 = 268_435_455;
13
14/// Variable length integer as defined by MQTT specification
15///
16/// Encodes values using 1-4 bytes:
17/// - 0-127: 1 byte
18/// - 128-16,383: 2 bytes
19/// - 16,384-2,097,151: 3 bytes
20/// - 2,097,152-268,435,455: 4 bytes
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct VariableInt {
23    value: u32,
24}
25
26impl VariableInt {
27    /// Creates a new `VariableInt` from a u32 value
28    ///
29    /// # Errors
30    ///
31    /// Returns `MqttError::ProtocolError` if the value exceeds the maximum
32    pub fn new(value: u32) -> Result<Self> {
33        if value > VARIABLE_INT_MAX {
34            return Err(MqttError::ProtocolError(format!(
35                "Variable integer value {value} exceeds maximum {VARIABLE_INT_MAX}"
36            )));
37        }
38        Ok(Self { value })
39    }
40
41    /// Creates a new `VariableInt` from a u32 value without validation
42    ///
43    /// # Safety
44    ///
45    /// The caller must ensure that value <= `VARIABLE_INT_MAX`
46    #[must_use]
47    pub fn new_unchecked(value: u32) -> Self {
48        debug_assert!(value <= VARIABLE_INT_MAX);
49        Self { value }
50    }
51
52    /// Returns the actual value
53    #[must_use]
54    pub fn value(&self) -> u32 {
55        self.value
56    }
57
58    /// Returns the number of bytes needed to encode this value
59    #[must_use]
60    pub fn encoded_size(&self) -> u32 {
61        match self.value {
62            0..=127 => 1,
63            128..=16_383 => 2,
64            16_384..=2_097_151 => 3,
65            2_097_152..=VARIABLE_INT_MAX => 4,
66            _ => unreachable!("Invalid variable int value"),
67        }
68    }
69
70    /// Encodes this variable integer into the provided buffer
71    ///
72    /// # Errors
73    ///
74    /// Always succeeds for valid `VariableInt` values
75    pub fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
76        let mut val = self.value;
77        loop {
78            let mut byte = (val % 128) as u8;
79            val /= 128;
80            if val > 0 {
81                byte |= 0x80; // Set continuation bit
82            }
83            buf.put_u8(byte);
84            if val == 0 {
85                break;
86            }
87        }
88        Ok(())
89    }
90
91    /// Decodes a variable integer from the buffer
92    ///
93    /// # Errors
94    ///
95    /// Returns an error if:
96    /// - The buffer doesn't contain enough bytes
97    /// - The encoded value exceeds the maximum
98    /// - More than 4 bytes are used (protocol violation)
99    pub fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
100        let mut value = 0u32;
101        let mut multiplier = 1u32;
102        let mut byte_count = 0;
103
104        loop {
105            if !buf.has_remaining() {
106                return Err(MqttError::MalformedPacket(
107                    "Insufficient bytes for variable integer".to_string(),
108                ));
109            }
110
111            byte_count += 1;
112            if byte_count > 4 {
113                return Err(MqttError::MalformedPacket(
114                    "Variable integer exceeds 4 bytes".to_string(),
115                ));
116            }
117
118            let byte = buf.get_u8();
119            value += u32::from(byte & 0x7F) * multiplier;
120
121            if (byte & 0x80) == 0 {
122                break;
123            }
124
125            multiplier *= 128;
126            if multiplier > 128 * 128 * 128 {
127                return Err(MqttError::MalformedPacket(
128                    "Variable integer overflow".to_string(),
129                ));
130            }
131        }
132
133        if value > VARIABLE_INT_MAX {
134            return Err(MqttError::MalformedPacket(format!(
135                "Variable integer value {value} exceeds maximum"
136            )));
137        }
138
139        Ok(Self { value })
140    }
141}
142
143impl fmt::Display for VariableInt {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "{}", self.value)
146    }
147}
148
149impl From<VariableInt> for u32 {
150    fn from(v: VariableInt) -> Self {
151        v.value
152    }
153}
154
155impl TryFrom<u32> for VariableInt {
156    type Error = MqttError;
157
158    fn try_from(value: u32) -> Result<Self> {
159        Self::new(value)
160    }
161}
162
163impl TryFrom<usize> for VariableInt {
164    type Error = MqttError;
165
166    fn try_from(value: usize) -> Result<Self> {
167        let value = u32::try_from(value).map_err(|_| {
168            MqttError::ProtocolError("Value too large for variable integer".to_string())
169        })?;
170        Self::new(value)
171    }
172}
173
174// BeBytes implementation
175impl BeBytes for VariableInt {
176    fn field_size() -> usize {
177        // Variable length, so we return 0 as it's determined at runtime
178        0
179    }
180
181    fn to_be_bytes(&self) -> Vec<u8> {
182        let mut buf = Vec::new();
183        let _ = self.encode(&mut buf); // Encoding can't fail for valid VariableInt
184        buf
185    }
186
187    fn try_from_be_bytes(
188        bytes: &[u8],
189    ) -> std::result::Result<(Self, usize), bebytes::BeBytesError> {
190        use bytes::Bytes;
191        let mut buf = Bytes::copy_from_slice(bytes);
192        let start_len = buf.len();
193
194        match Self::decode(&mut buf) {
195            Ok(var_int) => {
196                let consumed = start_len - buf.len();
197                Ok((var_int, consumed))
198            }
199            Err(_) => Err(bebytes::BeBytesError::InsufficientData {
200                expected: 1, // At least 1 byte expected for variable int
201                actual: bytes.len(),
202            }),
203        }
204    }
205
206    fn to_le_bytes(&self) -> Vec<u8> {
207        // MQTT uses big-endian encoding only
208        self.to_be_bytes()
209    }
210
211    fn try_from_le_bytes(
212        bytes: &[u8],
213    ) -> std::result::Result<(Self, usize), bebytes::BeBytesError> {
214        // MQTT uses big-endian encoding only
215        Self::try_from_be_bytes(bytes)
216    }
217}
218
219/// Encodes a u32 value as a variable byte integer (compatibility function)
220///
221/// This function provides compatibility with the old variable_byte module API.
222/// Prefer using `VariableInt::new(value)?.encode(buf)` for new code.
223///
224/// # Errors
225///
226/// Returns `MqttError::ProtocolError` if the value exceeds the maximum
227pub fn encode_variable_int<B: BufMut>(buf: &mut B, value: u32) -> Result<()> {
228    VariableInt::new(value)?.encode(buf)
229}
230
231/// Decodes a variable byte integer from the buffer (compatibility function)
232///
233/// This function provides compatibility with the old variable_byte module API.
234/// Prefer using `VariableInt::decode(buf)?.value()` for new code.
235///
236/// # Errors
237///
238/// Returns an error if decoding fails
239pub fn decode_variable_int<B: Buf>(buf: &mut B) -> Result<u32> {
240    Ok(VariableInt::decode(buf)?.value())
241}
242
243/// Calculates the number of bytes needed to encode a value (compatibility function)
244///
245/// This function provides compatibility with the old variable_byte module API.
246/// Prefer using `VariableInt::new(value)?.encoded_size()` for new code.
247#[must_use]
248pub fn variable_int_len(value: u32) -> usize {
249    VariableInt::new_unchecked(value.min(VARIABLE_INT_MAX)).encoded_size() as usize
250}
251
252/// Alias for `variable_int_len` for consistency (compatibility function)
253#[must_use]
254pub fn encoded_variable_int_len(value: u32) -> usize {
255    variable_int_len(value)
256}
257
258/// Maximum value that can be encoded as a variable byte integer (compatibility constant)
259pub const VARIABLE_BYTE_INT_MAX: u32 = VARIABLE_INT_MAX;
260
261#[cfg(test)]
262mod tests {
263    use super::*;
264    use bytes::BytesMut;
265
266    #[test]
267    fn test_new_valid_values() {
268        assert!(VariableInt::new(0).is_ok());
269        assert!(VariableInt::new(127).is_ok());
270        assert!(VariableInt::new(128).is_ok());
271        assert!(VariableInt::new(16_383).is_ok());
272        assert!(VariableInt::new(16_384).is_ok());
273        assert!(VariableInt::new(2_097_151).is_ok());
274        assert!(VariableInt::new(2_097_152).is_ok());
275        assert!(VariableInt::new(VARIABLE_INT_MAX).is_ok());
276    }
277
278    #[test]
279    fn test_new_invalid_values() {
280        assert!(VariableInt::new(VARIABLE_INT_MAX + 1).is_err());
281        assert!(VariableInt::new(u32::MAX).is_err());
282    }
283
284    #[test]
285    fn test_encoded_size() {
286        assert_eq!(VariableInt::new_unchecked(0).encoded_size(), 1);
287        assert_eq!(VariableInt::new_unchecked(127).encoded_size(), 1);
288        assert_eq!(VariableInt::new_unchecked(128).encoded_size(), 2);
289        assert_eq!(VariableInt::new_unchecked(16_383).encoded_size(), 2);
290        assert_eq!(VariableInt::new_unchecked(16_384).encoded_size(), 3);
291        assert_eq!(VariableInt::new_unchecked(2_097_151).encoded_size(), 3);
292        assert_eq!(VariableInt::new_unchecked(2_097_152).encoded_size(), 4);
293        assert_eq!(
294            VariableInt::new_unchecked(VARIABLE_INT_MAX).encoded_size(),
295            4
296        );
297    }
298
299    #[test]
300    fn test_encode_decode_single_byte() {
301        let mut buf = BytesMut::new();
302
303        for value in [0, 1, 64, 127] {
304            buf.clear();
305            let var_int = VariableInt::new(value).unwrap();
306            var_int.encode(&mut buf).unwrap();
307            assert_eq!(buf.len(), 1);
308
309            let decoded = VariableInt::decode(&mut buf).unwrap();
310            assert_eq!(decoded.value(), value);
311        }
312    }
313
314    #[test]
315    fn test_encode_decode_two_bytes() {
316        let mut buf = BytesMut::new();
317
318        for value in [128, 129, 321, 16_383] {
319            buf.clear();
320            let var_int = VariableInt::new(value).unwrap();
321            var_int.encode(&mut buf).unwrap();
322            assert_eq!(buf.len(), 2);
323
324            let decoded = VariableInt::decode(&mut buf).unwrap();
325            assert_eq!(decoded.value(), value);
326        }
327    }
328
329    #[test]
330    fn test_encode_decode_three_bytes() {
331        let mut buf = BytesMut::new();
332
333        for value in [16_384, 65_535, 2_097_151] {
334            buf.clear();
335            let var_int = VariableInt::new(value).unwrap();
336            var_int.encode(&mut buf).unwrap();
337            assert_eq!(buf.len(), 3);
338
339            let decoded = VariableInt::decode(&mut buf).unwrap();
340            assert_eq!(decoded.value(), value);
341        }
342    }
343
344    #[test]
345    fn test_encode_decode_four_bytes() {
346        let mut buf = BytesMut::new();
347
348        for value in [2_097_152, 10_000_000, VARIABLE_INT_MAX] {
349            buf.clear();
350            let var_int = VariableInt::new(value).unwrap();
351            var_int.encode(&mut buf).unwrap();
352            assert_eq!(buf.len(), 4);
353
354            let decoded = VariableInt::decode(&mut buf).unwrap();
355            assert_eq!(decoded.value(), value);
356        }
357    }
358
359    #[test]
360    fn test_mqtt_spec_examples() {
361        let mut buf = BytesMut::new();
362
363        // Example from MQTT spec: 64 decimal = 0x40
364        let var_int = VariableInt::new(64).unwrap();
365        var_int.encode(&mut buf).unwrap();
366        assert_eq!(buf[0], 0x40);
367
368        // Example: 321 decimal = 0xC1 0x02
369        buf.clear();
370        let var_int = VariableInt::new(321).unwrap();
371        var_int.encode(&mut buf).unwrap();
372        assert_eq!(buf[0], 0xC1);
373        assert_eq!(buf[1], 0x02);
374    }
375
376    #[test]
377    fn test_bebytes_integration() {
378        use bebytes::BeBytes;
379
380        // Test BeBytes to_be_bytes
381        let var_int = VariableInt::new(321).unwrap();
382        let bytes = var_int.to_be_bytes();
383        assert_eq!(bytes.len(), 2);
384        assert_eq!(bytes[0], 0xC1);
385        assert_eq!(bytes[1], 0x02);
386
387        // Test BeBytes try_from_be_bytes
388        let (decoded, consumed) = VariableInt::try_from_be_bytes(&bytes).unwrap();
389        assert_eq!(decoded.value(), 321);
390        assert_eq!(consumed, 2);
391
392        // Test field_size (static method)
393        assert_eq!(VariableInt::field_size(), 0); // Variable length
394    }
395
396    #[test]
397    fn test_decode_insufficient_bytes() {
398        let mut buf = BytesMut::new();
399        buf.put_u8(0x80); // Continuation bit set but no following byte
400
401        let result = VariableInt::decode(&mut buf);
402        assert!(result.is_err());
403    }
404
405    #[test]
406    fn test_decode_too_many_bytes() {
407        let mut buf = BytesMut::new();
408        // 5 bytes with continuation bits
409        buf.put_u8(0x80);
410        buf.put_u8(0x80);
411        buf.put_u8(0x80);
412        buf.put_u8(0x80);
413        buf.put_u8(0x01);
414
415        let result = VariableInt::decode(&mut buf);
416        assert!(result.is_err());
417    }
418
419    #[test]
420    fn test_conversions() {
421        let var_int = VariableInt::new(123).unwrap();
422
423        // Test From<VariableInt> for u32
424        let value: u32 = var_int.into();
425        assert_eq!(value, 123);
426
427        // Test TryFrom<u32> for VariableInt
428        let var_int2 = VariableInt::try_from(456u32).unwrap();
429        assert_eq!(var_int2.value(), 456);
430
431        // Test TryFrom<usize> for VariableInt
432        let var_int3 = VariableInt::try_from(789usize).unwrap();
433        assert_eq!(var_int3.value(), 789);
434
435        // Test invalid conversions
436        assert!(VariableInt::try_from(VARIABLE_INT_MAX + 1).is_err());
437    }
438
439    #[test]
440    fn test_display() {
441        let var_int = VariableInt::new(12345).unwrap();
442        assert_eq!(format!("{var_int}"), "12345");
443    }
444
445    #[cfg(test)]
446    mod property_tests {
447        use super::*;
448        use proptest::prelude::*;
449
450        proptest! {
451            #[test]
452            fn prop_round_trip(value in 0u32..=VARIABLE_INT_MAX) {
453                let mut buf = BytesMut::new();
454
455                let var_int = VariableInt::new(value).unwrap();
456                var_int.encode(&mut buf).unwrap();
457                let decoded = VariableInt::decode(&mut buf).unwrap();
458
459                prop_assert_eq!(decoded.value(), value);
460            }
461
462            #[test]
463            fn prop_encoded_size_matches_actual(value in 0u32..=VARIABLE_INT_MAX) {
464                let mut buf = BytesMut::new();
465
466                let var_int = VariableInt::new(value).unwrap();
467                let predicted_size = var_int.encoded_size();
468                var_int.encode(&mut buf).unwrap();
469                let actual_size = u32::try_from(buf.len()).expect("buffer size should fit in u32");
470
471                prop_assert_eq!(predicted_size, actual_size);
472            }
473
474            #[test]
475            fn prop_bebytes_round_trip(value in 0u32..=VARIABLE_INT_MAX) {
476                use bebytes::BeBytes;
477
478                let var_int = VariableInt::new(value).unwrap();
479                let bytes = var_int.to_be_bytes();
480                prop_assert_eq!(bytes.len(), var_int.encoded_size() as usize);
481
482                let (decoded, consumed) = VariableInt::try_from_be_bytes(&bytes).unwrap();
483                prop_assert_eq!(decoded.value(), value);
484                prop_assert_eq!(consumed, bytes.len());
485            }
486
487            #[test]
488            fn prop_invalid_values_rejected(value in (VARIABLE_INT_MAX + 1)..=u32::MAX) {
489                let result = VariableInt::new(value);
490                prop_assert!(result.is_err());
491            }
492
493            #[test]
494            fn prop_size_boundaries(value in 0u32..=VARIABLE_INT_MAX) {
495                let var_int = VariableInt::new(value).unwrap();
496                let size = var_int.encoded_size();
497
498                match value {
499                    0..=127 => prop_assert_eq!(size, 1),
500                    128..=16_383 => prop_assert_eq!(size, 2),
501                    16_384..=2_097_151 => prop_assert_eq!(size, 3),
502                    2_097_152..=VARIABLE_INT_MAX => prop_assert_eq!(size, 4),
503                    _ => unreachable!(),
504                }
505            }
506        }
507    }
508}