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