toad_cursor/
lib.rs

1//! This microcrate provides a Cursor usable over byte [`toad_array::Array`]s
2
3// docs
4#![doc(html_root_url = "https://docs.rs/toad-cursor/0.1.0")]
5#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
6// -
7// style
8#![allow(clippy::unused_unit)]
9// -
10// deny
11#![deny(missing_docs)]
12#![deny(missing_debug_implementations)]
13#![deny(missing_copy_implementations)]
14#![cfg_attr(not(test), deny(unsafe_code))]
15// -
16// warnings
17#![cfg_attr(not(test), warn(unreachable_pub))]
18// -
19// features
20#![cfg_attr(not(feature = "std"), no_std)]
21
22#[cfg(feature = "alloc")]
23extern crate alloc as std_alloc;
24
25/// A cursor over a byte array (std- and alloc-less port of [`std::io::Cursor`])
26#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
27pub struct Cursor<T> {
28  t: T,
29  cursor: usize,
30  len: usize,
31}
32
33impl<T: AsRef<[u8]>> Cursor<T> {
34  fn is_exhausted_(cursor: usize, len: usize) -> bool {
35    Self::remaining_(cursor, len) <= 0
36  }
37
38  fn remaining_(cursor: usize, len: usize) -> isize {
39    len as isize - cursor as isize
40  }
41
42  fn peek_(len: usize, cursor: usize, t: &T, n: usize) -> Option<&[u8]> {
43    if n as isize > Self::remaining_(cursor, len) {
44      None
45    } else {
46      Some(&t.as_ref()[cursor..cursor + n])
47    }
48  }
49
50  fn skip_(cursor: &mut usize, len: usize, n: usize) -> usize {
51    if Self::is_exhausted_(*cursor, len) {
52      0
53    } else if *cursor + n > len {
54      let left = len - *cursor;
55      *cursor += left;
56      left
57    } else {
58      *cursor += n;
59      n
60    }
61  }
62
63  fn peek_until_end_(cursor: usize, len: usize, t: &T) -> &[u8] {
64    if Self::is_exhausted_(cursor, len) {
65      &[]
66    } else {
67      &t.as_ref()[cursor..]
68    }
69  }
70
71  fn seek_to_end_(cursor: &mut usize, len: usize) {
72    *cursor = len;
73  }
74
75  fn take_until_end_<'a, 'b>(cursor: &'a mut usize, len: usize, t: &'b T) -> &'b [u8] {
76    let out = Self::peek_until_end_(*cursor, len, t);
77    Self::seek_to_end_(cursor, len);
78
79    out
80  }
81
82  /// Creates a new cursor
83  pub fn new(t: T) -> Cursor<T> {
84    let len = t.as_ref().len();
85    Cursor { t, cursor: 0, len }
86  }
87
88  /// Unwraps the cursor, discarding its internal position
89  pub fn into_inner(self) -> T {
90    self.t
91  }
92
93  /// Take the next byte in the cursor, returning None
94  /// if the cursor is exhausted.
95  ///
96  /// Runs in O(1) time.
97  #[allow(clippy::should_implement_trait)]
98  pub fn next(&mut self) -> Option<u8> {
99    self.take_exact(1).and_then(|a| match a {
100                        | &[a] => Some(a),
101                        | _ => None,
102                      })
103  }
104
105  /// Take `n` bytes from the cursor, stopping early if
106  /// the end of the buffer is encountered.
107  ///
108  /// Runs in O(1) time.
109  pub fn take(&mut self, n: usize) -> &[u8] {
110    Self::peek_(self.len, self.cursor, &self.t, n).map(|a| {
111                                                    Self::skip_(&mut self.cursor, self.len, n);
112                                                    a
113                                                  })
114                                                  .unwrap_or_else(|| {
115                                                    Self::take_until_end_(&mut self.cursor,
116                                                                          self.len,
117                                                                          &self.t)
118                                                  })
119  }
120
121  /// Take `n` bytes from the cursor, returning None if
122  /// the end of the buffer is encountered.
123  ///
124  /// Runs in O(1) time.
125  pub fn take_exact(&mut self, n: usize) -> Option<&[u8]> {
126    Self::peek_(self.len, self.cursor, &self.t, n).map(|a| {
127                                                    Self::skip_(&mut self.cursor, self.len, n);
128                                                    a
129                                                  })
130  }
131
132  /// Without advancing the position, look at the next
133  /// `n` bytes, or until the end if there are less than `n` bytes
134  /// remaining.
135  ///
136  /// Runs in O(1) time.
137  pub fn peek(&self, n: usize) -> &[u8] {
138    Self::peek_(self.len, self.cursor, &self.t, n).unwrap_or_else(|| self.peek_until_end())
139  }
140
141  /// Without advancing the position, look at the next
142  /// `n` bytes, returning None if there are less than `n` bytes
143  /// remaining.
144  ///
145  /// Runs in O(1) time.
146  pub fn peek_exact(&self, n: usize) -> Option<&[u8]> {
147    Self::peek_(self.len, self.cursor, &self.t, n)
148  }
149
150  /// Advance the cursor by `n` bytes.
151  ///
152  /// Returns the actual number of bytes skipped:
153  ///  - Equal to n if there are at least n more bytes in the buffer
154  ///  - Less than n if n would seek past the end
155  ///  - Zero if the cursor is already exhausted
156  ///
157  /// Runs in O(1) time.
158  pub fn skip(&mut self, n: usize) -> usize {
159    Self::skip_(&mut self.cursor, self.len, n)
160  }
161
162  /// Consume bytes until a predicate returns `false` or the end is reached.
163  ///
164  /// Runs in O(n) time.
165  pub fn take_while(&mut self, mut f: impl FnMut(u8) -> bool) -> &[u8] {
166    if self.is_exhausted() {
167      return &[];
168    }
169
170    (self.cursor..self.len).into_iter()
171                           .take_while(|ix| f(self.t.as_ref()[*ix]))
172                           .last()
173                           .map(|end_ix| {
174                             let out = &self.t.as_ref()[self.cursor..=end_ix];
175                             self.cursor = end_ix + 1;
176                             out
177                           })
178                           .unwrap_or(&[])
179  }
180
181  /// Whether the cursor has reached the end
182  /// of the buffer.
183  ///
184  /// Runs in O(1) time.
185  pub fn is_exhausted(&self) -> bool {
186    Self::is_exhausted_(self.cursor, self.len)
187  }
188
189  /// The number of elements not yet consumed
190  ///
191  /// Runs in O(1) time.
192  pub fn remaining(&self) -> usize {
193    Self::remaining_(self.cursor, self.len).max(0) as usize
194  }
195
196  /// Get the bytes remaining in the buffer without advancing the position.
197  ///
198  /// Runs in O(1) time.
199  pub fn peek_until_end(&self) -> &[u8] {
200    Self::peek_until_end_(self.cursor, self.len, &self.t)
201  }
202
203  /// Get the bytes remaining in the buffer, advancing
204  /// the position to the end.
205  ///
206  /// Runs in O(1) time.
207  pub fn take_until_end(&mut self) -> &[u8] {
208    Self::take_until_end_(&mut self.cursor, self.len, &self.t)
209  }
210
211  /// Get the position the cursor points to within
212  /// the buffer
213  pub fn position(&self) -> usize {
214    self.cursor
215  }
216}
217
218#[cfg(test)]
219mod tests {
220  use super::*;
221
222  #[test]
223  pub fn peek_until_end() {
224    let cur = Cursor::new(vec![]);
225    assert_eq!(cur.peek_until_end(), &[]);
226
227    let cur = Cursor::new(vec![1, 2, 3]);
228    assert_eq!(cur.peek_until_end(), &[1, 2, 3]);
229
230    let mut cur = Cursor::new(vec![1, 2, 3]);
231    cur.skip(1);
232    assert_eq!(cur.peek_until_end(), &[2, 3]);
233  }
234
235  #[test]
236  pub fn take_until_end() {
237    let mut cur = Cursor::new(vec![]);
238    assert_eq!(cur.take_until_end(), &[]);
239    assert_eq!(cur.take_until_end(), &[]);
240    assert_eq!(cur.take_until_end(), &[]);
241
242    let mut cur = Cursor::new(vec![1, 2, 3]);
243    assert_eq!(cur.take_until_end(), &[1, 2, 3]);
244
245    let mut cur = Cursor::new(vec![1, 2, 3]);
246    cur.skip(1);
247    assert_eq!(cur.take_until_end(), &[2, 3]);
248    assert_eq!(cur.peek_until_end(), &[]);
249  }
250
251  #[test]
252  pub fn next() {
253    let mut cur = Cursor::new(vec![1]);
254    assert_eq!(cur.next(), Some(1));
255    assert_eq!(cur.next(), None);
256    assert_eq!(cur.next(), None);
257  }
258
259  #[test]
260  pub fn take() {
261    let mut cur = Cursor::new(vec![1, 2, 3]);
262    assert_eq!(cur.take(2), &[1, 2]);
263    assert_eq!(cur.take(1), &[3]);
264    assert_eq!(cur.take(1), &[]);
265  }
266
267  #[test]
268  pub fn peek() {
269    let mut cur = Cursor::new(vec![1, 2, 3]);
270    assert_eq!(cur.peek(2), &[1, 2]);
271    assert_eq!(cur.peek(1), &[1]);
272    assert_eq!(cur.peek(4), &[1, 2, 3]);
273    cur.take(3);
274    assert_eq!(cur.peek(1), &[]);
275  }
276
277  #[test]
278  pub fn take_exact() {
279    let mut cur = Cursor::new(vec![1, 2, 3]);
280    assert_eq!(cur.take_exact(2), Some([1, 2].as_ref()));
281    assert_eq!(cur.take_exact(2), None);
282    assert_eq!(cur.take_exact(1), Some([3].as_ref()));
283  }
284
285  #[test]
286  pub fn peek_exact() {
287    let cur = Cursor::new(vec![1, 2, 3]);
288    assert_eq!(cur.peek_exact(3), Some([1, 2, 3].as_ref()));
289    assert_eq!(cur.peek_exact(1), Some([1].as_ref()));
290    assert_eq!(cur.peek_exact(4), None);
291  }
292
293  #[test]
294  pub fn take_while() {
295    let til_slash = |c: &mut Cursor<&str>| {
296      core::str::from_utf8(c.take_while(|b| (b as char) != '/')).unwrap()
297                                                                .to_string()
298    };
299
300    let mut cur = Cursor::new("abc/def");
301    assert_eq!(til_slash(&mut cur), "abc".to_string());
302    cur.skip(1);
303    assert_eq!(til_slash(&mut cur), "def".to_string());
304    assert_eq!(til_slash(&mut cur), "".to_string());
305
306    let mut cur = Cursor::new("a");
307    assert_eq!(til_slash(&mut cur), "a");
308
309    let mut cur = Cursor::new("");
310    assert_eq!(til_slash(&mut cur), "");
311
312    let mut cur = Cursor::new("ab");
313    assert_eq!(til_slash(&mut cur), "ab");
314
315    let mut cur = Cursor::new("/abcd");
316    assert_eq!(til_slash(&mut cur), "");
317  }
318
319  #[test]
320  pub fn seek() {
321    let mut cur = Cursor::new(vec![1, 2, 3, 4]);
322    assert_eq!(cur.skip(0), 0); // 0 <- cursor
323    assert_eq!(cur.skip(1), 1); // 1
324    assert_eq!(cur.skip(2), 2); // 3
325    assert_eq!(cur.skip(1), 1); // 4
326    assert_eq!(cur.skip(1), 0); // 4
327  }
328}