mqtt_protocol_core/mqtt/common/cursor.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 */
24
25/// Errors that can occur when reading from a cursor
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum CursorError {
28 /// Attempted to read beyond the end of the data
29 UnexpectedEof,
30}
31
32/// A cursor which wraps an in-memory buffer and provides positioned reading
33///
34/// `Cursor` is a simple wrapper that allows reading from various in-memory
35/// data types (like `&[u8]`) with a tracked position. This is useful for
36/// parsing protocols where you need to read sequentially through a buffer.
37///
38/// Unlike `std::io::Cursor`, this implementation is designed for `no_std`
39/// environments and focuses on reading operations only.
40///
41/// # Examples
42///
43/// ```
44/// use mqtt_protocol_core::mqtt::common::Cursor;
45///
46/// let data = &b"hello world"[..];
47/// let mut cursor = Cursor::new(data);
48///
49/// // Read individual bytes
50/// assert_eq!(cursor.read_u8(), Some(b'h'));
51/// assert_eq!(cursor.position(), 1);
52///
53/// // Read multiple bytes at once
54/// let chunk = cursor.read_bytes(5).unwrap();
55/// assert_eq!(chunk, b"ello ");
56/// assert_eq!(cursor.position(), 6);
57/// ```
58pub struct Cursor<T> {
59 inner: T,
60 pos: u64,
61}
62
63impl<T> Cursor<T> {
64 /// Creates a new cursor with the provided data
65 ///
66 /// The cursor starts at position 0.
67 ///
68 /// # Examples
69 ///
70 /// ```
71 /// use mqtt_protocol_core::mqtt::common::Cursor;
72 ///
73 /// let cursor = Cursor::new(&b"hello"[..]);
74 /// assert_eq!(cursor.position(), 0);
75 /// ```
76 #[inline]
77 pub fn new(inner: T) -> Self {
78 Cursor { inner, pos: 0 }
79 }
80
81 /// Returns the current position of the cursor
82 ///
83 /// # Returns
84 ///
85 /// The current position as a `u64` value
86 ///
87 /// # Examples
88 ///
89 /// ```
90 /// use mqtt_protocol_core::mqtt::common::Cursor;
91 ///
92 /// let mut cursor = Cursor::new(&b"hello"[..]);
93 /// assert_eq!(cursor.position(), 0);
94 ///
95 /// cursor.set_position(3);
96 /// assert_eq!(cursor.position(), 3);
97 /// ```
98 #[inline]
99 pub fn position(&self) -> u64 {
100 self.pos
101 }
102
103 /// Sets the position of the cursor
104 ///
105 /// # Parameters
106 ///
107 /// * `pos` - New position for the cursor
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// use mqtt_protocol_core::mqtt::common::Cursor;
113 ///
114 /// let mut cursor = Cursor::new(&b"hello"[..]);
115 /// cursor.set_position(3);
116 /// assert_eq!(cursor.position(), 3);
117 /// ```
118 #[inline]
119 pub fn set_position(&mut self, pos: u64) {
120 self.pos = pos;
121 }
122
123 /// Gets a reference to the underlying value
124 ///
125 /// # Returns
126 ///
127 /// A reference to the underlying data of type `&T`
128 ///
129 /// # Examples
130 ///
131 /// ```
132 /// use mqtt_protocol_core::mqtt::common::Cursor;
133 ///
134 /// let data = &b"hello"[..];
135 /// let cursor = Cursor::new(data);
136 /// assert_eq!(cursor.get_ref(), &data);
137 /// ```
138 #[inline]
139 pub fn get_ref(&self) -> &T {
140 &self.inner
141 }
142}
143
144impl Cursor<&[u8]> {
145 /// Returns a slice of the remaining unread data
146 ///
147 /// This returns all data from the current position to the end of the buffer.
148 /// If the position is beyond the buffer length, returns an empty slice.
149 ///
150 /// # Returns
151 ///
152 /// * `&[u8]` - Slice containing all unread data from current position to end
153 ///
154 /// # Examples
155 ///
156 /// ```
157 /// use mqtt_protocol_core::mqtt::common::Cursor;
158 ///
159 /// let mut cursor = Cursor::new(&b"hello"[..]);
160 /// cursor.set_position(2);
161 /// assert_eq!(cursor.remaining_slice(), b"llo");
162 /// ```
163 #[inline]
164 pub fn remaining_slice(&self) -> &[u8] {
165 let pos = self.pos as usize;
166 if pos <= self.inner.len() {
167 &self.inner[pos..]
168 } else {
169 &[]
170 }
171 }
172
173 /// Reads exactly `count` bytes from the cursor
174 ///
175 /// Advances the cursor position by `count` bytes and returns a slice
176 /// to the read data. Returns `None` if there are not enough bytes
177 /// remaining to satisfy the request.
178 ///
179 /// # Parameters
180 ///
181 /// * `count` - Number of bytes to read
182 ///
183 /// # Returns
184 ///
185 /// * `Some(&[u8])` - Slice containing exactly `count` bytes
186 /// * `None` - Not enough data available to read `count` bytes
187 ///
188 /// # Examples
189 ///
190 /// ```
191 /// use mqtt_protocol_core::mqtt::common::Cursor;
192 ///
193 /// let mut cursor = Cursor::new(&b"hello world"[..]);
194 /// assert_eq!(cursor.read_bytes(5), Some(&b"hello"[..]));
195 /// assert_eq!(cursor.position(), 5);
196 /// assert_eq!(cursor.read_bytes(20), None); // Not enough data
197 /// ```
198 #[inline]
199 pub fn read_bytes(&mut self, count: usize) -> Option<&[u8]> {
200 let pos = self.pos as usize;
201 if pos.saturating_add(count) <= self.inner.len() {
202 let data = &self.inner[pos..pos + count];
203 self.pos += count as u64;
204 Some(data)
205 } else {
206 None
207 }
208 }
209
210 /// Reads a single byte from the cursor
211 ///
212 /// Advances the cursor position by 1 byte and returns the byte value.
213 /// Returns `None` if there are no more bytes to read.
214 ///
215 /// # Returns
216 ///
217 /// * `Some(u8)` - The next byte from the cursor
218 /// * `None` - No more bytes available to read
219 ///
220 /// # Examples
221 ///
222 /// ```
223 /// use mqtt_protocol_core::mqtt::common::Cursor;
224 ///
225 /// let mut cursor = Cursor::new(&b"hi"[..]);
226 /// assert_eq!(cursor.read_u8(), Some(b'h'));
227 /// assert_eq!(cursor.read_u8(), Some(b'i'));
228 /// assert_eq!(cursor.read_u8(), None); // End of data
229 /// ```
230 #[inline]
231 pub fn read_u8(&mut self) -> Option<u8> {
232 let pos = self.pos as usize;
233 if pos < self.inner.len() {
234 let val = self.inner[pos];
235 self.pos += 1;
236 Some(val)
237 } else {
238 None
239 }
240 }
241}
242
243impl<T: AsRef<[u8]>> Cursor<T> {
244 /// Pulls some bytes from this cursor into the specified buffer
245 ///
246 /// This method is compatible with `std::io::Read::read()`. It reads at most
247 /// `buf.len()` bytes from the cursor into the provided buffer, advancing
248 /// the cursor position accordingly.
249 ///
250 /// # Parameters
251 ///
252 /// * `buf` - Buffer to read data into
253 ///
254 /// # Returns
255 ///
256 /// * `Ok(n)` - Number of bytes read (0 to `buf.len()`)
257 ///
258 /// This method currently never returns an error, but returns a `Result`
259 /// for compatibility with `std::io::Read::read()`.
260 ///
261 /// # Examples
262 ///
263 /// ```
264 /// use mqtt_protocol_core::mqtt::common::Cursor;
265 ///
266 /// let mut cursor = Cursor::new(&b"hello world"[..]);
267 /// let mut buf = [0u8; 5];
268 /// let n = cursor.read(&mut buf).unwrap();
269 /// assert_eq!(n, 5);
270 /// assert_eq!(&buf, b"hello");
271 /// assert_eq!(cursor.position(), 5);
272 /// ```
273 #[inline]
274 pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, CursorError> {
275 let pos = self.pos as usize;
276 let available = self.inner.as_ref().len().saturating_sub(pos);
277 let to_read = core::cmp::min(buf.len(), available);
278
279 if to_read > 0 {
280 buf[..to_read].copy_from_slice(&self.inner.as_ref()[pos..pos + to_read]);
281 self.pos += to_read as u64;
282 }
283
284 Ok(to_read)
285 }
286
287 /// Reads the exact number of bytes required to fill `buf`
288 ///
289 /// This method is compatible with `std::io::Read::read_exact()`. It reads
290 /// exactly `buf.len()` bytes from the cursor into the buffer, or returns
291 /// an error if not enough data is available.
292 ///
293 /// # Parameters
294 ///
295 /// * `buf` - Buffer to read data into (must be completely filled)
296 ///
297 /// # Returns
298 ///
299 /// * `Ok(())` - Successfully read exactly `buf.len()` bytes
300 /// * `Err(CursorError::UnexpectedEof)` - Not enough data available
301 ///
302 /// # Examples
303 ///
304 /// ```
305 /// use mqtt_protocol_core::mqtt::common::{Cursor, CursorError};
306 ///
307 /// let mut cursor = Cursor::new(&b"hello"[..]);
308 /// let mut buf = [0u8; 3];
309 /// cursor.read_exact(&mut buf).unwrap();
310 /// assert_eq!(&buf, b"hel");
311 ///
312 /// // Trying to read more than available
313 /// let mut buf2 = [0u8; 10];
314 /// assert_eq!(cursor.read_exact(&mut buf2), Err(CursorError::UnexpectedEof));
315 /// ```
316 #[inline]
317 pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), CursorError> {
318 let pos = self.pos as usize;
319 let available = self.inner.as_ref().len().saturating_sub(pos);
320
321 if available < buf.len() {
322 return Err(CursorError::UnexpectedEof);
323 }
324
325 buf.copy_from_slice(&self.inner.as_ref()[pos..pos + buf.len()]);
326 self.pos += buf.len() as u64;
327 Ok(())
328 }
329}