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}