use crate::{
Charset,
CharsetCodec,
CharsetDecodeError,
CharsetDecodeErrorKind,
CharsetDecodeResult,
CharsetEncodeError,
CharsetEncodeErrorKind,
CharsetEncodeProbe,
CharsetEncodeResult,
Unicode,
Utf32,
};
use qubit_codec::Codec;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct Utf32U32Codec;
impl Utf32U32Codec {
#[must_use]
#[inline(always)]
pub const fn charset(self) -> Charset {
Charset::UTF_32
}
}
impl CharsetCodec for Utf32U32Codec {
#[inline(always)]
fn charset(&self) -> Charset {
Charset::UTF_32
}
}
impl CharsetEncodeProbe for Utf32U32Codec {
#[inline(always)]
fn encode_len(
&self,
_ch: char,
_index: usize,
) -> CharsetEncodeResult<usize> {
Ok(Utf32::MAX_UNITS_PER_CHAR)
}
}
unsafe impl Codec for Utf32U32Codec {
type Value = char;
type Unit = u32;
type DecodeError = CharsetDecodeError;
type EncodeError = CharsetEncodeError;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
core::num::NonZeroUsize::MIN
}
#[inline(always)]
fn max_units_per_value(&self) -> core::num::NonZeroUsize {
core::num::NonZeroUsize::MIN
}
#[inline(always)]
unsafe fn decode_unchecked(
&self,
input: &[u32],
index: usize,
) -> CharsetDecodeResult<(char, core::num::NonZeroUsize)> {
let (ch, consumed) = decode_units_prefix(input, index)?;
debug_assert!(consumed.get() <= input.len() - index);
Ok((ch, consumed))
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
ch: &char,
output: &mut [u32],
index: usize,
) -> CharsetEncodeResult<usize> {
let written = encode_units_char(*ch, output, index)?;
debug_assert_eq!(written, Utf32::MAX_UNITS_PER_CHAR);
debug_assert!(written <= output.len() - index);
Ok(written)
}
}
#[inline]
fn decode_units_prefix(
input: &[u32],
index: usize,
) -> CharsetDecodeResult<(char, core::num::NonZeroUsize)> {
if index > input.len() {
let kind = CharsetDecodeErrorKind::InvalidInputIndex {
input_len: input.len(),
};
return Err(CharsetDecodeError::new(Charset::UTF_32, kind, index));
}
if index == input.len() {
let kind = CharsetDecodeErrorKind::IncompleteSequence {
required: 1,
available: 0,
};
return Err(CharsetDecodeError::new(Charset::UTF_32, kind, index));
}
match Unicode::to_char(input[index]) {
Some(ch) => Ok((ch, core::num::NonZeroUsize::MIN)),
None => {
let kind = CharsetDecodeErrorKind::InvalidCodePoint {
value: input[index],
};
Err(CharsetDecodeError::new(Charset::UTF_32, kind, index))
}
}
}
#[inline]
fn encode_units_char(
ch: char,
output: &mut [u32],
index: usize,
) -> CharsetEncodeResult<usize> {
if index >= output.len() {
let kind = CharsetEncodeErrorKind::BufferTooSmall {
required: required_index(index, 1),
available: 0,
};
return Err(CharsetEncodeError::new(Charset::UTF_32, kind, index));
}
output[index] = ch as u32;
Ok(1)
}
#[inline(always)]
const fn required_index(index: usize, required_units: usize) -> usize {
match index.checked_add(required_units) {
Some(required) => required,
None => usize::MAX,
}
}