use core::str::CharIndices;
#[derive(Clone, Debug)]
pub struct Utf8Scalars<'a> {
chars: CharIndices<'a>,
base: usize,
}
impl<'a> Utf8Scalars<'a> {
#[inline]
pub fn new(text: &'a str) -> Self {
Self::with_start(text, 0)
}
#[inline]
pub fn with_start(text: &'a str, start: usize) -> Self {
let (rest, base) = match text.get(start..) {
Some(rest) => (rest, start),
None => ("", text.len()),
};
Self {
chars: rest.char_indices(),
base,
}
}
}
impl Iterator for Utf8Scalars<'_> {
type Item = (usize, char);
#[inline]
fn next(&mut self) -> Option<(usize, char)> {
let (offset, ch) = self.chars.next()?;
Some((self.base + offset, ch))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.chars.size_hint()
}
}
#[derive(Clone, Debug)]
pub struct Utf16Scalars<'a> {
units: &'a [u16],
at: usize,
}
impl<'a> Utf16Scalars<'a> {
#[inline]
pub fn new(units: &'a [u16]) -> Self {
Self::with_start(units, 0)
}
#[inline]
pub fn with_start(units: &'a [u16], start: usize) -> Self {
Self {
units,
at: start.min(units.len()),
}
}
}
impl Iterator for Utf16Scalars<'_> {
type Item = (usize, char);
#[inline]
fn next(&mut self) -> Option<(usize, char)> {
let at = self.at;
let unit = u32::from(*self.units.get(at)?);
self.at = at + 1;
if !(0xD800..0xE000).contains(&unit) {
return Some((
at,
char::from_u32(unit).unwrap_or(char::REPLACEMENT_CHARACTER),
));
}
let low = self.units.get(at + 1).map_or(0, |&low| u32::from(low));
if unit < 0xDC00 && (0xDC00..0xE000).contains(&low) {
self.at = at + 2;
let scalar = 0x1_0000 + ((unit - 0xD800) << 10) + (low - 0xDC00);
return Some((
at,
char::from_u32(scalar).unwrap_or(char::REPLACEMENT_CHARACTER),
));
}
Some((at, char::REPLACEMENT_CHARACTER))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let left = self.units.len().saturating_sub(self.at);
(left.div_ceil(2), Some(left))
}
}