use displaydoc::Display;
use icu_collections::codepointtrie::{CodePointTrie, TrieValue};
use icu_provider::prelude::*;
use zerovec::ule::{AsULE, CharULE, ULE};
use zerovec::ZeroVecError;
#[icu_provider::data_struct(marker(
BidiAuxiliaryPropertiesV1Marker,
"props/bidiauxiliaryprops@1",
singleton
))]
#[derive(Debug, Eq, PartialEq, Clone)]
#[cfg_attr(
feature = "datagen",
derive(serde::Serialize, databake::Bake),
databake(path = icu_properties::provider::bidi_data),
)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct BidiAuxiliaryPropertiesV1<'data> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub trie: CodePointTrie<'data, MirroredPairedBracketData>,
}
impl<'data> BidiAuxiliaryPropertiesV1<'data> {
#[doc(hidden)]
pub fn new(
trie: CodePointTrie<'data, MirroredPairedBracketData>,
) -> BidiAuxiliaryPropertiesV1<'data> {
BidiAuxiliaryPropertiesV1 { trie }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_properties::provider::bidi_data))]
#[doc(hidden)] pub struct MirroredPairedBracketData {
pub mirroring_glyph: char,
pub mirrored: bool,
pub paired_bracket_type: CheckedBidiPairedBracketType,
}
impl Default for MirroredPairedBracketData {
fn default() -> Self {
Self {
mirroring_glyph: 0 as char,
mirrored: false,
paired_bracket_type: CheckedBidiPairedBracketType::None,
}
}
}
impl From<MirroredPairedBracketData> for u32 {
fn from(mpbd: MirroredPairedBracketData) -> u32 {
let mut result = mpbd.mirroring_glyph as u32;
result |= (mpbd.mirrored as u32) << 21;
result |= (mpbd.paired_bracket_type as u32) << 22;
result
}
}
#[derive(Display, Debug, Clone, Copy, PartialEq, Eq)]
#[displaydoc("Invalid MirroredPairedBracketData serialized in int: {0}")]
pub struct MirroredPairedBracketDataTryFromError(u32);
impl TryFrom<u32> for MirroredPairedBracketData {
type Error = MirroredPairedBracketDataTryFromError;
fn try_from(i: u32) -> Result<Self, MirroredPairedBracketDataTryFromError> {
let code_point = i & 0x1FFFFF;
let mirroring_glyph =
char::try_from_u32(code_point).map_err(|_| MirroredPairedBracketDataTryFromError(i))?;
let mirrored = ((i >> 21) & 0x1) == 1;
let paired_bracket_type = {
let value = ((i >> 22) & 0x3) as u8;
match value {
0 => CheckedBidiPairedBracketType::None,
1 => CheckedBidiPairedBracketType::Open,
2 => CheckedBidiPairedBracketType::Close,
_ => {
return Err(MirroredPairedBracketDataTryFromError(i));
}
}
};
Ok(MirroredPairedBracketData {
mirroring_glyph,
mirrored,
paired_bracket_type,
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_properties::provider::bidi_data))]
#[repr(u8)]
#[zerovec::make_ule(CheckedBidiPairedBracketTypeULE)]
#[allow(clippy::exhaustive_enums)]
pub enum CheckedBidiPairedBracketType {
None = 0,
Open = 1,
Close = 2,
}
#[doc(hidden)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
#[repr(packed)]
pub struct MirroredPairedBracketDataULE([u8; 3]);
unsafe impl ULE for MirroredPairedBracketDataULE {
#[inline]
fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> {
if bytes.len() % 3 != 0 {
return Err(ZeroVecError::length::<Self>(bytes.len()));
}
#[allow(clippy::indexing_slicing)] for byte_triple in bytes.chunks_exact(3) {
#[allow(clippy::unwrap_used)] let [byte0, byte1, byte2] = *<&[u8; 3]>::try_from(byte_triple).unwrap();
let mut mirroring_glyph_code_point: u32 = (byte2 & 0x1F) as u32;
mirroring_glyph_code_point = (mirroring_glyph_code_point << 8) | (byte1 as u32);
mirroring_glyph_code_point = (mirroring_glyph_code_point << 8) | (byte0 as u32);
let _mirroring_glyph =
char::from_u32(mirroring_glyph_code_point).ok_or(ZeroVecError::parse::<Self>())?;
if (byte2 & 0xC0) == 0xC0 {
return Err(ZeroVecError::parse::<Self>());
}
}
Ok(())
}
}
impl AsULE for MirroredPairedBracketData {
type ULE = MirroredPairedBracketDataULE;
#[inline]
fn to_unaligned(self) -> Self::ULE {
let mut ch = u32::from(self.mirroring_glyph);
ch |= u32::from(self.mirrored) << 21;
ch |= (self.paired_bracket_type as u32) << 22;
let [byte0, byte1, byte2, _] = ch.to_le_bytes();
MirroredPairedBracketDataULE([byte0, byte1, byte2])
}
#[inline]
fn from_unaligned(unaligned: Self::ULE) -> Self {
let [unaligned_byte0, unaligned_byte1, unaligned_byte2] = unaligned.0;
let mirroring_glyph_ule_bytes = &[unaligned_byte0, unaligned_byte1, unaligned_byte2 & 0x1F];
let mirroring_glyph_ule =
unsafe { CharULE::from_byte_slice_unchecked(mirroring_glyph_ule_bytes) };
let mirroring_glyph = mirroring_glyph_ule
.first()
.map(|ule| char::from_unaligned(*ule))
.unwrap_or(char::REPLACEMENT_CHARACTER);
let mirrored = ((unaligned.0[2] >> 5) & 0x1) == 1;
let paired_bracket_type = {
let discriminant = unaligned.0[2] >> 6;
debug_assert!(
discriminant != 3,
"Bidi_Paired_Bracket_Type can only be Open/Close/None in MirroredPairedBracketData"
);
match discriminant {
1 => CheckedBidiPairedBracketType::Open,
2 => CheckedBidiPairedBracketType::Close,
_ => CheckedBidiPairedBracketType::None,
}
};
MirroredPairedBracketData {
mirroring_glyph,
mirrored,
paired_bracket_type,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() {
let data = MirroredPairedBracketData {
mirroring_glyph: '}',
mirrored: true,
paired_bracket_type: CheckedBidiPairedBracketType::Open,
};
let expected_bytes = &[0x7D, 0x0, 0x60];
assert_eq!(
expected_bytes,
MirroredPairedBracketDataULE::as_byte_slice(&[data.to_unaligned()])
);
let ule = MirroredPairedBracketDataULE::parse_byte_slice(expected_bytes).unwrap();
let parsed_data = MirroredPairedBracketData::from_unaligned(*ule.first().unwrap());
assert_eq!(data, parsed_data);
}
#[test]
fn test_parse_error() {
let ule_bytes = &mut [0x7D, 0x0, 0x60];
ule_bytes[2] |= 0xC0;
let ule_parse_result = MirroredPairedBracketDataULE::parse_byte_slice(ule_bytes);
assert!(ule_parse_result.is_err());
}
}