use core::marker::PhantomData;
use crate::{convert::TextBuf, unit::Unit};
pub mod sealed {
pub trait Sealed {}
impl Sealed for crate::utf8::Utf8 {}
impl<const FOREIGN: bool> Sealed for crate::utf16::Utf16<FOREIGN> {}
impl<const FOREIGN: bool> Sealed for crate::utf32::Utf32<FOREIGN> {}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Utf8,
Utf16,
Utf32,
}
pub trait Encoding: sealed::Sealed + 'static {
type Unit: Unit;
type Container: TextBuf<Unit = Self::Unit>;
const KIND: Kind;
const FOREIGN: bool;
const MAX_UNITS: usize;
fn run_length(front: Self::Unit) -> usize;
fn encoded_length(cp: u32) -> usize;
fn encode(cp: u32, out: &mut [Self::Unit]) -> usize;
fn decode(input: &mut &[Self::Unit]) -> u32;
fn decode_back(input: &mut &[Self::Unit]) -> u32;
fn from_bytes(data: &[u8]) -> Self::Container
where
Self: Sized,
{
let units = crate::bytes::from_bytes::<Self>(data);
TextBuf::from_units(units)
}
}
#[inline(always)]
pub const fn same_encoding<A: Encoding, B: Encoding>() -> bool {
A::KIND as u8 == B::KIND as u8 && A::FOREIGN == B::FOREIGN
}
pub struct Codepoints<'a, E: Encoding> {
rest: &'a [E::Unit],
_encoding: PhantomData<E>,
}
impl<E: Encoding> Clone for Codepoints<'_, E> {
#[inline(always)]
fn clone(&self) -> Self {
Self { rest: self.rest, _encoding: PhantomData }
}
}
impl<E: Encoding> Iterator for Codepoints<'_, E> {
type Item = u32;
#[inline]
fn next(&mut self) -> Option<u32> {
if self.rest.is_empty() {
None
} else {
Some(E::decode(&mut self.rest))
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.rest.len().div_ceil(E::MAX_UNITS), Some(self.rest.len()))
}
}
impl<E: Encoding> DoubleEndedIterator for Codepoints<'_, E> {
#[inline]
fn next_back(&mut self) -> Option<u32> {
if self.rest.is_empty() {
None
} else {
Some(E::decode_back(&mut self.rest))
}
}
}
impl<E: Encoding> core::iter::FusedIterator for Codepoints<'_, E> {}
#[inline]
pub const fn codepoints<E: Encoding>(input: &[E::Unit]) -> Codepoints<'_, E> {
Codepoints { rest: input, _encoding: PhantomData }
}
#[inline]
pub fn chars<E: Encoding>(input: &[E::Unit]) -> impl Iterator<Item = char> + '_ {
codepoints::<E>(input).map(|cp| char::from_u32(cp).unwrap_or(char::REPLACEMENT_CHARACTER))
}