util: add slice cursor for parsers
This commit is contained in:
parent
f945f5659f
commit
a583875cd7
2 changed files with 261 additions and 0 deletions
|
@ -2,6 +2,8 @@ pub mod bear;
|
|||
pub mod crypto;
|
||||
pub mod http;
|
||||
pub mod password;
|
||||
/// Cursor utilities for parsers.
|
||||
pub mod slice;
|
||||
pub mod token;
|
||||
pub mod transcode;
|
||||
pub mod validate;
|
||||
|
|
259
src/util/slice.rs
Normal file
259
src/util/slice.rs
Normal file
|
@ -0,0 +1,259 @@
|
|||
use std::ops::{Add, AddAssign, Not, Sub, SubAssign};
|
||||
|
||||
/// Helper utility for parsers operating on slices
|
||||
pub struct SliceCursor<'a, T> {
|
||||
data: &'a [T],
|
||||
pos: Position,
|
||||
chop: usize,
|
||||
}
|
||||
|
||||
/// Helper for the [`SliceCursor`] helper.
|
||||
struct Position {
|
||||
/// Always within -1 and `end` (both inclusive).
|
||||
pos: isize,
|
||||
/// Length of the slice (i.e. first out-of-bound index).
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl<'a, T> SliceCursor<'a, T> {
|
||||
pub fn new(data: &'a [T]) -> Self {
|
||||
assert!(data.len() <= isize::MAX as usize);
|
||||
|
||||
Self {
|
||||
data,
|
||||
pos: Position::new(data.len()),
|
||||
chop: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> SliceCursor<'a, T> {
|
||||
/// Return the item at the current position, if any.
|
||||
pub fn current(&self) -> Option<&'a T> {
|
||||
self.pos.index().map(|index| &self.data[index])
|
||||
}
|
||||
|
||||
/// Advance the cursor to the next item and return that item.
|
||||
pub fn next(&mut self) -> Option<&'a T> {
|
||||
self.pos.advance().map(|index| &self.data[index])
|
||||
}
|
||||
|
||||
/// Return the next item without advancing the cursor.
|
||||
pub fn peek(&self) -> Option<&'a T> {
|
||||
self.pos.next_index().map(|index| &self.data[index])
|
||||
}
|
||||
|
||||
/// Return the current item and reverse the cursor.
|
||||
pub fn prev(&mut self) -> Option<&'a T> {
|
||||
self.pos.reverse().map(|index| &self.data[index])
|
||||
}
|
||||
|
||||
/// Peek for the next item and advance the cursor if `predicate` is true.
|
||||
pub fn next_if<F>(&mut self, predicate: F) -> Option<&'a T>
|
||||
where
|
||||
F: FnOnce(&'a T) -> bool,
|
||||
{
|
||||
let next = self.peek()?;
|
||||
if predicate(next) {
|
||||
self.pos.advance();
|
||||
Some(next)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_while<F>(&mut self, mut predicate: F) -> &'a [T]
|
||||
where
|
||||
F: FnMut(&'a T) -> bool,
|
||||
{
|
||||
let start = self.pos.next_index_or_end();
|
||||
let len = self.data[start..]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, v)| predicate(v).not().then_some(i))
|
||||
.unwrap_or(self.data.len() - start);
|
||||
let end = start + len;
|
||||
self.pos += len;
|
||||
&self.data[start..end]
|
||||
}
|
||||
|
||||
/// Return a slice over all elements since the last time this method was called.
|
||||
/// If the cursor went backwards, the slice is empty.
|
||||
pub fn chop(&mut self) -> &'a [T] {
|
||||
let start = self.chop;
|
||||
let end = self.pos.next_index_or_end();
|
||||
|
||||
let slice = if start < end {
|
||||
&self.data[start..end]
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
|
||||
self.chop = end;
|
||||
slice
|
||||
}
|
||||
|
||||
/// Return how many items remain in the cursor.
|
||||
/// This indicates how many times it is possible
|
||||
/// to call [`Self::next()`] before it returns `None`.
|
||||
pub fn remaining(&self) -> usize {
|
||||
self.data.len() - self.pos.next_index_or_end()
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new(end: usize) -> Position {
|
||||
assert!(end <= isize::MAX as usize);
|
||||
Position { pos: -1, end }
|
||||
}
|
||||
|
||||
pub fn advance(&mut self) -> Option<usize> {
|
||||
*self += 1;
|
||||
self.index()
|
||||
}
|
||||
|
||||
pub fn reverse(&mut self) -> Option<usize> {
|
||||
*self -= 1;
|
||||
self.index()
|
||||
}
|
||||
|
||||
pub fn jump_to(&mut self, index: usize) {
|
||||
assert!(index <= self.end);
|
||||
self.pos = index as isize;
|
||||
}
|
||||
|
||||
pub fn index(&self) -> Option<usize> {
|
||||
(0..self.end as isize)
|
||||
.contains(&self.pos)
|
||||
.then_some(self.pos as usize)
|
||||
}
|
||||
|
||||
pub fn next_index(&self) -> Option<usize> {
|
||||
let next_pos = assert_usize(self.pos + 1);
|
||||
(next_pos < self.end).then_some(next_pos)
|
||||
}
|
||||
|
||||
pub fn next_index_or_end(&self) -> usize {
|
||||
self.next_index().unwrap_or(self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for Position {
|
||||
type Output = Position;
|
||||
fn add(self, rhs: usize) -> Position {
|
||||
let rhs = assert_isize(rhs);
|
||||
let result = self.pos.saturating_add(rhs);
|
||||
Position {
|
||||
pos: result.min(self.end as isize),
|
||||
end: self.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for Position {
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
let rhs = assert_isize(rhs);
|
||||
self.pos = self.pos.saturating_add(rhs).min(self.end as isize);
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<usize> for Position {
|
||||
type Output = Position;
|
||||
fn sub(self, rhs: usize) -> Position {
|
||||
let rhs = assert_isize(rhs);
|
||||
let result = self.pos.saturating_sub(rhs);
|
||||
Position {
|
||||
pos: result.max(-1),
|
||||
end: self.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<usize> for Position {
|
||||
fn sub_assign(&mut self, rhs: usize) {
|
||||
let rhs = assert_isize(rhs);
|
||||
self.pos = self.pos.saturating_sub(rhs).max(-1);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_usize(i: isize) -> usize {
|
||||
assert!(i >= 0);
|
||||
i as usize
|
||||
}
|
||||
|
||||
fn assert_isize(u: usize) -> isize {
|
||||
assert!(u <= isize::MAX as usize);
|
||||
u as isize
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::util::slice::SliceCursor;
|
||||
|
||||
#[test]
|
||||
fn next_and_prev() {
|
||||
let data: Vec<u8> = (0..10).collect();
|
||||
let mut cursor = SliceCursor::new(&data);
|
||||
assert_eq!(cursor.remaining(), 10);
|
||||
|
||||
for (i, v) in data.iter().enumerate() {
|
||||
assert_eq!(cursor.remaining(), data.len() - i);
|
||||
assert_eq!(cursor.next(), Some(v));
|
||||
}
|
||||
assert_eq!(cursor.remaining(), 0);
|
||||
assert!(cursor.next().is_none());
|
||||
assert_eq!(cursor.remaining(), 0);
|
||||
|
||||
for (i, v) in data.iter().rev().enumerate() {
|
||||
assert_eq!(cursor.prev(), Some(v));
|
||||
assert_eq!(cursor.remaining(), i);
|
||||
}
|
||||
assert_eq!(cursor.prev(), None);
|
||||
assert_eq!(cursor.remaining(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chop() {
|
||||
let data: Vec<u8> = (0..10).collect();
|
||||
let mut cursor = SliceCursor::new(&data);
|
||||
|
||||
assert_eq!(cursor.chop(), &data[0..0]);
|
||||
|
||||
cursor.next();
|
||||
assert_eq!(cursor.chop(), &data[0..1]);
|
||||
|
||||
for _ in 0..3 {
|
||||
cursor.next();
|
||||
}
|
||||
assert_eq!(cursor.chop(), &data[1..4]);
|
||||
|
||||
cursor.prev();
|
||||
assert_eq!(cursor.chop(), &data[3..3]);
|
||||
|
||||
while cursor.next().is_some() {}
|
||||
assert_eq!(cursor.chop(), &data[3..10]);
|
||||
|
||||
for i in (0u8..10).rev() {
|
||||
assert_eq!(cursor.prev(), Some(&i));
|
||||
}
|
||||
assert!(cursor.prev().is_none());
|
||||
assert_eq!(cursor.chop(), &data[0..0]);
|
||||
|
||||
while cursor.next().is_some() {}
|
||||
assert_eq!(cursor.chop(), &data[..]);
|
||||
assert_eq!(cursor.chop(), &data[10..10]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn predicates() {
|
||||
let data: Vec<u8> = (0..10).collect();
|
||||
let mut cursor = SliceCursor::new(&data);
|
||||
|
||||
assert_eq!(cursor.next_if(|c| *c == 0), Some(&data[0]));
|
||||
assert_eq!(cursor.next_if(|c| *c == 0), None);
|
||||
|
||||
assert_eq!(cursor.next_while(|c| *c < 5), &data[1..5]);
|
||||
assert_eq!(cursor.current(), Some(&4));
|
||||
assert_eq!(cursor.chop(), &data[0..5]);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue