const HIGH: u64 = 0x8080_8080_8080_8080;
const STEP: usize = size_of::<u64>();
#[must_use]
pub fn valid(bytes: &[u8]) -> bool {
let len = bytes.len();
let mut at = 0;
while at < len {
if bytes[at] < 0x80 {
while let Some(&[a, b, c, d, e, f, g, h]) = bytes.get(at..at + STEP) {
if u64::from_le_bytes([a, b, c, d, e, f, g, h]) & HIGH != 0 {
break;
}
at += STEP;
}
while at < len && bytes[at] < 0x80 {
at += 1;
}
continue;
}
let first = bytes[at];
let follows = match first {
0xC2..=0xDF => 1,
0xE0..=0xEF => 2,
0xF0..=0xF4 => 3,
_ => return false,
};
if at + follows >= len {
return false;
}
let second = bytes[at + 1];
let allowed = match first {
0xC2..=0xDF => 0x80..=0xBF,
0xE0 => 0xA0..=0xBF,
0xED => 0x80..=0x9F,
0xE1..=0xEF => 0x80..=0xBF,
0xF0 => 0x90..=0xBF,
0xF4 => 0x80..=0x8F,
_ => 0x80..=0xBF,
};
if !allowed.contains(&second) {
return false;
}
if follows >= 2 && bytes[at + 2] & 0xC0 != 0x80 {
return false;
}
if follows >= 3 && bytes[at + 3] & 0xC0 != 0x80 {
return false;
}
at += follows + 1;
}
true
}
#[cfg(test)]
mod tests {
use super::valid;
fn agree(bytes: &[u8]) -> bool {
let ours = valid(bytes);
assert_eq!(ours, std::str::from_utf8(bytes).is_ok(), "disagreed about {bytes:?}");
ours
}
#[test]
fn nothing_is_text() {
assert!(agree(b""));
}
#[test]
fn ascii_of_every_length_around_the_word_is_text() {
let line = b"abcdefghijklmnopqrstuvwxyz0123456789";
for len in 0..line.len() {
assert!(agree(&line[..len]), "{len} bytes of ASCII");
}
}
#[test]
fn a_control_byte_and_a_nul_are_still_ascii() {
assert!(agree(b"a\0b\x7f\x01"));
}
#[test]
fn the_sequence_lengths_are_all_text() {
assert!(agree("é".as_bytes()));
assert!(agree("Яндекс".as_bytes()));
assert!(agree("日本語".as_bytes()));
assert!(agree("😀".as_bytes()));
assert!(agree("\u{10ffff}".as_bytes()));
}
#[test]
fn a_character_that_straddles_the_end_of_a_word_is_still_read_whole() {
for pad in 0..16 {
let mut bytes = vec![b'a'; pad];
bytes.extend_from_slice("😀".as_bytes());
bytes.extend_from_slice(b"tail");
assert!(agree(&bytes), "padded by {pad}");
}
}
#[test]
fn a_continuation_byte_on_its_own_is_not_text() {
assert!(!agree(&[0x80]));
assert!(!agree(&[0xbf]));
assert!(!agree(b"good\x80bytes"));
}
#[test]
fn a_sequence_cut_short_by_the_end_is_not_text() {
assert!(!agree(&[0xc3]));
assert!(!agree(&[0xe6, 0x97]));
assert!(!agree(&[0xf0, 0x9f, 0x98]));
}
#[test]
fn a_sequence_cut_short_by_the_next_one_is_not_text() {
assert!(!agree(&[0xc3, 0x28]));
assert!(!agree(&[0xe6, 0x97, 0x28]));
assert!(!agree(&[0xf0, 0x9f, 0x98, 0x28]));
}
#[test]
fn an_overlong_spelling_is_not_text() {
assert!(!agree(&[0xc0, 0xaf]));
assert!(!agree(&[0xc1, 0xbf]));
assert!(!agree(&[0xe0, 0x80, 0xaf]));
assert!(!agree(&[0xe0, 0x9f, 0xbf]));
assert!(!agree(&[0xf0, 0x80, 0x80, 0xaf]));
assert!(!agree(&[0xf0, 0x8f, 0xbf, 0xbf]));
}
#[test]
fn half_of_a_surrogate_pair_is_not_text() {
assert!(!agree(&[0xed, 0xa0, 0x80]));
assert!(!agree(&[0xed, 0xbf, 0xbf]));
assert!(agree(&[0xed, 0x9f, 0xbf]));
assert!(agree(&[0xee, 0x80, 0x80]));
}
#[test]
fn a_number_above_the_last_character_is_not_text() {
assert!(!agree(&[0xf4, 0x90, 0x80, 0x80]));
assert!(!agree(&[0xf5, 0x80, 0x80, 0x80]));
assert!(!agree(&[0xfe]));
assert!(!agree(&[0xff]));
}
#[test]
fn every_byte_on_its_own_answers_what_the_standard_library_answers() {
for byte in 0..=u8::MAX {
agree(&[byte]);
agree(&[b'a', byte]);
agree(&[byte, 0x80]);
}
}
#[test]
fn a_run_of_every_two_byte_pair_answers_what_the_standard_library_answers() {
for first in 0..=u8::MAX {
for second in 0..=u8::MAX {
agree(&[first, second]);
}
}
}
const EDGES: [u8; 10] = [0x00, 0x7f, 0x80, 0x8f, 0x90, 0x9f, 0xa0, 0xbf, 0xc0, 0xff];
#[test]
fn a_three_byte_sequence_answers_what_the_standard_library_answers() {
for first in 0xC0..=0xFF {
for second in 0..=u8::MAX {
for third in EDGES {
agree(&[first, second, third]);
}
}
}
}
#[test]
fn a_four_byte_sequence_answers_what_the_standard_library_answers() {
for first in 0xEF..=0xF5 {
for second in 0..=u8::MAX {
for third in EDGES {
for fourth in EDGES {
agree(&[first, second, third, fourth]);
}
}
}
}
}
#[test]
fn a_sequence_after_a_word_of_ascii_answers_what_the_standard_library_answers() {
for pad in 0..9 {
let ascii = vec![b'a'; pad];
for first in 0xC0..=0xFF {
for second in EDGES {
for third in EDGES {
let mut bytes = ascii.clone();
bytes.extend_from_slice(&[first, second, third]);
bytes.extend_from_slice(b"tail");
agree(&bytes);
}
}
}
}
}
}