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}