#![no_std]
#![cfg_attr(feature = "verus", feature(proc_macro_hygiene))]
#![deny(unsafe_op_in_unsafe_fn)]
#![warn(missing_docs)]
#![allow(clippy::inline_always)]
#![cfg_attr(rr, feature(register_tool))]
#![cfg_attr(rr, feature(custom_inner_attributes))]
#![cfg_attr(rr, register_tool(rr))]
#![cfg_attr(rr, rr::package("smooth_utf8"))]
#![cfg_attr(rr, rr::coq_prefix("smooth_utf8"))]
#![cfg_attr(rr, rr::include("ptr"))]
#[cfg(feature = "alloc")]
extern crate alloc;
use core::ops::Range;
macro_rules! da {
($($t:tt)*) => { #[cfg(not(feature = "verus"))] { core::debug_assert!($($t)*); } };
}
#[cfg(feature = "verus")]
use spec::*;
#[cfg(feature = "verus")]
use verus_builtin_macros::{proof, verus_spec, verus_verify};
#[cfg(feature = "verus")]
#[allow(unused_imports)]
use vstd::prelude::*;
mod ascii_skip;
mod raw;
#[cfg(feature = "verus")]
pub mod spec;
#[cfg(all(feature = "simdutf8", not(target_arch = "aarch64")))]
const LONG_THRESHOLD: usize = 128;
#[cfg(all(feature = "simdutf8", target_arch = "aarch64"))]
const LONG_THRESHOLD: usize = 64;
#[cfg_attr(feature = "verus", verus_verify)]
#[allow(clippy::unreadable_literal)] const SIGN_BITS: u64 = 0x8080_8080_8080_8080;
#[cfg_attr(feature = "verus", verus_verify)]
pub const SLACK: usize = 8;
#[cfg_attr(feature = "verus", verus_spec(ret =>
ensures ret == is_valid_utf8(b@),
))]
#[inline]
#[must_use]
pub fn verify(b: &[u8]) -> bool {
#[cfg(feature = "simdutf8")]
if b.len() >= LONG_THRESHOLD {
return verify_long(b, 0, b.len());
}
#[cfg(feature = "verus")]
proof! { assert(b@.subrange(0, b@.len() as int) =~= b@); }
unsafe { verify_impl::<0>(b, 0..b.len()) }
}
#[cfg(feature = "simdutf8")]
#[cold]
#[inline(never)]
fn verify_long(buf: &[u8], start: usize, end: usize) -> bool {
simdutf8::basic::from_utf8(&buf[start..end]).is_ok()
}
#[inline]
#[must_use]
pub fn from_utf8(b: &[u8]) -> Option<&str> {
if verify(b) {
Some(unsafe { core::str::from_utf8_unchecked(b) })
} else {
None
}
}
#[deprecated(since = "0.2.2", note = "renamed to `from_utf8`")]
#[inline]
#[must_use]
pub fn to_str(b: &[u8]) -> Option<&str> {
from_utf8(b)
}
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires
range.start <= range.end,
range.end + SLACK <= buf@.len(),
ensures
ret == is_valid_utf8(buf@.subrange(range.start as int, range.end as int)),
))]
#[inline]
#[must_use]
pub unsafe fn verify_with_slack(buf: &[u8], range: Range<usize>) -> bool {
da!(range.start <= range.end);
da!(range.end.saturating_add(SLACK) <= buf.len());
#[cfg(feature = "simdutf8")]
if range.end - range.start >= LONG_THRESHOLD {
return verify_long(buf, range.start, range.end);
}
unsafe { verify_impl::<SLACK>(buf, range) }
}
pub use slack_buf::SlackBuf;
mod slack_buf {
#[cfg(feature = "verus")]
use super::{is_valid_utf8, verus_spec, verus_verify};
use super::{verify_with_slack, Range, SLACK};
#[cfg(feature = "verus")]
#[allow(unused_imports)]
use vstd::prelude::*;
#[cfg_attr(feature = "verus", verus_verify)]
#[repr(transparent)]
#[derive(Clone, Copy)]
#[cfg_attr(not(feature = "verus"), derive(Debug))]
pub struct SlackBuf<'a>(&'a [u8]);
#[cfg(feature = "verus")]
verus_builtin_macros::verus! {
impl<'a> View for SlackBuf<'a> {
type V = Seq<u8>;
closed spec fn view(&self) -> Seq<u8> { self.0@ }
}
}
#[cfg(feature = "verus")]
#[verus_verify]
#[allow(missing_docs)] impl<'a> SlackBuf<'a> {
#[verus_spec(ret =>
requires buf@.len() >= SLACK,
ensures ret@ == buf@,
)]
pub unsafe fn new_unchecked(buf: &'a [u8]) -> Self {
Self(buf)
}
#[verus_spec(ret =>
requires buf@.len() >= SLACK,
ensures ret@ == buf@,
)]
pub fn new_embedded_slack(buf: &'a [u8]) -> Self {
Self(buf)
}
#[verus_spec(ret =>
ensures ret@ == self@,
)]
pub fn as_bytes(&self) -> &'a [u8] {
self.0
}
#[verus_spec(ret =>
requires self@.len() >= SLACK,
ensures ret == self@.len() - SLACK,
)]
pub fn payload_len(&self) -> usize {
self.0.len() - SLACK
}
#[verus_spec(ret =>
requires
range.start <= range.end,
range.end + SLACK <= self@.len(),
ensures
ret == is_valid_utf8(self@.subrange(range.start as int, range.end as int)),
)]
pub fn verify(&self, range: Range<usize>) -> bool {
unsafe { verify_with_slack(self.0, range) }
}
}
#[cfg(not(feature = "verus"))]
const _: () = assert!(SLACK >= 4);
#[cfg(not(feature = "verus"))]
impl<'a> SlackBuf<'a> {
#[inline]
#[must_use]
pub const unsafe fn new_unchecked(buf: &'a [u8]) -> Self {
debug_assert!(buf.len() >= SLACK);
Self(buf)
}
#[inline]
#[must_use]
pub const fn new(buf: &'a [u8]) -> Option<Self> {
if buf.len() >= SLACK {
Some(Self(buf))
} else {
None
}
}
#[cfg_attr(feature = "alloc", doc = " [`new_add_slack`](Self::new_add_slack)")]
#[cfg_attr(not(feature = "alloc"), doc = " `new_add_slack`")]
#[inline]
#[must_use]
#[track_caller]
pub const fn new_embedded_slack(buf: &'a [u8]) -> Self {
assert!(buf.len() >= SLACK, "buf must carry SLACK trailing bytes");
Self(buf)
}
#[cfg(feature = "alloc")]
#[inline]
#[must_use]
pub fn new_add_slack(v: &'a mut alloc::vec::Vec<u8>) -> Self {
v.extend_from_slice(&[0u8; SLACK]);
Self(v.as_slice())
}
#[inline]
#[must_use]
pub const fn as_bytes(&self) -> &'a [u8] {
self.0
}
#[inline]
#[must_use]
pub const fn payload_len(&self) -> usize {
self.0.len() - SLACK
}
#[inline]
#[must_use]
pub fn verify(&self, range: Range<usize>) -> bool {
assert!(range.start <= range.end && range.end <= self.payload_len());
unsafe { verify_with_slack(self.0, range) }
}
#[inline]
#[must_use]
pub fn to_str(&self, range: Range<usize>) -> Option<&'a str> {
if self.verify(range.clone()) {
Some(unsafe { core::str::from_utf8_unchecked(self.0.get_unchecked(range)) })
} else {
None
}
}
#[inline]
#[must_use]
pub fn le_u32(&self, at: usize) -> u32 {
assert!(at <= self.payload_len());
u32::from_le(unsafe { self.0.as_ptr().add(at).cast::<u32>().read_unaligned() })
}
}
}
#[cfg_attr(feature = "verus", verus_verify(external_body))]
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires at + 8 <= buf@.len(),
ensures ret == pack64(buf@, at as int),
))]
#[inline(always)]
unsafe fn load64(buf: &[u8], at: usize) -> u64 {
da!(at + 8 <= buf.len());
unsafe { raw::load64_raw(buf.as_ptr(), at) }
}
#[cfg_attr(feature = "verus", verus_verify(external_body))]
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires at + 4 <= buf@.len(),
ensures ret == pack32(buf@, at as int),
))]
#[inline(always)]
unsafe fn load32(buf: &[u8], at: usize) -> u32 {
da!(at + 4 <= buf.len());
unsafe { raw::load32_raw(buf.as_ptr(), at) }
}
#[cfg_attr(feature = "verus", verus_verify(external_body))]
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires at + 2 <= buf@.len(),
ensures ret == pack16(buf@, at as int),
))]
#[inline(always)]
unsafe fn load16(buf: &[u8], at: usize) -> u16 {
da!(at + 2 <= buf.len());
unsafe { raw::load16_raw(buf.as_ptr(), at) }
}
#[cfg_attr(feature = "verus", verus_verify(external_body))]
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires at < buf@.len(),
ensures ret == buf@[at as int],
))]
#[inline(always)]
unsafe fn load8(buf: &[u8], at: usize) -> u8 {
da!(at < buf.len());
unsafe { raw::load8_raw(buf.as_ptr(), at) }
}
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires
start <= end,
end - start < 8,
end + PAD <= buf@.len(),
ensures
ret == is_valid_utf8(buf@.subrange(start as int, end as int)),
))]
#[inline(always)]
unsafe fn verify_short<const PAD: usize>(buf: &[u8], start: usize, end: usize) -> bool {
da!(start <= end && end - start < 8 && end.saturating_add(PAD) <= buf.len());
if PAD >= 8 {
if start == end {
#[cfg(feature = "verus")]
proof! { assert(buf@.subrange(start as int, end as int).len() == 0); }
return true;
}
let left = end - start;
let bytes = unsafe { load64(buf, start) };
let mask = SIGN_BITS >> ((8 - left) * 8);
if bytes & mask != 0 {
return verify_multibyte(buf, start, end);
}
#[cfg(feature = "verus")]
proof! {
assert forall |j: int| 0 <= j < left
implies #[trigger] byte64(bytes, j) == buf@[start + j] by {
lemma_pack64_byte(buf@, start as int, j);
};
assert(mask == sign_mask(left as int));
lemma_mask_zero_ascii(buf@, start as int, left as int, bytes);
lemma_ascii_valid(buf@, start as int, end as int);
}
return true;
}
if end - start >= 4 {
let lo = unsafe { load32(buf, start) };
let hi = unsafe { load32(buf, end - 4) };
if (lo | hi) & 0x8080_8080 != 0 {
return verify_multibyte(buf, start, end);
}
#[cfg(feature = "verus")]
proof! {
assert((lo | hi) & 0x8080_8080u32 == 0
==> lo & 0x8080_8080u32 == 0 && hi & 0x8080_8080u32 == 0) by (bit_vector);
lemma_signbits4(buf@, start as int);
lemma_signbits4(buf@, end as int - 4);
assert(all_ascii(buf@, start as int, end as int));
lemma_ascii_valid(buf@, start as int, end as int);
}
return true;
}
if end - start >= 2 {
let lo = unsafe { load16(buf, start) };
let hi = unsafe { load16(buf, end - 2) };
if (lo | hi) & 0x8080 != 0 {
return verify_multibyte(buf, start, end);
}
#[cfg(feature = "verus")]
proof! {
assert((lo | hi) & 0x8080u16 == 0
==> lo & 0x8080u16 == 0 && hi & 0x8080u16 == 0) by (bit_vector);
lemma_signbits2(buf@, start as int);
lemma_signbits2(buf@, end as int - 2);
assert(all_ascii(buf@, start as int, end as int));
lemma_ascii_valid(buf@, start as int, end as int);
}
return true;
}
if start < end {
let b = unsafe { load8(buf, start) };
if b >= 0x80 {
return verify_multibyte(buf, start, end);
}
#[cfg(feature = "verus")]
proof! {
assert(all_ascii(buf@, start as int, end as int));
lemma_ascii_valid(buf@, start as int, end as int);
}
return true;
}
#[cfg(feature = "verus")]
proof! { assert(buf@.subrange(start as int, end as int).len() == 0); }
true
}
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires
range.start <= range.end,
range.end + PAD <= buf@.len(),
ensures
ret == is_valid_utf8(buf@.subrange(range.start as int, range.end as int)),
))]
#[inline(always)]
unsafe fn verify_impl<const PAD: usize>(buf: &[u8], range: Range<usize>) -> bool {
let start = range.start;
let end = range.end;
da!(start <= end && end.saturating_add(PAD) <= buf.len());
if end - start < 8 {
return unsafe { verify_short::<PAD>(buf, start, end) };
}
let mut p = start;
p = ascii_skip::skip(buf, p, end);
if end - p >= ascii_skip::STEP {
#[cfg(feature = "verus")]
proof! { lemma_ascii_prefix_iff(buf@, start as int, p as int, end as int); }
return verify_multibyte(buf, p, end);
}
#[cfg_attr(feature = "verus", verus_spec(
invariant
start == range.start, end == range.end,
start <= p, p <= end, end + PAD <= buf@.len(),
all_ascii(buf@, start as int, p as int),
decreases end - p
))]
while end - p >= 8 {
let bytes = unsafe { load64(buf, p) };
if bytes & SIGN_BITS != 0 {
#[cfg(feature = "verus")]
proof! { lemma_ascii_prefix_iff(buf@, start as int, p as int, end as int); }
return verify_multibyte(buf, p, end);
}
#[cfg(feature = "verus")]
proof! {
lemma_signbits8(buf@, p as int);
lemma_ascii_extend(buf@, start as int, p as int, p as int + 8);
}
p += 8;
}
if p < end {
let bytes = unsafe { load64(buf, end - 8) };
if bytes & SIGN_BITS != 0 {
#[cfg(feature = "verus")]
proof! { lemma_ascii_prefix_iff(buf@, start as int, p as int, end as int); }
return verify_multibyte(buf, p, end);
}
#[cfg(feature = "verus")]
proof! {
lemma_signbits8(buf@, end as int - 8);
assert(all_ascii(buf@, p as int, end as int));
lemma_ascii_extend(buf@, start as int, p as int, end as int);
lemma_ascii_valid(buf@, start as int, end as int);
}
} else {
#[cfg(feature = "verus")]
proof! { lemma_ascii_valid(buf@, start as int, end as int); }
}
true
}
#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
fn verify_multibyte(buf: &[u8], start: usize, end: usize) -> bool {
core::str::from_utf8(&buf[start..end]).is_ok()
}
#[allow(dead_code)] const ACCEPT: u8 = 12;
#[allow(dead_code)]
const REJECT: u8 = 0;
#[rustfmt::skip]
const CLASS: [u8; 256] = [
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
];
#[rustfmt::skip]
const TRANS: [u8; 256] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
12, 0,24,36,60,96,84, 0, 0, 0,48,72,
0,12, 0, 0, 0, 0, 0,12, 0,12, 0, 0,
0,24, 0, 0, 0, 0, 0,24, 0,24, 0, 0,
0, 0, 0, 0, 0, 0, 0,24, 0, 0, 0, 0,
0,24, 0, 0, 0, 0, 0, 0, 0,24, 0, 0,
0, 0, 0, 0, 0, 0, 0,36, 0,36, 0, 0,
0,36, 0, 0, 0, 0, 0,36, 0,36, 0, 0,
0,36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
];
const fn build_rows() -> [u64; 256] {
let mut row = [0u64; 256];
let mut b = 0usize;
while b < 256 {
let class = CLASS[b] as usize;
let mut s = 0usize;
while s < 9 {
let next = (TRANS[s * 12 + class] / 12) as u64 * 6;
row[b] |= next << (s * 6);
s += 1;
}
b += 1;
}
row
}
const ROW: [u64; 256] = build_rows();
mod state {
pub const REJECT: u64 = 0;
pub const ACCEPT: u64 = 6;
pub const C1: u64 = 12;
pub const C2: u64 = 18;
pub const E0: u64 = 24;
pub const ED: u64 = 30;
pub const F0: u64 = 36;
pub const C3: u64 = 42;
pub const F4: u64 = 48;
}
#[cfg_attr(feature = "verus", verus_verify)]
const DFA_ACCEPT: u64 = 6;
#[cfg_attr(feature = "verus", verus_verify)]
const DFA_REJECT: u64 = 0;
const _: [(); 0] = [(); (DFA_ACCEPT != state::ACCEPT) as usize];
const _: [(); 0] = [(); (DFA_REJECT != state::REJECT) as usize];
const _: [(); 0] = [(); (state::F4 + 6 > 64) as usize];
macro_rules! row_check {
($($byte:literal -> $state:path,)*) => {
$(const _: [(); 0] = [(); ((ROW[$byte] >> state::ACCEPT) & 63 != $state) as usize];)*
};
}
row_check! {
0x41 -> state::ACCEPT, 0x80 -> state::REJECT, 0xC1 -> state::REJECT,
0xC2 -> state::C1, 0xE0 -> state::E0, 0xE1 -> state::C2,
0xED -> state::ED, 0xEE -> state::C2, 0xF0 -> state::F0,
0xF1 -> state::C3, 0xF4 -> state::F4, 0xF5 -> state::REJECT,
}
#[cfg_attr(feature = "verus", verus_verify(external_body))]
#[cfg_attr(feature = "verus", verus_spec(ret =>
ensures ret == spec_row(byte),
))]
#[inline(always)]
#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))]
const fn row(byte: u8) -> u64 {
ROW[byte as usize]
}
#[allow(clippy::unreadable_literal, clippy::cast_possible_truncation)]
const _CHECK_SPEC_ROW: () = {
let mut b = 0usize;
while b < 256 {
let bb = b as u8;
let spec: u64 = if bb <= 0x7F {
0x0000000000000180
} else if bb <= 0x8F {
0x0012480300306000
} else if bb <= 0x9F {
0x0000492300306000
} else if bb <= 0xBF {
0x000049200C306000
} else if bb <= 0xC1 {
0
} else if bb <= 0xDF {
0x0000000000000300
} else if bb == 0xE0 {
0x0000000000000600
} else if bb <= 0xEC {
0x0000000000000480
} else if bb == 0xED {
0x0000000000000780
} else if bb <= 0xEF {
0x0000000000000480
} else if bb == 0xF0 {
0x0000000000000900
} else if bb <= 0xF3 {
0x0000000000000A80
} else if bb == 0xF4 {
0x0000000000000C00
} else {
0
};
assert!(ROW[b] == spec);
b += 1;
}
};
#[cfg_attr(feature = "verus", verus_verify)]
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires is_state(state),
ensures ret == spec_step(state, byte), is_state(ret),
))]
#[inline(always)]
#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))]
const fn step(state: u64, byte: u8) -> u64 {
#[cfg(feature = "verus")]
proof! { lemma_row_step(state, byte); }
(row(byte) >> (state & 63)) & 63
}
#[cfg_attr(feature = "verus", verus_verify)]
#[cfg_attr(feature = "verus", verus_spec(ret =>
requires
start < end,
end <= buf@.len(),
ensures
ret == is_valid_utf8(buf@.subrange(start as int, end as int)),
))]
#[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))]
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::too_many_lines)] fn verify_multibyte(buf: &[u8], start: usize, end: usize) -> bool {
let mut state = DFA_ACCEPT;
let mut p = start;
#[cfg(feature = "verus")]
proof! { assert(buf@.subrange(start as int, start as int).len() == 0); }
#[cfg_attr(feature = "verus", verus_spec(
invariant
start <= p, p <= end, end <= buf@.len(),
is_state(state),
state == run(ST_ACCEPT, buf@.subrange(start as int, p as int)),
decreases end - p
))]
while end - p >= 8 {
let w = unsafe { load64(buf, p) };
if state == DFA_ACCEPT && w & SIGN_BITS == 0 {
#[cfg(feature = "verus")]
proof! {
lemma_signbits8(buf@, p as int);
lemma_ascii_valid(buf@, p as int, p as int + 8);
lemma_run_valid(buf@.subrange(p as int, p as int + 8));
lemma_run_join(ST_ACCEPT, buf@, start as int, p as int, p as int + 8);
}
p += 8;
continue;
}
#[cfg(feature = "verus")]
proof! {
assert(w >> 0u64 == w) by (bit_vector);
lemma_chunk_snoc(buf@, start as int, p as int, w, 0, state);
}
state = step(state, w as u8);
#[cfg(feature = "verus")]
proof! { lemma_chunk_snoc(buf@, start as int, p as int, w, 1, state); }
state = step(state, (w >> 8) as u8);
#[cfg(feature = "verus")]
proof! { lemma_chunk_snoc(buf@, start as int, p as int, w, 2, state); }
state = step(state, (w >> 16) as u8);
#[cfg(feature = "verus")]
proof! { lemma_chunk_snoc(buf@, start as int, p as int, w, 3, state); }
state = step(state, (w >> 24) as u8);
#[cfg(feature = "verus")]
proof! { lemma_chunk_snoc(buf@, start as int, p as int, w, 4, state); }
state = step(state, (w >> 32) as u8);
#[cfg(feature = "verus")]
proof! { lemma_chunk_snoc(buf@, start as int, p as int, w, 5, state); }
state = step(state, (w >> 40) as u8);
#[cfg(feature = "verus")]
proof! { lemma_chunk_snoc(buf@, start as int, p as int, w, 6, state); }
state = step(state, (w >> 48) as u8);
#[cfg(feature = "verus")]
proof! { lemma_chunk_snoc(buf@, start as int, p as int, w, 7, state); }
state = step(state, (w >> 56) as u8);
p += 8;
if state == DFA_REJECT {
#[cfg(feature = "verus")]
proof! {
lemma_run_join(ST_ACCEPT, buf@, start as int, p as int, end as int);
lemma_run_reject(buf@.subrange(p as int, end as int));
lemma_run_valid(buf@.subrange(start as int, end as int));
}
return false;
}
}
#[cfg_attr(feature = "verus", verus_spec(
invariant
start <= p, p <= end, end <= buf@.len(),
is_state(state),
state == run(ST_ACCEPT, buf@.subrange(start as int, p as int)),
decreases end - p
))]
while p < end {
state = step(state, unsafe { load8(buf, p) });
#[cfg(feature = "verus")]
proof! { lemma_run_snoc(ST_ACCEPT, buf@, start as int, p as int); }
p += 1;
}
#[cfg(feature = "verus")]
proof! { lemma_run_valid(buf@.subrange(start as int, end as int)); }
state == DFA_ACCEPT
}
#[cfg(test)]
mod tests;