mqtt_protocol_core/mqtt/arc_payload.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 serde::{Serialize, Serializer};
25use std::sync::Arc;
26
27/// A reference-counted byte payload with slice semantics
28///
29/// `ArcPayload` provides an efficient way to handle byte data by using `Arc<[u8]>`
30/// for reference counting, combined with offset and length information to represent
31/// a slice view of the underlying data. This allows for zero-copy sharing of payload
32/// data across multiple consumers while maintaining slice-like semantics.
33#[derive(Clone, Debug)]
34pub struct ArcPayload {
35 data: Arc<[u8]>,
36 start: usize,
37 length: usize,
38}
39
40impl PartialEq for ArcPayload {
41 fn eq(&self, other: &Self) -> bool {
42 self.as_slice() == other.as_slice()
43 }
44}
45
46impl Eq for ArcPayload {}
47
48impl Serialize for ArcPayload {
49 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
50 where
51 S: Serializer,
52 {
53 self.as_slice().serialize(serializer)
54 }
55}
56
57impl ArcPayload {
58 /// Create a new `ArcPayload` from reference-counted data with specified range
59 ///
60 /// Creates a new payload that represents a slice view of the provided `Arc<[u8]>` data
61 /// starting at the specified offset with the given length.
62 ///
63 /// # Parameters
64 ///
65 /// * `data` - The reference-counted byte data
66 /// * `start` - The starting offset within the data
67 /// * `length` - The length of the payload slice
68 ///
69 /// # Panics
70 ///
71 /// Panics in debug mode if `start + length > data.len()` (payload out of bounds)
72 ///
73 /// # Examples
74 ///
75 /// ```ignore
76 /// use std::sync::Arc;
77 /// use mqtt_protocol_core::mqtt::ArcPayload;
78 ///
79 /// let data = Arc::from(&b"hello world"[..]);
80 /// let payload = ArcPayload::new(data, 0, 5); // "hello"
81 /// ```
82 pub fn new(data: Arc<[u8]>, start: usize, length: usize) -> Self {
83 debug_assert!(start + length <= data.len(), "payload out of bounds",);
84 Self {
85 data,
86 start,
87 length,
88 }
89 }
90
91 /// Get a slice view of the payload data
92 ///
93 /// Returns a byte slice representing the payload data within the specified range.
94 ///
95 /// # Returns
96 ///
97 /// A `&[u8]` slice of the payload data
98 pub fn as_slice(&self) -> &[u8] {
99 &self.data[self.start..self.start + self.length]
100 }
101
102 /// Get the length of the payload
103 ///
104 /// Returns the number of bytes in the payload slice.
105 ///
106 /// # Returns
107 ///
108 /// The length of the payload in bytes
109 pub fn len(&self) -> usize {
110 self.length
111 }
112
113 /// Check if the payload is empty
114 ///
115 /// Returns `true` if the payload contains no bytes.
116 ///
117 /// # Returns
118 ///
119 /// `true` if the payload length is zero, `false` otherwise
120 pub fn is_empty(&self) -> bool {
121 self.length == 0
122 }
123
124 /// Get a reference to the underlying `Arc<[u8]>` data
125 ///
126 /// Returns a reference to the reference-counted byte array that contains
127 /// the actual data. This provides access to the full underlying data,
128 /// not just the slice view represented by this payload.
129 ///
130 /// # Returns
131 ///
132 /// A reference to the underlying `Arc<[u8]>` data
133 pub fn arc_data(&self) -> &Arc<[u8]> {
134 &self.data
135 }
136}
137
138impl Default for ArcPayload {
139 fn default() -> Self {
140 ArcPayload {
141 data: Arc::from(&[] as &[u8]),
142 start: 0,
143 length: 0,
144 }
145 }
146}
147
148/// Trait for converting various types into `ArcPayload`
149///
150/// This trait provides a uniform interface for converting different data types
151/// into `ArcPayload` instances. It allows for convenient creation of payloads
152/// from common types like strings, byte slices, vectors, and arrays.
153pub trait IntoPayload {
154 /// Convert the value into an `ArcPayload`
155 ///
156 /// # Returns
157 ///
158 /// An `ArcPayload` containing the converted data
159 fn into_payload(self) -> ArcPayload;
160}
161
162/// Convert a string slice (`&str`) into an `ArcPayload`
163impl IntoPayload for &str {
164 fn into_payload(self) -> ArcPayload {
165 let bytes = self.as_bytes();
166 ArcPayload::new(Arc::from(bytes), 0, bytes.len())
167 }
168}
169
170/// Convert an owned string (`String`) into an `ArcPayload`
171impl IntoPayload for String {
172 fn into_payload(self) -> ArcPayload {
173 let bytes = self.as_bytes();
174 ArcPayload::new(Arc::from(bytes), 0, bytes.len())
175 }
176}
177
178/// Convert a byte slice (`&[u8]`) into an `ArcPayload`
179impl IntoPayload for &[u8] {
180 fn into_payload(self) -> ArcPayload {
181 ArcPayload::new(Arc::from(self), 0, self.len())
182 }
183}
184
185/// Convert an owned byte vector (`Vec<u8>`) into an `ArcPayload`
186impl IntoPayload for Vec<u8> {
187 fn into_payload(self) -> ArcPayload {
188 let len = self.len();
189 ArcPayload::new(Arc::from(self), 0, len)
190 }
191}
192
193/// Convert a reference to a byte vector (`&Vec<u8>`) into an `ArcPayload`
194impl IntoPayload for &Vec<u8> {
195 fn into_payload(self) -> ArcPayload {
196 let slice: &[u8] = self.as_slice();
197 ArcPayload::new(Arc::from(slice), 0, slice.len())
198 }
199}
200
201/// Convert a reference to a byte array (`&[u8; N]`) into an `ArcPayload`
202impl<const N: usize> IntoPayload for &[u8; N] {
203 fn into_payload(self) -> ArcPayload {
204 let slice: &[u8] = self.as_slice();
205 ArcPayload::new(Arc::from(slice), 0, slice.len())
206 }
207}
208
209/// Convert an `Arc<[u8]>` directly into an `ArcPayload`
210impl IntoPayload for Arc<[u8]> {
211 fn into_payload(self) -> ArcPayload {
212 let len = self.len();
213 ArcPayload::new(self, 0, len)
214 }
215}
216
217/// Convert unit type (`()`) into an empty `ArcPayload`
218impl IntoPayload for () {
219 fn into_payload(self) -> ArcPayload {
220 ArcPayload::default() // Empty payload
221 }
222}
223
224/// Identity conversion for `ArcPayload` (no-op)
225impl IntoPayload for ArcPayload {
226 fn into_payload(self) -> ArcPayload {
227 self
228 }
229}