#![allow(unused)]
use core::{ptr::null, slice, str::from_utf8_unchecked};
pub struct Chars<'a> {
src: &'a str,
iter: slice::Iter<'a, u8>,
ptr: *const u8,
ch: Option<char>,
}
#[derive(Copy, Clone)]
pub struct Cursor {
ptr: *const u8,
}
impl<'a> Chars<'a> {
#[inline(always)]
pub fn offset_from_source_str(&self) -> usize {
unsafe { self.ptr.offset_from(self.src.as_ptr()) as _ }
}
#[inline(always)]
pub fn cursor(&self) -> Cursor {
Cursor { ptr: self.ptr }
}
#[inline(always)]
pub fn head(&self) -> Option<char> {
self.ch
}
#[inline(always)]
pub fn sub_str_from_cursor(&self, cursor: Cursor) -> &'a str {
unsafe {
let offset = cursor.ptr.offset_from(self.src.as_ptr());
assert!(
0 <= offset && (offset as usize) < self.src.len(),
"cursor is from a different str"
);
from_utf8_unchecked(slice::from_raw_parts(
cursor.ptr,
self.ptr.offset_from(cursor.ptr) as _,
))
}
}
#[inline(always)]
pub fn source(&self) -> &'a str {
self.src
}
#[must_use]
#[inline(always)]
pub fn tail(&self) -> &'a str {
unsafe { from_utf8_unchecked(self.iter.as_slice()) }
}
}
impl<'a> From<&'a str> for Chars<'a> {
fn from(src: &'a str) -> Self {
let mut chars = Chars {
src,
iter: src.as_bytes().iter(),
ptr: null(),
ch: None,
};
chars.next();
chars
}
}
impl<'a> Iterator for Chars<'a> {
type Item = char;
#[inline(always)]
fn next(&mut self) -> Option<char> {
let tmp = self.ch;
self.ptr = self.iter.as_slice().as_ptr();
self.ch = next_code_point(&mut self.iter).map(|ch| unsafe { char::from_u32_unchecked(ch) });
tmp
}
}
#[inline(always)]
fn next_code_point<'a, I: Iterator<Item = &'a u8>>(bytes: &mut I) -> Option<u32> {
#[inline(always)]
const fn utf8_first_byte(byte: u8, width: u32) -> u32 {
(byte & (0x7F >> width)) as u32
}
#[inline(always)]
const fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 {
(ch << 6) | (byte & CONT_MASK) as u32
}
const CONT_MASK: u8 = 0b0011_1111;
let x = *bytes.next()?;
if x < 128 {
return Some(x as u32);
}
let init = utf8_first_byte(x, 2);
let y = unsafe { *bytes.next().unwrap_unchecked() };
let mut ch = utf8_acc_cont_byte(init, y);
if x >= 0xE0 {
let z = unsafe { *bytes.next().unwrap_unchecked() };
let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z);
ch = init << 12 | y_z;
if x >= 0xF0 {
let w = unsafe { *bytes.next().unwrap_unchecked() };
ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w);
}
}
Some(ch)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn iter() {
let mut chars = Chars::from("abcd");
assert_eq!(chars.head(), Some('a'));
assert_eq!(chars.next(), Some('a'));
assert_eq!(chars.head(), Some('b'));
assert_eq!(chars.next(), Some('b'));
assert_eq!(chars.head(), Some('c'));
}
}