use crate::wtf8::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct JSChar(u32);
impl JSChar {
#[inline]
pub const fn from_u32(value: u32) -> Option<Self> {
if value <= CODE_POINT_MAX { Some(Self(value)) } else { None }
}
#[inline]
pub const fn to_u32(self) -> u32 {
self.0
}
#[inline]
pub const fn to_char(self) -> Option<char> {
if self.is_surrogate() {
None
} else {
Some(unsafe { char::from_u32_unchecked(self.0) })
}
}
#[inline]
pub(super) const unsafe fn from_u32_unchecked(value: u32) -> Self {
Self(value)
}
#[inline]
pub(super) const fn from_code_unit(unit: u16) -> Self {
Self(unit as u32)
}
#[inline]
pub(super) const fn is_surrogate(self) -> bool {
self.0 >= LEAD_SURROGATE_MIN && self.0 <= TRAIL_SURROGATE_MAX
}
#[inline]
pub(super) const fn is_lead_surrogate(self) -> bool {
self.0 >= LEAD_SURROGATE_MIN && self.0 <= LEAD_SURROGATE_MAX
}
#[inline]
pub(super) const fn is_trail_surrogate(self) -> bool {
self.0 >= TRAIL_SURROGATE_MIN && self.0 <= TRAIL_SURROGATE_MAX
}
#[inline]
#[expect(clippy::cast_possible_truncation, reason = "each byte is masked or range-checked")]
pub(super) fn encode(self, buffer: &mut [u8; char::MAX_LEN_UTF8]) -> &[u8] {
let value = self.0;
let len = match value {
0..=ONE_BYTE_CODE_POINT_MAX => {
buffer[0] = value as u8;
1
}
TWO_BYTE_CODE_POINT_MIN..=TWO_BYTE_CODE_POINT_MAX => {
buffer[0] = TWO_BYTE_TAG | (value >> 6) as u8;
buffer[1] = CONT_TAG | (value as u8 & CONT_MASK);
2
}
THREE_BYTE_CODE_POINT_MIN..=THREE_BYTE_CODE_POINT_MAX => {
buffer[0] = THREE_BYTE_TAG | (value >> 12) as u8;
buffer[1] = CONT_TAG | ((value >> 6) as u8 & CONT_MASK);
buffer[2] = CONT_TAG | (value as u8 & CONT_MASK);
3
}
_ => {
buffer[0] = FOUR_BYTE_TAG | (value >> 18) as u8;
buffer[1] = CONT_TAG | ((value >> 12) as u8 & CONT_MASK);
buffer[2] = CONT_TAG | ((value >> 6) as u8 & CONT_MASK);
buffer[3] = CONT_TAG | (value as u8 & CONT_MASK);
4
}
};
&buffer[..len]
}
}