embedded_io_cursor/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![warn(missing_docs)]
4#![doc = include_str!("../README.md")]
5
6//! A `no_std`-compatible Cursor implementation designed for use with `embedded-io`.
7//!
8//! This crate provides a `Cursor` type that wraps an in-memory buffer and implements
9//! the `embedded-io` traits (`Read`, `Write`, `BufRead`, and `Seek`). It serves as
10//! the `embedded-io` ecosystem's equivalent to `std::io::Cursor`, optimized for
11//! embedded and `no_std` environments while maintaining API compatibility where possible.
12
13use core::cmp;
14use embedded_io::{BufRead, Error, ErrorKind, ErrorType, Read, Seek, SeekFrom, Write};
15
16#[cfg(feature = "alloc")]
17extern crate alloc;
18
19#[cfg(feature = "alloc")]
20use alloc::{boxed::Box, vec::Vec};
21
22/// A `Cursor` wraps an in-memory buffer and provides `embedded-io` trait implementations.
23///
24/// This is the `embedded-io` equivalent of `std::io::Cursor`, designed for
25/// `no_std` environments. It implements the `embedded-io` traits (`Read`, `Write`,
26/// `BufRead`, and `Seek`) for various buffer types.
27///
28/// `Cursor`s are used with in-memory buffers (anything implementing [`AsRef<[u8]>`])
29/// to allow them to implement `embedded-io` traits, making these buffers usable
30/// anywhere you need an `embedded-io` reader, writer, or seeker.
31///
32/// Supported buffer types include `&[u8]`, `[u8; N]`, `&mut [u8]`, and with the
33/// `alloc` feature: `Vec<u8>` and `Box<[u8]>`.
34///
35/// # Examples
36///
37/// Examples of using the Cursor for writing and reading can be found in the comprehensive
38/// test suite in the `tests/` directory.
39///
40/// Reading from a buffer using `embedded-io` traits:
41///
42/// ```
43/// use embedded_io::Read;
44/// use embedded_io_cursor::Cursor;
45///
46/// let mut cursor = Cursor::new(&b"hello world"[..]);
47/// let mut buf = [0u8; 5];
48/// cursor.read_exact(&mut buf).unwrap();
49/// assert_eq!(&buf, b"hello");
50/// ```
51#[derive(Debug, Default, Eq, PartialEq)]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53pub struct Cursor<T> {
54    inner: T,
55    pos: u64,
56}
57
58impl<T> Cursor<T> {
59    /// Creates a new cursor wrapping the provided underlying in-memory buffer.
60    ///
61    /// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`)
62    /// is not empty. So writing to cursor starts with overwriting `Vec`
63    /// content, not with appending to it.
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use embedded_io_cursor::Cursor;
69    ///
70    /// let cursor = Cursor::new(Vec::<u8>::new());
71    /// # let _ = cursor;
72    /// ```
73    pub const fn new(inner: T) -> Cursor<T> {
74        Cursor { pos: 0, inner }
75    }
76
77    /// Consumes this cursor, returning the underlying value.
78    ///
79    /// # Examples
80    ///
81    /// ```
82    /// use embedded_io_cursor::Cursor;
83    ///
84    /// let buff = Cursor::new(Vec::<u8>::new());
85    /// let vec = buff.into_inner();
86    /// ```
87    pub fn into_inner(self) -> T {
88        self.inner
89    }
90
91    /// Gets a reference to the underlying value in this cursor.
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// use embedded_io_cursor::Cursor;
97    ///
98    /// let buff = Cursor::new(Vec::<u8>::new());
99    /// let reference = buff.get_ref();
100    /// ```
101    pub const fn get_ref(&self) -> &T {
102        &self.inner
103    }
104
105    /// Gets a mutable reference to the underlying value in this cursor.
106    ///
107    /// Care should be taken to avoid modifying the internal I/O state of the
108    /// underlying value as it may corrupt this cursor's position.
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// use embedded_io_cursor::Cursor;
114    ///
115    /// let mut buff = Cursor::new(Vec::<u8>::new());
116    /// let reference = buff.get_mut();
117    /// ```
118    pub fn get_mut(&mut self) -> &mut T {
119        &mut self.inner
120    }
121
122    /// Returns the current position of this cursor.
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// use embedded_io::{Seek, SeekFrom};
128    /// use embedded_io_cursor::Cursor;
129    ///
130    /// let mut buff = Cursor::new(&[1u8, 2, 3, 4, 5][..]);
131    /// assert_eq!(buff.position(), 0);
132    ///
133    /// buff.seek(SeekFrom::Current(2)).unwrap();
134    /// assert_eq!(buff.position(), 2);
135    ///
136    /// buff.seek(SeekFrom::Current(-1)).unwrap();
137    /// assert_eq!(buff.position(), 1);
138    /// ```
139    pub const fn position(&self) -> u64 {
140        self.pos
141    }
142
143    /// Sets the position of this cursor.
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// use embedded_io_cursor::Cursor;
149    ///
150    /// let mut buff = Cursor::new(&[1u8, 2, 3, 4, 5][..]);
151    /// assert_eq!(buff.position(), 0);
152    ///
153    /// buff.set_position(2);
154    /// assert_eq!(buff.position(), 2);
155    ///
156    /// buff.set_position(4);
157    /// assert_eq!(buff.position(), 4);
158    /// ```
159    pub fn set_position(&mut self, pos: u64) {
160        self.pos = pos;
161    }
162}
163
164impl<T> Cursor<T>
165where
166    T: AsRef<[u8]>,
167{
168    /// Returns the remaining slice from the current position.
169    ///
170    /// This method returns the portion of the underlying buffer that
171    /// can still be read from the current cursor position.
172    ///
173    /// # Examples
174    ///
175    /// ```
176    /// use embedded_io_cursor::Cursor;
177    ///
178    /// let mut cursor = Cursor::new(&[1u8, 2, 3, 4, 5][..]);
179    /// assert_eq!(cursor.remaining_slice(), &[1, 2, 3, 4, 5]);
180    ///
181    /// cursor.set_position(2);
182    /// assert_eq!(cursor.remaining_slice(), &[3, 4, 5]);
183    ///
184    /// cursor.set_position(10);
185    /// assert_eq!(cursor.remaining_slice(), &[]);
186    /// ```
187    pub fn remaining_slice(&self) -> &[u8] {
188        let pos = cmp::min(self.pos, self.inner.as_ref().len() as u64) as usize;
189        &self.inner.as_ref()[pos..]
190    }
191
192    /// Returns `true` if there are no more bytes to read from the cursor.
193    ///
194    /// This is equivalent to checking if `remaining_slice().is_empty()`.
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// use embedded_io_cursor::Cursor;
200    ///
201    /// let mut cursor = Cursor::new(&[1u8, 2, 3][..]);
202    /// assert!(!cursor.is_empty());
203    ///
204    /// cursor.set_position(3);
205    /// assert!(cursor.is_empty());
206    ///
207    /// cursor.set_position(10);
208    /// assert!(cursor.is_empty());
209    /// ```
210    pub fn is_empty(&self) -> bool {
211        self.pos >= self.inner.as_ref().len() as u64
212    }
213}
214
215impl<T> Clone for Cursor<T>
216where
217    T: Clone,
218{
219    #[inline]
220    fn clone(&self) -> Self {
221        Cursor {
222            inner: self.inner.clone(),
223            pos: self.pos,
224        }
225    }
226
227    #[inline]
228    fn clone_from(&mut self, other: &Self) {
229        self.inner.clone_from(&other.inner);
230        self.pos = other.pos;
231    }
232}
233
234impl<T> ErrorType for Cursor<T> {
235    type Error = ErrorKind;
236}
237
238// Read implementation for AsRef<[u8]> types
239impl<T> Read for Cursor<T>
240where
241    T: AsRef<[u8]>,
242{
243    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
244        let remaining = self.remaining_slice();
245        let n = cmp::min(buf.len(), remaining.len());
246
247        if n > 0 {
248            buf[..n].copy_from_slice(&remaining[..n]);
249        }
250
251        self.pos += n as u64;
252        Ok(n)
253    }
254}
255
256// BufRead implementation for AsRef<[u8]> types
257impl<T> BufRead for Cursor<T>
258where
259    T: AsRef<[u8]>,
260{
261    fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
262        Ok(self.remaining_slice())
263    }
264
265    fn consume(&mut self, amt: usize) {
266        self.pos += amt as u64;
267    }
268}
269
270// Seek implementation for AsRef<[u8]> types
271impl<T> Seek for Cursor<T>
272where
273    T: AsRef<[u8]>,
274{
275    fn seek(&mut self, style: SeekFrom) -> Result<u64, Self::Error> {
276        let (base_pos, offset) = match style {
277            SeekFrom::Start(n) => {
278                self.pos = n;
279                return Ok(n);
280            }
281            SeekFrom::End(n) => (self.inner.as_ref().len() as u64, n),
282            SeekFrom::Current(n) => (self.pos, n),
283        };
284
285        match base_pos.checked_add_signed(offset) {
286            Some(n) => {
287                self.pos = n;
288                Ok(self.pos)
289            }
290            None => Err(ErrorKind::InvalidInput),
291        }
292    }
293}
294
295/// Helper function for writing to fixed-size slices
296fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> Result<usize, ErrorKind> {
297    let pos = cmp::min(*pos_mut, slice.len() as u64) as usize;
298    let amt = (&mut slice[pos..]).write(buf).map_err(|err| err.kind())?;
299    *pos_mut += amt as u64;
300    Ok(amt)
301}
302
303/// Helper function for writing to resizable vectors
304#[cfg(feature = "alloc")]
305fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> usize {
306    let pos = *pos_mut as usize;
307    let end_pos = pos + buf.len();
308
309    // Ensure the vector is large enough
310    if end_pos > vec.len() {
311        vec.resize(end_pos, 0);
312    }
313
314    vec[pos..end_pos].copy_from_slice(buf);
315    *pos_mut += buf.len() as u64;
316    buf.len()
317}
318
319// Write implementation for &mut [u8]
320impl Write for Cursor<&mut [u8]> {
321    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
322        slice_write(&mut self.pos, self.inner, buf)
323    }
324
325    fn flush(&mut self) -> Result<(), Self::Error> {
326        Ok(())
327    }
328}
329
330// Write implementation for arrays
331impl<const N: usize> Write for Cursor<&mut [u8; N]> {
332    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
333        slice_write(&mut self.pos, &mut self.inner[..], buf)
334    }
335
336    fn flush(&mut self) -> Result<(), Self::Error> {
337        Ok(())
338    }
339}
340
341// Write implementation for owned arrays
342impl<const N: usize> Write for Cursor<[u8; N]> {
343    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
344        slice_write(&mut self.pos, &mut self.inner[..], buf)
345    }
346
347    fn flush(&mut self) -> Result<(), Self::Error> {
348        Ok(())
349    }
350}
351
352// Alloc-specific implementations
353#[cfg(feature = "alloc")]
354impl Write for Cursor<&mut Vec<u8>> {
355    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
356        Ok(vec_write(&mut self.pos, self.inner, buf))
357    }
358
359    fn flush(&mut self) -> Result<(), Self::Error> {
360        Ok(())
361    }
362}
363
364#[cfg(feature = "alloc")]
365impl Write for Cursor<Vec<u8>> {
366    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
367        Ok(vec_write(&mut self.pos, &mut self.inner, buf))
368    }
369
370    fn flush(&mut self) -> Result<(), Self::Error> {
371        Ok(())
372    }
373}
374
375#[cfg(feature = "alloc")]
376impl Write for Cursor<Box<[u8]>> {
377    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
378        slice_write(&mut self.pos, &mut self.inner, buf)
379    }
380
381    fn flush(&mut self) -> Result<(), Self::Error> {
382        Ok(())
383    }
384}