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}