use crate::digest::Digest;
use std::{ops::RangeFrom, slice::SliceIndex};
#[derive(Debug, Clone)]
pub struct Instant<TextRef> {
text: TextRef,
rest: TextRef,
digested: usize,
}
impl<'text, Text: ?Sized> Instant<&'text Text> {
#[inline]
pub const fn new(text: &'text Text) -> Self {
Instant {
text,
rest: text,
digested: 0,
}
}
#[inline]
pub const fn text(&self) -> &'text Text {
self.text
}
#[inline]
pub const fn rest(&self) -> &'text Text {
self.rest
}
}
impl<TextRef> Instant<TextRef> {
#[inline]
pub const fn digested(&self) -> usize {
self.digested
}
}
impl<Text: ?Sized + Digest> Instant<&Text>
where
RangeFrom<usize>: SliceIndex<Text, Output = Text>,
{
#[inline]
pub unsafe fn digest_unchecked(&mut self, n: usize) {
debug_assert!(self.rest.validate(n));
self.rest = self.rest.get_unchecked(n..);
self.digested = self.digested.unchecked_add(n);
}
#[inline]
pub unsafe fn to_digested_unchecked(&self, n: usize) -> Self {
let mut instant = self.clone();
instant.digest_unchecked(n);
instant
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn instant_new_getters() {
let i = Instant::new("123");
assert_eq!(i.digested(), 0);
assert_eq!(i.rest(), "123");
assert_eq!(i.text(), "123");
}
#[test]
fn instant_clone() {
let i = Instant::new("123");
let _ = i.clone();
}
#[test]
fn instant_debug() {
let _ = format!("{:?}", Instant::new("123"));
}
#[test]
fn instant_bytes_digest_unchecked() {
let mut i = Instant::new(b"123" as &[u8]);
unsafe { i.digest_unchecked(1) };
assert_eq!(i.digested(), 1);
assert_eq!(i.rest(), b"23");
assert_eq!(i.text(), b"123");
unsafe { i.digest_unchecked(1) };
assert_eq!(i.digested(), 2);
assert_eq!(i.rest(), b"3");
assert_eq!(i.text(), b"123");
unsafe { i.digest_unchecked(1) };
assert_eq!(i.digested(), 3);
assert_eq!(i.rest(), b"");
assert_eq!(i.text(), b"123");
}
#[test]
#[should_panic]
fn instant_bytes_digest_unchecked_overflow() {
let mut i = Instant::new(b"123" as &[u8]);
unsafe { i.digest_unchecked(4) };
}
#[test]
fn instant_str_digest_unchecked() {
let mut i = Instant::new("123");
unsafe { i.digest_unchecked(1) };
assert_eq!(i.digested(), 1);
assert_eq!(i.rest(), "23");
assert_eq!(i.text(), "123");
unsafe { i.digest_unchecked(1) };
assert_eq!(i.digested(), 2);
assert_eq!(i.rest(), "3");
assert_eq!(i.text(), "123");
unsafe { i.digest_unchecked(1) };
assert_eq!(i.digested(), 3);
assert_eq!(i.rest(), "");
assert_eq!(i.text(), "123");
}
#[test]
#[should_panic]
fn instant_str_digest_unchecked_overflow() {
let mut i = Instant::new("123");
unsafe { i.digest_unchecked(4) };
}
#[test]
#[should_panic]
fn instant_str_digest_unchecked_invalid_code_point() {
let mut i = Instant::new("好");
unsafe { i.digest_unchecked(1) };
}
#[test]
fn instant_to_digested_unchecked() {
let instant = unsafe { Instant::new("123").to_digested_unchecked(1) };
assert_eq!(instant.digested(), 1);
assert_eq!(instant.rest(), "23");
let instant = unsafe { Instant::new(b"123" as &[u8]).to_digested_unchecked(1) };
assert_eq!(instant.digested(), 1);
assert_eq!(instant.rest(), b"23");
}
#[test]
#[should_panic]
fn instant_bytes_to_digested_unchecked_overflow() {
let _ = unsafe { Instant::new(b"123" as &[u8]).to_digested_unchecked(4) };
}
#[test]
#[should_panic]
fn instant_str_to_digested_unchecked_invalid_utf8() {
let _ = unsafe { Instant::new("好").to_digested_unchecked(1) };
}
#[test]
#[should_panic]
fn instant_str_to_digested_unchecked_overflow() {
let _ = unsafe { Instant::new("123").to_digested_unchecked(4) };
}
}