use crate::{
ByteOrder,
Charset,
CharsetCodec,
CharsetDecodeError,
CharsetDecodeErrorKind,
CharsetDecodeResult,
CharsetEncodeError,
CharsetEncodeErrorKind,
CharsetEncodeProbe,
CharsetEncodeResult,
Unicode,
Utf32,
};
use qubit_codec::Codec;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Utf32ByteCodec {
byte_order: ByteOrder,
}
impl Utf32ByteCodec {
#[must_use]
#[inline(always)]
pub const fn new(byte_order: ByteOrder) -> Self {
Self { byte_order }
}
#[must_use]
#[inline(always)]
pub const fn byte_order(self) -> ByteOrder {
self.byte_order
}
#[must_use]
#[inline(always)]
pub const fn charset(self) -> Charset {
Charset::from_utf32_byte_order(self.byte_order)
}
}
impl CharsetCodec for Utf32ByteCodec {
#[inline(always)]
fn charset(&self) -> Charset {
Charset::from_utf32_byte_order(self.byte_order)
}
}
impl CharsetEncodeProbe for Utf32ByteCodec {
#[inline(always)]
fn encode_len(
&self,
_ch: char,
_index: usize,
) -> CharsetEncodeResult<usize> {
Ok(Utf32::MAX_BYTES_PER_CHAR)
}
}
unsafe impl Codec for Utf32ByteCodec {
type Value = char;
type Unit = u8;
type DecodeError = CharsetDecodeError;
type EncodeError = CharsetEncodeError;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe { core::num::NonZeroUsize::new_unchecked(4) }
}
#[inline(always)]
fn max_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(Utf32::MAX_BYTES_PER_CHAR)
}
}
#[inline(always)]
unsafe fn decode_unchecked(
&self,
input: &[u8],
index: usize,
) -> CharsetDecodeResult<(char, core::num::NonZeroUsize)> {
let (ch, consumed) =
decode_bytes_prefix(input, index, self.byte_order)?;
debug_assert!(consumed.get() <= input.len() - index);
Ok((ch, consumed))
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
ch: &char,
output: &mut [u8],
index: usize,
) -> CharsetEncodeResult<usize> {
let written = encode_bytes_char(*ch, output, self.byte_order, index)?;
debug_assert_eq!(written, Utf32::MAX_BYTES_PER_CHAR);
debug_assert!(written <= output.len() - index);
Ok(written)
}
}
#[inline]
fn decode_bytes_prefix(
input: &[u8],
index: usize,
byte_order: ByteOrder,
) -> CharsetDecodeResult<(char, core::num::NonZeroUsize)> {
let charset = Charset::from_utf32_byte_order(byte_order);
if index > input.len() {
let kind = CharsetDecodeErrorKind::InvalidInputIndex {
input_len: input.len(),
};
return Err(CharsetDecodeError::new(charset, kind, index));
}
let available = input.len() - index;
if available < 4 {
let kind = CharsetDecodeErrorKind::IncompleteSequence {
required: 4,
available,
};
return Err(CharsetDecodeError::new(charset, kind, index));
}
let unit = read_ordered_u32(input, index, byte_order);
match Unicode::to_char(unit) {
Some(ch) => {
Ok((ch, unsafe { core::num::NonZeroUsize::new_unchecked(4) }))
}
None => {
let kind = CharsetDecodeErrorKind::InvalidCodePoint { value: unit };
Err(CharsetDecodeError::new(charset, kind, index).with_consumed(4))
}
}
}
#[inline]
fn encode_bytes_char(
ch: char,
output: &mut [u8],
byte_order: ByteOrder,
index: usize,
) -> CharsetEncodeResult<usize> {
let charset = Charset::from_utf32_byte_order(byte_order);
if index > output.len() {
let kind = CharsetEncodeErrorKind::BufferTooSmall {
required: required_index(index, 4),
available: 0,
};
return Err(CharsetEncodeError::new(charset, kind, index));
}
let required = 4;
let available = output.len() - index;
if available < required {
let kind = CharsetEncodeErrorKind::BufferTooSmall {
required: required_index(index, required),
available,
};
return Err(CharsetEncodeError::new(charset, kind, index));
}
write_ordered_u32(output, index, ch as u32, byte_order);
Ok(4)
}
#[inline(always)]
const fn required_index(index: usize, required_units: usize) -> usize {
match index.checked_add(required_units) {
Some(required) => required,
None => usize::MAX,
}
}
#[inline(always)]
fn read_ordered_u32(input: &[u8], index: usize, byte_order: ByteOrder) -> u32 {
let bytes = [
input[index],
input[index + 1],
input[index + 2],
input[index + 3],
];
match byte_order {
ByteOrder::BigEndian => u32::from_be_bytes(bytes),
ByteOrder::LittleEndian => u32::from_le_bytes(bytes),
}
}
#[inline(always)]
fn write_ordered_u32(
output: &mut [u8],
index: usize,
unit: u32,
byte_order: ByteOrder,
) {
let bytes = match byte_order {
ByteOrder::BigEndian => unit.to_be_bytes(),
ByteOrder::LittleEndian => unit.to_le_bytes(),
};
output[index..index + 4].copy_from_slice(&bytes);
}