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