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 crypto;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod password;
|
pub mod password;
|
||||||
|
/// Cursor utilities for parsers.
|
||||||
|
pub mod slice;
|
||||||
pub mod token;
|
pub mod token;
|
||||||
pub mod transcode;
|
pub mod transcode;
|
||||||
pub mod validate;
|
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