use crate::char_from_u16;
use crate::char_from_u32;
use crate::in_inclusive_range;
use crate::provider::CanonicalCompositions;
use crate::provider::DecompositionData;
use crate::provider::DecompositionTables;
use crate::provider::NonRecursiveDecompositionSupplement;
use crate::provider::NormalizerNfcV1;
use crate::provider::NormalizerNfdDataV1;
use crate::provider::NormalizerNfdSupplementV1;
use crate::provider::NormalizerNfdTablesV1;
use crate::trie_value_has_ccc;
use crate::CanonicalCombiningClass;
use crate::BACKWARD_COMBINING_MARKER;
use crate::FDFA_MARKER;
use crate::HANGUL_L_BASE;
use crate::HANGUL_N_COUNT;
use crate::HANGUL_S_BASE;
use crate::HANGUL_S_COUNT;
use crate::HANGUL_T_BASE;
use crate::HANGUL_T_COUNT;
use crate::HANGUL_V_BASE;
use crate::HIGH_ZEROS_MASK;
use crate::LOW_ZEROS_MASK;
use crate::NON_ROUND_TRIP_MARKER;
use icu_provider::prelude::*;
#[derive(Debug, Copy, Clone)]
pub struct CanonicalCompositionBorrowed<'a> {
canonical_compositions: &'a CanonicalCompositions<'a>,
}
#[cfg(feature = "compiled_data")]
impl Default for CanonicalCompositionBorrowed<'static> {
fn default() -> Self {
Self::new()
}
}
impl CanonicalCompositionBorrowed<'static> {
pub const fn static_to_owned(self) -> CanonicalComposition {
CanonicalComposition {
canonical_compositions: DataPayload::from_static_ref(self.canonical_compositions),
}
}
#[cfg(feature = "compiled_data")]
pub const fn new() -> Self {
Self {
canonical_compositions: crate::provider::Baked::SINGLETON_NORMALIZER_NFC_V1,
}
}
}
impl CanonicalCompositionBorrowed<'_> {
#[inline(always)]
pub fn compose(self, starter: char, second: char) -> Option<char> {
crate::compose(
self.canonical_compositions.canonical_compositions.iter(),
starter,
second,
)
}
}
#[derive(Debug)]
pub struct CanonicalComposition {
canonical_compositions: DataPayload<NormalizerNfcV1>,
}
#[cfg(feature = "compiled_data")]
impl Default for CanonicalComposition {
fn default() -> Self {
Self::new().static_to_owned()
}
}
impl CanonicalComposition {
pub fn as_borrowed(&self) -> CanonicalCompositionBorrowed<'_> {
CanonicalCompositionBorrowed {
canonical_compositions: self.canonical_compositions.get(),
}
}
#[cfg(feature = "compiled_data")]
#[expect(clippy::new_ret_no_self)]
pub const fn new() -> CanonicalCompositionBorrowed<'static> {
CanonicalCompositionBorrowed::new()
}
icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
functions: [
new: skip,
try_new_with_buffer_provider,
try_new_unstable,
Self,
]
);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
pub fn try_new_unstable<D>(provider: &D) -> Result<Self, DataError>
where
D: DataProvider<NormalizerNfcV1> + ?Sized,
{
let canonical_compositions: DataPayload<NormalizerNfcV1> =
provider.load(Default::default())?.payload;
Ok(CanonicalComposition {
canonical_compositions,
})
}
}
#[allow(clippy::exhaustive_enums)]
#[derive(Debug, PartialEq, Eq)]
pub enum Decomposed {
Default,
Singleton(char),
Expansion(char, char),
}
#[derive(Debug)]
pub struct CanonicalDecompositionBorrowed<'a> {
decompositions: &'a DecompositionData<'a>,
tables: &'a DecompositionTables<'a>,
non_recursive: &'a NonRecursiveDecompositionSupplement<'a>,
}
#[cfg(feature = "compiled_data")]
impl Default for CanonicalDecompositionBorrowed<'static> {
fn default() -> Self {
Self::new()
}
}
impl CanonicalDecompositionBorrowed<'static> {
pub const fn static_to_owned(self) -> CanonicalDecomposition {
CanonicalDecomposition {
decompositions: DataPayload::from_static_ref(self.decompositions),
tables: DataPayload::from_static_ref(self.tables),
non_recursive: DataPayload::from_static_ref(self.non_recursive),
}
}
#[cfg(feature = "compiled_data")]
pub const fn new() -> Self {
const _: () = assert!(
crate::provider::Baked::SINGLETON_NORMALIZER_NFD_TABLES_V1
.scalars16
.const_len()
+ crate::provider::Baked::SINGLETON_NORMALIZER_NFD_TABLES_V1
.scalars24
.const_len()
<= 0xFFF,
"future extension"
);
Self {
decompositions: crate::provider::Baked::SINGLETON_NORMALIZER_NFD_DATA_V1,
tables: crate::provider::Baked::SINGLETON_NORMALIZER_NFD_TABLES_V1,
non_recursive: crate::provider::Baked::SINGLETON_NORMALIZER_NFD_SUPPLEMENT_V1,
}
}
}
impl CanonicalDecompositionBorrowed<'_> {
#[inline]
pub fn decompose(&self, c: char) -> Decomposed {
let lvt = u32::from(c).wrapping_sub(HANGUL_S_BASE);
if lvt >= HANGUL_S_COUNT {
return self.decompose_non_hangul(c);
}
let t = lvt % HANGUL_T_COUNT;
if t == 0 {
let l = lvt / HANGUL_N_COUNT;
let v = (lvt % HANGUL_N_COUNT) / HANGUL_T_COUNT;
return Decomposed::Expansion(
unsafe { char::from_u32_unchecked(HANGUL_L_BASE + l) },
unsafe { char::from_u32_unchecked(HANGUL_V_BASE + v) },
);
}
let lv = lvt - t;
Decomposed::Expansion(
unsafe { char::from_u32_unchecked(HANGUL_S_BASE + lv) },
unsafe { char::from_u32_unchecked(HANGUL_T_BASE + t) },
)
}
#[inline(always)]
fn decompose_non_hangul(&self, c: char) -> Decomposed {
let decomposition = self.decompositions.trie.get(c);
if (decomposition & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER)) == 0 {
return Decomposed::Default;
}
#[expect(clippy::never_loop)]
loop {
let high_zeros = (decomposition & HIGH_ZEROS_MASK) == 0;
let low_zeros = (decomposition & LOW_ZEROS_MASK) == 0;
if !high_zeros && !low_zeros {
if in_inclusive_range(c, '\u{1F71}', '\u{1FFB}') {
break;
}
let starter = char_from_u32(decomposition & 0x7FFF);
let combining = char_from_u32((decomposition >> 15) & 0x7FFF);
return Decomposed::Expansion(starter, combining);
}
if high_zeros {
if trie_value_has_ccc(decomposition) {
if !in_inclusive_range(c, '\u{0340}', '\u{0F81}') {
return Decomposed::Default;
}
return match c {
'\u{0340}' => {
Decomposed::Singleton('\u{0300}')
}
'\u{0341}' => {
Decomposed::Singleton('\u{0301}')
}
'\u{0343}' => {
Decomposed::Singleton('\u{0313}')
}
'\u{0344}' => {
Decomposed::Expansion('\u{0308}', '\u{0301}')
}
'\u{0F73}' => {
Decomposed::Expansion('\u{0F71}', '\u{0F72}')
}
'\u{0F75}' => {
Decomposed::Expansion('\u{0F71}', '\u{0F74}')
}
'\u{0F81}' => {
Decomposed::Expansion('\u{0F71}', '\u{0F80}')
}
_ => Decomposed::Default,
};
}
let singleton = decomposition as u16;
debug_assert_ne!(
singleton, FDFA_MARKER,
"How come we got the U+FDFA NFKD marker here?"
);
return Decomposed::Singleton(char_from_u16(singleton));
}
if c == '\u{212B}' {
return Decomposed::Singleton('\u{00C5}');
}
let offset = (((decomposition & !(0b11 << 30)) >> 16) as usize) - 1;
let len_bits = decomposition & 0b1111;
let tables = self.tables;
if offset < tables.scalars16.len() {
if len_bits != 0 {
break;
}
if let Some(first) = tables.scalars16.get(offset) {
if let Some(second) = tables.scalars16.get(offset + 1) {
return Decomposed::Expansion(char_from_u16(first), char_from_u16(second));
}
}
debug_assert!(false);
return Decomposed::Default;
}
let len = len_bits + 1;
if len > 2 {
break;
}
let offset24 = offset - tables.scalars16.len();
if let Some(first_c) = tables.scalars24.get(offset24) {
if len == 1 {
return Decomposed::Singleton(first_c);
}
if let Some(second_c) = tables.scalars24.get(offset24 + 1) {
return Decomposed::Expansion(first_c, second_c);
}
}
debug_assert!(false);
return Decomposed::Default;
}
let non_recursive = self.non_recursive;
let non_recursive_decomposition = non_recursive.trie.get(c);
if non_recursive_decomposition == 0 {
debug_assert!(false);
return Decomposed::Default;
}
let trail_or_complex = (non_recursive_decomposition >> 16) as u16;
let lead = non_recursive_decomposition as u16;
if lead != 0 && trail_or_complex != 0 {
return Decomposed::Expansion(char_from_u16(lead), char_from_u16(trail_or_complex));
}
if lead != 0 {
return Decomposed::Singleton(char_from_u16(lead));
}
let offset = usize::from(trail_or_complex - 1);
if let Some(first) = non_recursive.scalars24.get(offset) {
if let Some(second) = non_recursive.scalars24.get(offset + 1) {
return Decomposed::Expansion(first, second);
}
}
debug_assert!(false);
Decomposed::Default
}
}
#[derive(Debug)]
pub struct CanonicalDecomposition {
decompositions: DataPayload<NormalizerNfdDataV1>,
tables: DataPayload<NormalizerNfdTablesV1>,
non_recursive: DataPayload<NormalizerNfdSupplementV1>,
}
#[cfg(feature = "compiled_data")]
impl Default for CanonicalDecomposition {
fn default() -> Self {
Self::new().static_to_owned()
}
}
impl CanonicalDecomposition {
pub fn as_borrowed(&self) -> CanonicalDecompositionBorrowed<'_> {
CanonicalDecompositionBorrowed {
decompositions: self.decompositions.get(),
tables: self.tables.get(),
non_recursive: self.non_recursive.get(),
}
}
#[cfg(feature = "compiled_data")]
#[expect(clippy::new_ret_no_self)]
pub const fn new() -> CanonicalDecompositionBorrowed<'static> {
CanonicalDecompositionBorrowed::new()
}
icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
functions: [
new: skip,
try_new_with_buffer_provider,
try_new_unstable,
Self,
]
);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
pub fn try_new_unstable<D>(provider: &D) -> Result<Self, DataError>
where
D: DataProvider<NormalizerNfdDataV1>
+ DataProvider<NormalizerNfdTablesV1>
+ DataProvider<NormalizerNfdSupplementV1>
+ ?Sized,
{
let decompositions: DataPayload<NormalizerNfdDataV1> =
provider.load(Default::default())?.payload;
let tables: DataPayload<NormalizerNfdTablesV1> = provider.load(Default::default())?.payload;
if tables.get().scalars16.len() + tables.get().scalars24.len() > 0xFFF {
return Err(DataError::custom("future extension"));
}
let non_recursive: DataPayload<NormalizerNfdSupplementV1> =
provider.load(Default::default())?.payload;
Ok(CanonicalDecomposition {
decompositions,
tables,
non_recursive,
})
}
}
#[derive(Debug)]
pub struct CanonicalCombiningClassMapBorrowed<'a> {
decompositions: &'a DecompositionData<'a>,
}
#[cfg(feature = "compiled_data")]
impl Default for CanonicalCombiningClassMapBorrowed<'static> {
fn default() -> Self {
Self::new()
}
}
impl CanonicalCombiningClassMapBorrowed<'static> {
pub const fn static_to_owned(self) -> CanonicalCombiningClassMap {
CanonicalCombiningClassMap {
decompositions: DataPayload::from_static_ref(self.decompositions),
}
}
#[cfg(feature = "compiled_data")]
pub const fn new() -> Self {
CanonicalCombiningClassMapBorrowed {
decompositions: crate::provider::Baked::SINGLETON_NORMALIZER_NFD_DATA_V1,
}
}
}
impl CanonicalCombiningClassMapBorrowed<'_> {
#[inline(always)]
pub fn get_u8(&self, c: char) -> u8 {
let trie_value = self.decompositions.trie.get(c);
if trie_value_has_ccc(trie_value) {
trie_value as u8
} else {
ccc!(NotReordered, 0).to_icu4c_value()
}
}
pub fn get32_u8(&self, c: u32) -> u8 {
let trie_value = self.decompositions.trie.get32(c);
if trie_value_has_ccc(trie_value) {
trie_value as u8
} else {
ccc!(NotReordered, 0).to_icu4c_value()
}
}
#[inline(always)]
#[cfg(feature = "icu_properties")]
pub fn get(&self, c: char) -> CanonicalCombiningClass {
CanonicalCombiningClass::from_icu4c_value(self.get_u8(c))
}
#[cfg(feature = "icu_properties")]
pub fn get32(&self, c: u32) -> CanonicalCombiningClass {
CanonicalCombiningClass::from_icu4c_value(self.get32_u8(c))
}
}
#[derive(Debug)]
pub struct CanonicalCombiningClassMap {
decompositions: DataPayload<NormalizerNfdDataV1>,
}
#[cfg(feature = "compiled_data")]
impl Default for CanonicalCombiningClassMap {
fn default() -> Self {
Self::new().static_to_owned()
}
}
impl CanonicalCombiningClassMap {
pub fn as_borrowed(&self) -> CanonicalCombiningClassMapBorrowed<'_> {
CanonicalCombiningClassMapBorrowed {
decompositions: self.decompositions.get(),
}
}
#[cfg(feature = "compiled_data")]
#[expect(clippy::new_ret_no_self)]
pub const fn new() -> CanonicalCombiningClassMapBorrowed<'static> {
CanonicalCombiningClassMapBorrowed::new()
}
icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
functions: [
new: skip,
try_new_with_buffer_provider,
try_new_unstable,
Self,
]);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
pub fn try_new_unstable<D>(provider: &D) -> Result<Self, DataError>
where
D: DataProvider<NormalizerNfdDataV1> + ?Sized,
{
let decompositions: DataPayload<NormalizerNfdDataV1> =
provider.load(Default::default())?.payload;
Ok(CanonicalCombiningClassMap { decompositions })
}
}