mqtt_async_embedded/
util.rs

1//! # MQTT Serialization Utilities
2//!
3//! This module provides helper functions for reading and writing MQTT-specific data types
4//! from and to byte buffers, such as variable-byte integers and length-prefixed strings.
5
6use crate::error::{MqttError, ProtocolError};
7use crate::packet;
8use crate::transport;
9use heapless::Vec;
10
11/// Reads a variable-byte integer from the buffer, advancing the cursor.
12///
13/// This is a common encoding scheme in MQTT for packet lengths.
14pub fn read_variable_byte_integer(
15    cursor: &mut usize,
16    buf: &[u8],
17) -> Result<usize, MqttError<transport::ErrorPlaceHolder>> {
18    let mut multiplier = 1;
19    let mut value = 0;
20    let mut i = 0;
21    loop {
22        let encoded_byte = buf
23            .get(*cursor + i)
24            .ok_or(MqttError::Protocol(ProtocolError::MalformedPacket))?;
25        value += (encoded_byte & 127) as usize * multiplier;
26        if (encoded_byte & 128) == 0 {
27            break;
28        }
29        multiplier *= 128;
30        i += 1;
31        if i >= 4 {
32            return Err(MqttError::Protocol(ProtocolError::MalformedPacket));
33        }
34    }
35    *cursor += i + 1;
36    Ok(value)
37}
38
39/// Writes a variable-byte integer to the buffer, advancing the cursor.
40pub fn write_variable_byte_integer(
41    cursor: &mut usize,
42    buf: &mut [u8],
43    mut val: usize,
44) -> Result<(), MqttError<transport::ErrorPlaceHolder>> {
45    loop {
46        let mut encoded_byte = (val % 128) as u8;
47        val /= 128;
48        if val > 0 {
49            encoded_byte |= 128;
50        }
51        // CORRECTED: Dereference the `&mut u8` to assign the value directly.
52        *buf.get_mut(*cursor)
53            .ok_or(MqttError::BufferTooSmall)? = encoded_byte;
54        *cursor += 1;
55        if val == 0 {
56            break;
57        }
58    }
59    Ok(())
60}
61
62/// A simplified version of `write_variable_byte_integer` for external use that returns the byte count.
63pub fn write_variable_byte_integer_len(
64    buf: &mut [u8],
65    mut val: usize,
66) -> Result<usize, MqttError<transport::ErrorPlaceHolder>> {
67    let mut i = 0;
68    loop {
69        let mut encoded_byte = (val % 128) as u8;
70        val /= 128;
71        if val > 0 {
72            encoded_byte |= 128;
73        }
74        // CORRECTED: Dereference the `&mut u8` to assign the value directly.
75        *buf.get_mut(i).ok_or(MqttError::BufferTooSmall)? = encoded_byte;
76        i += 1;
77        if val == 0 {
78            break;
79        }
80    }
81    Ok(i)
82}
83
84/// Reads a UTF-8 encoded string (prefixed with a 2-byte length) from the buffer.
85pub fn read_utf8_string<'a>(
86    cursor: &mut usize,
87    buf: &'a [u8],
88) -> Result<&'a str, MqttError<transport::ErrorPlaceHolder>> {
89    let len = u16::from_be_bytes(
90        buf.get(*cursor..*cursor + 2)
91            .ok_or(MqttError::Protocol(ProtocolError::MalformedPacket))?
92            .try_into()
93            .unwrap(),
94    ) as usize;
95    *cursor += 2;
96    let s = core::str::from_utf8(
97        buf.get(*cursor..*cursor + len)
98            .ok_or(MqttError::Protocol(ProtocolError::MalformedPacket))?,
99    )
100        .map_err(|_| MqttError::Protocol(ProtocolError::InvalidUtf8String))?;
101    *cursor += len;
102    Ok(s)
103}
104
105/// Writes a UTF-8 encoded string (prefixed with a 2-byte length) to the buffer.
106pub fn write_utf8_string(
107    buf: &mut [u8],
108    s: &str,
109) -> Result<usize, MqttError<transport::ErrorPlaceHolder>> {
110    let len = s.len();
111    if len > u16::MAX as usize {
112        return Err(MqttError::Protocol(ProtocolError::PayloadTooLarge));
113    }
114    let len_bytes = (len as u16).to_be_bytes();
115
116    let required_space = 2 + len;
117    let slice = buf
118        .get_mut(0..required_space)
119        .ok_or(MqttError::BufferTooSmall)?;
120
121    slice[0..2].copy_from_slice(&len_bytes);
122    slice[2..].copy_from_slice(s.as_bytes());
123    Ok(required_space)
124}
125
126/// Reads MQTT v5 properties from the buffer.
127#[cfg(feature = "v5")]
128pub fn read_properties<'a>(
129    cursor: &mut usize,
130    buf: &'a [u8],
131) -> Result<Vec<packet::Property<'a>, 8>, MqttError<transport::ErrorPlaceHolder>> {
132    let mut properties = Vec::new();
133    let prop_len = read_variable_byte_integer(cursor, buf)?;
134    let prop_end = *cursor + prop_len;
135
136    while *cursor < prop_end {
137        let id = buf[*cursor];
138        *cursor += 1;
139        let data_start = *cursor;
140        // This is a simplified implementation. A real one would decode property data
141        // based on the specific property ID.
142        let data_len = 1; // Placeholder
143        *cursor += data_len;
144        properties
145            .push(packet::Property {
146                id,
147                data: &buf[data_start..data_start + data_len],
148            })
149            .map_err(|_| MqttError::Protocol(ProtocolError::TooManyProperties))?;
150    }
151    Ok(properties)
152}
153
154/// Writes MQTT v5 properties to the buffer.
155#[cfg(feature = "v5")]
156pub fn write_properties(
157    cursor: &mut usize,
158    buf: &mut [u8],
159    properties: &[packet::Property],
160) -> Result<(), MqttError<transport::ErrorPlaceHolder>> {
161    // This is a simplified implementation. A real one would calculate total length first.
162    let prop_len_cursor_start = *cursor;
163    *cursor += 1; // Reserve space for length
164
165    let props_start = *cursor;
166    for prop in properties {
167        buf[*cursor] = prop.id;
168        *cursor += 1;
169        buf[*cursor..*cursor + prop.data.len()].copy_from_slice(prop.data);
170        *cursor += prop.data.len();
171    }
172    let total_prop_len = *cursor - props_start;
173
174    // Write the actual property length
175    let mut temp_cursor = prop_len_cursor_start;
176    let _ = crate::util::write_variable_byte_integer(&mut temp_cursor, buf, total_prop_len)?;
177
178    Ok(())
179}
180