mqtt_protocol_core/mqtt/packet/
mqtt_binary.rs

1/**
2 * MIT License
3 *
4 * Copyright (c) 2025 Takatoshi Kondo
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24use crate::mqtt::result_code::MqttError;
25use alloc::{vec, vec::Vec};
26use core::convert::TryFrom;
27use serde::{Serialize, Serializer};
28#[cfg(feature = "std")]
29use std::io::IoSlice;
30
31/// MQTT Binary Data representation with pre-encoded byte buffer
32///
33/// This struct represents binary data as specified in the MQTT protocol specification.
34/// It efficiently stores binary data with a 2-byte length prefix, following the MQTT
35/// wire format for binary data fields.
36///
37/// The binary data is stored in a pre-encoded format internally, which includes:
38/// - 2 bytes for the length prefix (big-endian u16)
39/// - The actual binary data bytes
40///
41/// This approach provides several benefits:
42/// - Zero-copy serialization to network buffers
43/// - Efficient memory usage with single allocation
44/// - Guaranteed MQTT protocol compliance
45/// - Fast size calculations
46///
47/// # Size Limits
48///
49/// The maximum size of binary data is 65,535 bytes (2^16 - 1), as specified
50/// by the MQTT protocol. Attempting to create an `MqttBinary` with larger
51/// data will result in an error.
52///
53/// # Examples
54///
55/// ```ignore
56/// use mqtt_protocol_core::mqtt;
57///
58/// // Create binary data from a byte slice
59/// let data = b"hello world";
60/// let mqtt_binary = mqtt::packet::MqttBinary::new(data).unwrap();
61///
62/// // Access the binary data
63/// assert_eq!(mqtt_binary.as_slice(), b"hello world");
64/// assert_eq!(mqtt_binary.len(), 11);
65///
66/// // Get the complete encoded buffer (length prefix + data)
67/// let encoded = mqtt_binary.as_bytes();
68/// assert_eq!(encoded.len(), 13); // 2 bytes length + 11 bytes data
69/// ```
70#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
71pub struct MqttBinary {
72    /// Complete buffer containing length prefix (2 bytes) + binary data
73    encoded: Vec<u8>,
74}
75
76impl MqttBinary {
77    /// Create a new MqttBinary from binary data
78    ///
79    /// Creates an `MqttBinary` instance from the provided binary data.
80    /// The data is copied into an internal buffer with a 2-byte length prefix.
81    ///
82    /// # Parameters
83    ///
84    /// * `data` - Binary data to store. Can be any type that implements `AsRef<[u8]>`
85    ///   such as `&[u8]`, `Vec<u8>`, or `&str`
86    ///
87    /// # Returns
88    ///
89    /// * `Ok(MqttBinary)` - Successfully created binary data
90    /// * `Err(MqttError::MalformedPacket)` - If data length exceeds 65,535 bytes
91    ///
92    /// # Examples
93    ///
94    /// ```ignore
95    /// use mqtt_protocol_core::mqtt;
96    ///
97    /// // From byte slice
98    /// let binary = mqtt::packet::MqttBinary::new(b"hello").unwrap();
99    ///
100    /// // From Vec<u8>
101    /// let vec_data = vec![1, 2, 3, 4, 5];
102    /// let binary = mqtt::packet::MqttBinary::new(vec_data).unwrap();
103    ///
104    /// // From string (converted to bytes)
105    /// let binary = mqtt::packet::MqttBinary::new("text data").unwrap();
106    /// ```
107    pub fn new(data: impl AsRef<[u8]>) -> Result<Self, MqttError> {
108        let data_ref = data.as_ref();
109        if data_ref.len() > 65535 {
110            return Err(MqttError::MalformedPacket);
111        }
112        let len = data_ref.len() as u16;
113
114        let mut encoded = Vec::with_capacity(2 + data_ref.len());
115        encoded.push((len >> 8) as u8);
116        encoded.push(len as u8);
117        encoded.extend_from_slice(data_ref);
118
119        Ok(Self { encoded })
120    }
121
122    /// Get the complete encoded byte sequence including length prefix
123    ///
124    /// Returns the complete internal buffer, which includes the 2-byte length prefix
125    /// followed by the binary data. This is the exact format used in MQTT wire protocol.
126    ///
127    /// # Returns
128    ///
129    /// A byte slice containing [length_high, length_low, data...]
130    ///
131    /// # Examples
132    ///
133    /// ```ignore
134    /// use mqtt_protocol_core::mqtt;
135    ///
136    /// let binary = mqtt::packet::MqttBinary::new(b"hi").unwrap();
137    /// let bytes = binary.as_bytes();
138    /// assert_eq!(bytes, &[0x00, 0x02, b'h', b'i']);
139    /// ```
140    pub fn as_bytes(&self) -> &[u8] {
141        &self.encoded
142    }
143
144    /// Get only the binary data without the length prefix
145    ///
146    /// Returns a byte slice containing only the binary data portion,
147    /// excluding the 2-byte length prefix.
148    ///
149    /// # Returns
150    ///
151    /// A byte slice containing the raw binary data
152    ///
153    /// # Examples
154    ///
155    /// ```ignore
156    /// use mqtt_protocol_core::mqtt;
157    ///
158    /// let binary = mqtt::packet::MqttBinary::new(b"hello").unwrap();
159    /// assert_eq!(binary.as_slice(), b"hello");
160    /// ```
161    pub fn as_slice(&self) -> &[u8] {
162        &self.encoded[2..]
163    }
164
165    /// Get the length of the binary data in bytes
166    ///
167    /// Returns the number of bytes in the binary data portion only,
168    /// excluding the 2-byte length prefix.
169    ///
170    /// # Returns
171    ///
172    /// The length of the binary data in bytes
173    ///
174    /// # Examples
175    ///
176    /// ```ignore
177    /// use mqtt_protocol_core::mqtt;
178    ///
179    /// let binary = mqtt::packet::MqttBinary::new(b"hello").unwrap();
180    /// assert_eq!(binary.len(), 5);
181    ///
182    /// let empty = mqtt::packet::MqttBinary::new(b"").unwrap();
183    /// assert_eq!(empty.len(), 0);
184    /// ```
185    pub fn len(&self) -> usize {
186        self.encoded.len() - 2
187    }
188
189    /// Check if the binary data is empty
190    ///
191    /// Returns `true` if the binary data contains no bytes,
192    /// `false` otherwise.
193    ///
194    /// # Returns
195    ///
196    /// `true` if the binary data is empty, `false` otherwise
197    ///
198    /// # Examples
199    ///
200    /// ```ignore
201    /// use mqtt_protocol_core::mqtt;
202    ///
203    /// let empty = mqtt::packet::MqttBinary::new(b"").unwrap();
204    /// assert!(empty.is_empty());
205    ///
206    /// let data = mqtt::packet::MqttBinary::new(b"x").unwrap();
207    /// assert!(!data.is_empty());
208    /// ```
209    pub fn is_empty(&self) -> bool {
210        self.encoded.len() <= 2
211    }
212
213    /// Get the total encoded size including the length field
214    ///
215    /// Returns the total number of bytes in the encoded representation,
216    /// including the 2-byte length prefix and the binary data.
217    ///
218    /// # Returns
219    ///
220    /// The total size in bytes (length prefix + binary data)
221    ///
222    /// # Examples
223    ///
224    /// ```ignore
225    /// use mqtt_protocol_core::mqtt;
226    ///
227    /// let binary = mqtt::packet::MqttBinary::new(b"hello").unwrap();
228    /// assert_eq!(binary.size(), 7); // 2 bytes prefix + 5 bytes data
229    /// assert_eq!(binary.len(), 5);  // Only data length
230    /// ```
231    pub fn size(&self) -> usize {
232        self.encoded.len()
233    }
234
235    /// Create IoSlice buffers for efficient network I/O
236    ///
237    /// Returns a vector of `IoSlice` objects that can be used for vectored I/O
238    /// operations, allowing zero-copy writes to network sockets.
239    ///
240    /// # Returns
241    ///
242    /// A vector containing a single `IoSlice` referencing the complete encoded buffer
243    ///
244    /// # Examples
245    ///
246    /// ```ignore
247    /// use mqtt_protocol_core::mqtt;
248    /// use std::io::IoSlice;
249    ///
250    /// let binary = mqtt::packet::MqttBinary::new(b"data").unwrap();
251    /// let buffers = binary.to_buffers();
252    /// // Can be used with vectored write operations
253    /// // socket.write_vectored(&buffers)?;
254    /// ```
255    #[cfg(feature = "std")]
256    pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
257        vec![IoSlice::new(&self.encoded)]
258    }
259
260    /// Create a continuous buffer containing the complete packet data
261    ///
262    /// Returns a vector containing all packet bytes in a single continuous buffer.
263    /// This method is compatible with no-std environments and provides an alternative
264    /// to [`to_buffers()`] when vectored I/O is not needed.
265    ///
266    /// # Returns
267    ///
268    /// A vector containing the complete encoded buffer
269    ///
270    /// # Examples
271    ///
272    /// ```ignore
273    /// use mqtt_protocol_core::mqtt;
274    ///
275    /// let binary = mqtt::packet::MqttBinary::new(b"data").unwrap();
276    /// let buffer = binary.to_continuous_buffer();
277    /// // buffer contains all packet bytes
278    /// ```
279    ///
280    /// [`to_buffers()`]: #method.to_buffers
281    pub fn to_continuous_buffer(&self) -> Vec<u8> {
282        self.encoded.clone()
283    }
284
285    /// Parse binary data from a byte sequence
286    ///
287    /// Decodes MQTT binary data from a byte buffer according to the MQTT protocol.
288    /// The buffer must start with a 2-byte length prefix followed by the binary data.
289    ///
290    /// # Parameters
291    ///
292    /// * `data` - Byte buffer containing the encoded binary data
293    ///
294    /// # Returns
295    ///
296    /// * `Ok((MqttBinary, bytes_consumed))` - Successfully parsed binary data and number of bytes consumed
297    /// * `Err(MqttError::MalformedPacket)` - If the buffer is too short or malformed
298    ///
299    /// # Examples
300    ///
301    /// ```ignore
302    /// use mqtt_protocol_core::mqtt;
303    ///
304    /// // Buffer: [length_high, length_low, data...]
305    /// let buffer = &[0x00, 0x05, b'h', b'e', b'l', b'l', b'o'];
306    /// let (binary, consumed) = mqtt::packet::MqttBinary::decode(buffer).unwrap();
307    ///
308    /// assert_eq!(binary.as_slice(), b"hello");
309    /// assert_eq!(consumed, 7);
310    /// ```
311    pub fn decode(data: &[u8]) -> Result<(Self, usize), MqttError> {
312        if data.len() < 2 {
313            return Err(MqttError::MalformedPacket);
314        }
315
316        let data_len = ((data[0] as usize) << 8) | (data[1] as usize);
317        if data.len() < 2 + data_len {
318            return Err(MqttError::MalformedPacket);
319        }
320
321        // Create encoded buffer
322        let mut encoded = Vec::with_capacity(2 + data_len);
323        encoded.extend_from_slice(&data[0..2 + data_len]);
324
325        Ok((Self { encoded }, 2 + data_len))
326    }
327}
328
329/// Implementation of `AsRef<[u8]>` for `MqttBinary`
330///
331/// Returns the binary data portion (without length prefix) when the
332/// `MqttBinary` is used in contexts expecting a byte slice reference.
333impl AsRef<[u8]> for MqttBinary {
334    fn as_ref(&self) -> &[u8] {
335        self.as_slice()
336    }
337}
338
339/// Implementation of `Deref` for `MqttBinary`
340///
341/// Allows `MqttBinary` to be used directly as a byte slice in many contexts
342/// through automatic dereferencing. The dereferenced value is the binary data
343/// without the length prefix.
344impl core::ops::Deref for MqttBinary {
345    type Target = [u8];
346
347    fn deref(&self) -> &Self::Target {
348        self.as_slice()
349    }
350}
351
352/// Implementation of `Serialize` for `MqttBinary`
353///
354/// Serializes the binary data portion (without length prefix) as bytes.
355/// This is useful for JSON serialization and other serialization formats.
356impl Serialize for MqttBinary {
357    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
358    where
359        S: Serializer,
360    {
361        serializer.serialize_bytes(self.as_slice())
362    }
363}
364
365/// Implementation of `TryFrom<&str>` for `MqttBinary`
366///
367/// Converts a string slice to `MqttBinary` by converting the string to UTF-8 bytes.
368/// This is useful when you need to store text data as binary in MQTT packets.
369impl TryFrom<&str> for MqttBinary {
370    type Error = MqttError;
371
372    fn try_from(s: &str) -> Result<Self, Self::Error> {
373        Self::new(s.as_bytes())
374    }
375}
376
377/// Implementation of `Default` for `MqttBinary`
378///
379/// Creates an empty `MqttBinary` with zero-length binary data.
380/// The internal buffer contains only the 2-byte length prefix (0x00, 0x00).
381impl Default for MqttBinary {
382    fn default() -> Self {
383        MqttBinary {
384            encoded: vec![0x00, 0x00],
385        }
386    }
387}