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 serde::{Serialize, Serializer};
26use std::convert::TryFrom;
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 pub fn to_buffers(&self) -> Vec<IoSlice<'_>> {
254 vec![IoSlice::new(&self.encoded)]
255 }
256
257 /// Parse binary data from a byte sequence
258 ///
259 /// Decodes MQTT binary data from a byte buffer according to the MQTT protocol.
260 /// The buffer must start with a 2-byte length prefix followed by the binary data.
261 ///
262 /// # Parameters
263 ///
264 /// * `data` - Byte buffer containing the encoded binary data
265 ///
266 /// # Returns
267 ///
268 /// * `Ok((MqttBinary, bytes_consumed))` - Successfully parsed binary data and number of bytes consumed
269 /// * `Err(MqttError::MalformedPacket)` - If the buffer is too short or malformed
270 ///
271 /// # Examples
272 ///
273 /// ```ignore
274 /// use mqtt_protocol_core::mqtt;
275 ///
276 /// // Buffer: [length_high, length_low, data...]
277 /// let buffer = &[0x00, 0x05, b'h', b'e', b'l', b'l', b'o'];
278 /// let (binary, consumed) = mqtt::packet::MqttBinary::decode(buffer).unwrap();
279 ///
280 /// assert_eq!(binary.as_slice(), b"hello");
281 /// assert_eq!(consumed, 7);
282 /// ```
283 pub fn decode(data: &[u8]) -> Result<(Self, usize), MqttError> {
284 if data.len() < 2 {
285 return Err(MqttError::MalformedPacket);
286 }
287
288 let data_len = ((data[0] as usize) << 8) | (data[1] as usize);
289 if data.len() < 2 + data_len {
290 return Err(MqttError::MalformedPacket);
291 }
292
293 // Create encoded buffer
294 let mut encoded = Vec::with_capacity(2 + data_len);
295 encoded.extend_from_slice(&data[0..2 + data_len]);
296
297 Ok((Self { encoded }, 2 + data_len))
298 }
299}
300
301/// Implementation of `AsRef<[u8]>` for `MqttBinary`
302///
303/// Returns the binary data portion (without length prefix) when the
304/// `MqttBinary` is used in contexts expecting a byte slice reference.
305impl AsRef<[u8]> for MqttBinary {
306 fn as_ref(&self) -> &[u8] {
307 self.as_slice()
308 }
309}
310
311/// Implementation of `Deref` for `MqttBinary`
312///
313/// Allows `MqttBinary` to be used directly as a byte slice in many contexts
314/// through automatic dereferencing. The dereferenced value is the binary data
315/// without the length prefix.
316impl std::ops::Deref for MqttBinary {
317 type Target = [u8];
318
319 fn deref(&self) -> &Self::Target {
320 self.as_slice()
321 }
322}
323
324/// Implementation of `Serialize` for `MqttBinary`
325///
326/// Serializes the binary data portion (without length prefix) as bytes.
327/// This is useful for JSON serialization and other serialization formats.
328impl Serialize for MqttBinary {
329 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
330 where
331 S: Serializer,
332 {
333 serializer.serialize_bytes(self.as_slice())
334 }
335}
336
337/// Implementation of `TryFrom<&str>` for `MqttBinary`
338///
339/// Converts a string slice to `MqttBinary` by converting the string to UTF-8 bytes.
340/// This is useful when you need to store text data as binary in MQTT packets.
341impl TryFrom<&str> for MqttBinary {
342 type Error = MqttError;
343
344 fn try_from(s: &str) -> Result<Self, Self::Error> {
345 Self::new(s.as_bytes())
346 }
347}
348
349/// Implementation of `Default` for `MqttBinary`
350///
351/// Creates an empty `MqttBinary` with zero-length binary data.
352/// The internal buffer contains only the 2-byte length prefix (0x00, 0x00).
353impl Default for MqttBinary {
354 fn default() -> Self {
355 MqttBinary {
356 encoded: vec![0x00, 0x00],
357 }
358 }
359}