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}