mqtt_protocol_core/mqtt/packet/
mqtt_binary.rs

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