1#![allow(clippy::exhaustive_structs, clippy::exhaustive_enums)]
20
21use icu_collections::char16trie::Char16TrieIterator;
22use icu_collections::codepointtrie::SmallCodePointTrie;
23use icu_collections::codepointtrie::TypedCodePointTrie;
24use icu_provider::prelude::*;
25use zerovec::ule::AsULE;
26use zerovec::ZeroVec;
27use zerovec::{zeroslice, ZeroSlice};
28
29use crate::elements::CollationElement;
30
31#[cfg(not(feature = "datagen"))]
32use crate::elements::CollationElement32;
33#[cfg(not(feature = "datagen"))]
34use crate::elements::Tag;
35
36#[cfg(feature = "datagen")]
37pub use crate::elements::CollationElement32;
38#[cfg(feature = "datagen")]
39pub use crate::elements::Tag;
40#[cfg(feature = "datagen")]
41pub use crate::elements::COMMON_SEC_AND_TER_CE32;
42#[cfg(feature = "datagen")]
43pub use crate::elements::LONG_PRIMARY_FOR_REORDERABLE_CE32_LOW_BYTE;
44#[cfg(feature = "datagen")]
45pub use crate::elements::MAX_INDEX;
46#[cfg(feature = "datagen")]
47pub use crate::elements::SPECIAL_CE32_LOW_BYTE;
48
49use crate::elements::EMPTY_U16;
50use crate::elements::FFFD_CE;
51use crate::elements::FFFD_CE32;
52use crate::elements::FFFD_CE32_VALUE;
53use crate::elements::FFFD_CE_VALUE;
54use crate::elements::FFFD_PRIMARY;
55use crate::elements::NO_CE_PRIMARY;
56use crate::preferences::CollationCaseFirst;
57
58use crate::options::MaxVariable;
59
60#[cfg(feature = "compiled_data")]
61#[derive(Debug)]
62pub struct Baked;
70
71#[cfg(feature = "compiled_data")]
72#[allow(unused_imports)]
73const _: () = {
74 use icu_collator_data::*;
75 pub mod icu {
76 pub use crate as collator;
77 pub use icu_collections as collections;
78 pub use icu_locale as locale;
79 }
80 make_provider!(Baked);
81 impl_collation_root_v1!(Baked);
82 impl_collation_tailoring_v1!(Baked);
83 impl_collation_diacritics_v1!(Baked);
84 impl_collation_jamo_v1!(Baked);
85 impl_collation_metadata_v1!(Baked);
86 impl_collation_special_primaries_v1!(Baked);
87 impl_collation_reordering_v1!(Baked);
88};
89
90const SCRIPT_FALLBACK: icu_provider::fallback::LocaleFallbackConfig = {
91 let mut c = icu_provider::fallback::LocaleFallbackConfig::default();
92 c.priority = icu_provider::fallback::LocaleFallbackPriority::Script;
93 c
94};
95
96icu_provider::data_marker!(
97 CollationRootV1,
99 "collation/root/v1",
100 CollationData<'static>,
101 is_singleton = true,
102);
103icu_provider::data_marker!(
104 CollationTailoringV1,
106 "collation/tailoring/v1",
107 CollationData<'static>,
108 fallback_config = SCRIPT_FALLBACK,
109 #[cfg(feature = "datagen")]
110 attributes_domain = "collator",
111);
112icu_provider::data_marker!(
113 CollationDiacriticsV1,
115 "collation/diacritics/v1",
116 CollationDiacritics<'static>,
117 fallback_config = SCRIPT_FALLBACK,
118 #[cfg(feature = "datagen")]
119 attributes_domain = "collator",
120);
121icu_provider::data_marker!(
122 CollationJamoV1,
124 "collation/jamo/v1",
125 CollationJamo<'static>,
126 is_singleton = true,
127);
128icu_provider::data_marker!(
129 CollationReorderingV1,
131 "collation/reordering/v1",
132 CollationReordering<'static>,
133 fallback_config = SCRIPT_FALLBACK,
134 #[cfg(feature = "datagen")]
135 attributes_domain = "collator",
136);
137icu_provider::data_marker!(
138 CollationMetadataV1,
140 "collation/metadata/v1",
141 CollationMetadata,
142 fallback_config = SCRIPT_FALLBACK,
143 #[cfg(feature = "datagen")]
144 attributes_domain = "collator",
145);
146icu_provider::data_marker!(
147 CollationSpecialPrimariesV1,
149 "collation/special/primaries/v1",
150 CollationSpecialPrimaries<'static>,
151 is_singleton = true,
152);
153
154#[cfg(feature = "datagen")]
155pub const MARKERS: &[DataMarkerInfo] = &[
157 CollationRootV1::INFO,
158 CollationTailoringV1::INFO,
159 CollationDiacriticsV1::INFO,
160 CollationJamoV1::INFO,
161 CollationMetadataV1::INFO,
162 CollationReorderingV1::INFO,
163 CollationSpecialPrimariesV1::INFO,
164];
165
166const SINGLE_U32: &ZeroSlice<u32> =
167 zeroslice!(u32; <u32 as AsULE>::ULE::from_unsigned; [FFFD_CE32_VALUE]);
168const SINGLE_U64: &ZeroSlice<u64> =
169 zeroslice!(u64; <u64 as AsULE>::ULE::from_unsigned; [FFFD_CE_VALUE]);
170
171fn data_ce_to_primary(data_ce: u64, c: char) -> u32 {
172 let p = (data_ce >> 32) as u32; let lower32 = data_ce as u32 as i32; let mut offset = ((u32::from(c) as i32) - (lower32 >> 8)) * (lower32 & 0x7F); let is_compressible = (lower32 & 0x80) != 0;
177 offset += (((p >> 8) & 0xFF) as i32) - 2;
179 let mut primary = (((offset % 254) + 2) as u32) << 8;
180 offset /= 254;
181 if is_compressible {
184 offset += (((p >> 16) & 0xFF) as i32) - 4;
185 primary |= (((offset % 251) + 4) as u32) << 16;
186 offset /= 251;
187 } else {
188 offset += (((p >> 16) & 0xFF) as i32) - 2;
189 primary |= (((offset % 254) + 2) as u32) << 16;
190 offset /= 254;
191 }
192 primary | ((p & 0xFF000000) + ((offset as u32) << 24))
193}
194
195#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
203#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
204#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
205#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
206pub struct CollationData<'data> {
207 #[cfg_attr(feature = "serde", serde(borrow))]
210 pub trie: SmallCodePointTrie<'data, u32>,
211 #[cfg_attr(feature = "serde", serde(borrow))]
214 pub ces: ZeroVec<'data, u64>,
215 #[cfg_attr(feature = "serde", serde(borrow))]
218 pub ce32s: ZeroVec<'data, u32>,
219 #[cfg_attr(feature = "serde", serde(borrow))]
221 pub contexts: ZeroVec<'data, u16>,
222}
223
224icu_provider::data_struct!(
225 CollationData<'_>,
226 #[cfg(feature = "datagen")]
227);
228
229impl<'data> CollationData<'data> {
230 pub(crate) fn ce32_for_char(&self, c: char) -> CollationElement32 {
231 CollationElement32::new(self.trie.get(c))
232 }
233 pub(crate) fn get_ce32(&'data self, index: usize) -> CollationElement32 {
234 CollationElement32::new(if let Some(u) = self.ce32s.get(index) {
235 u
236 } else {
237 debug_assert!(false);
239 FFFD_CE32_VALUE
240 })
241 }
242 pub(crate) fn get_ce32s(&'data self, index: usize, len: usize) -> &'data ZeroSlice<u32> {
243 if len > 0 {
244 if let Some(slice) = self.ce32s.get_subslice(index..index + len) {
245 return slice;
246 }
247 }
248 debug_assert!(false);
250 SINGLE_U32
251 }
252 pub(crate) fn get_ces(&'data self, index: usize, len: usize) -> &'data ZeroSlice<u64> {
253 if len > 0 {
254 if let Some(slice) = self.ces.get_subslice(index..index + len) {
255 return slice;
256 }
257 }
258 debug_assert!(false);
260 SINGLE_U64
261 }
262 pub(crate) fn get_primary_from_ces(&'data self, index: usize) -> u32 {
263 if let Some(ce) = self.ces.get(index) {
264 (ce >> 32) as u32
265 } else {
266 debug_assert!(false);
268 FFFD_PRIMARY
269 }
270 }
271 fn get_default_and_trie_impl(
272 &'data self,
273 index: usize,
274 ) -> (CollationElement32, &'data ZeroSlice<u16>) {
275 if let Some(slice) = self.contexts.get_subslice(index..self.contexts.len()) {
276 #[expect(clippy::unwrap_used)]
277 if slice.len() >= 2 {
278 let first = slice.get(0).unwrap();
280 let second = slice.get(1).unwrap();
281 let trie = slice.get_subslice(2..slice.len()).unwrap();
282 return (
283 CollationElement32::new((u32::from(first) << 16) | u32::from(second)),
284 trie,
285 );
286 }
287 }
288 debug_assert!(false);
290 (FFFD_CE32, EMPTY_U16)
291 }
292 pub(crate) fn get_default_and_trie(
293 &'data self,
294 index: usize,
295 ) -> (CollationElement32, Char16TrieIterator<'data>) {
296 let (ce32, trie) = self.get_default_and_trie_impl(index);
297 (ce32, Char16TrieIterator::new(trie))
298 }
299 pub(crate) fn get_default(&'data self, index: usize) -> CollationElement32 {
300 let (ce32, _) = self.get_default_and_trie_impl(index);
301 ce32
302 }
303 pub(crate) fn ce_from_offset_ce32(
304 &self,
305 c: char,
306 ce32: CollationElement32,
307 ) -> CollationElement {
308 debug_assert!(ce32.tag() == Tag::Offset);
309 if let Some(data_ce) = self.ces.get(ce32.index()) {
310 CollationElement::new_from_primary(data_ce_to_primary(data_ce, c))
311 } else {
312 debug_assert!(false);
314 FFFD_CE
315 }
316 }
317}
318
319#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
327#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
328#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
329#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
330pub struct CollationDiacritics<'data> {
331 #[cfg_attr(feature = "serde", serde(borrow))]
336 pub secondaries: ZeroVec<'data, u16>,
337}
338
339icu_provider::data_struct!(
340 CollationDiacritics<'_>,
341 #[cfg(feature = "datagen")]
342);
343
344#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
352#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
353#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
354#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
355pub struct CollationJamo<'data> {
356 #[cfg_attr(feature = "serde", serde(borrow))]
359 pub ce32s: ZeroVec<'data, u32>,
360}
361
362icu_provider::data_struct!(
363 CollationJamo<'_>,
364 #[cfg(feature = "datagen")]
365);
366
367#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
375#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
376#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
377#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
378pub struct CollationReordering<'data> {
379 pub min_high_no_reorder: u32,
383 #[cfg_attr(feature = "serde", serde(borrow))]
391 pub reorder_table: ZeroVec<'data, u8>, #[cfg_attr(feature = "serde", serde(borrow))]
414 pub reorder_ranges: ZeroVec<'data, u32>,
415}
416
417icu_provider::data_struct!(
418 CollationReordering<'_>,
419 #[cfg(feature = "datagen")]
420);
421
422impl CollationReordering<'_> {
423 pub(crate) fn reorder(&self, primary: u32) -> u32 {
424 if let Some(b) = self.reorder_table.get((primary >> 24) as usize) {
425 if b != 0 || primary <= NO_CE_PRIMARY {
426 (u32::from(b) << 24) | (primary & 0x00FFFFFF)
427 } else {
428 self.reorder_ex(primary)
429 }
430 } else {
431 debug_assert!(false);
433 primary
434 }
435 }
436
437 fn reorder_ex(&self, primary: u32) -> u32 {
438 if primary >= self.min_high_no_reorder {
439 return primary;
440 }
441 let q = primary | 0xFFFF;
442 for &range in self.reorder_ranges.as_ule_slice().iter() {
443 let r = u32::from_unaligned(range);
444 if q < r {
445 return primary.wrapping_add(r << 24);
446 }
447 }
448 debug_assert!(false);
450 primary
451 }
452}
453
454#[derive(Debug, PartialEq, Clone, Copy, yoke::Yokeable, zerofrom::ZeroFrom)]
464#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
465#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
466#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
467pub struct CollationMetadata {
468 pub bits: u32,
478}
479
480icu_provider::data_struct!(
481 CollationMetadata,
482 #[cfg(feature = "datagen")]
483);
484
485impl CollationMetadata {
486 const MAX_VARIABLE_MASK: u32 = 0b11;
487 const TAILORED_MASK: u32 = 1 << 3;
488 const TAILORED_DIACRITICS_MASK: u32 = 1 << 4;
489 const REORDERING_MASK: u32 = 1 << 5;
490 const LITHUANIAN_DOT_ABOVE_MASK: u32 = 1 << 6;
491 const BACWARD_SECOND_LEVEL_MASK: u32 = 1 << 7;
492 const ALTERNATE_SHIFTED_MASK: u32 = 1 << 8;
493 const CASE_FIRST_MASK: u32 = 1 << 9;
494 const UPPER_FIRST_MASK: u32 = 1 << 10;
495
496 #[inline(always)]
497 pub(crate) fn max_variable(self) -> MaxVariable {
498 unsafe { core::mem::transmute((self.bits & CollationMetadata::MAX_VARIABLE_MASK) as u8) }
502 }
503
504 #[inline(always)]
505 pub(crate) fn tailored(self) -> bool {
506 self.bits & CollationMetadata::TAILORED_MASK != 0
507 }
508
509 #[inline(always)]
511 pub(crate) fn tailored_diacritics(self) -> bool {
512 self.bits & CollationMetadata::TAILORED_DIACRITICS_MASK != 0
513 }
514
515 #[inline(always)]
517 pub(crate) fn lithuanian_dot_above(self) -> bool {
518 self.bits & CollationMetadata::LITHUANIAN_DOT_ABOVE_MASK != 0
519 }
520
521 #[inline(always)]
523 pub(crate) fn backward_second_level(self) -> bool {
524 self.bits & CollationMetadata::BACWARD_SECOND_LEVEL_MASK != 0
525 }
526
527 #[inline(always)]
528 pub(crate) fn reordering(self) -> bool {
529 self.bits & CollationMetadata::REORDERING_MASK != 0
530 }
531
532 #[inline(always)]
534 pub(crate) fn alternate_shifted(self) -> bool {
535 self.bits & CollationMetadata::ALTERNATE_SHIFTED_MASK != 0
536 }
537
538 #[inline(always)]
539 pub(crate) fn case_first(self) -> CollationCaseFirst {
540 if self.bits & CollationMetadata::CASE_FIRST_MASK != 0 {
541 if self.bits & CollationMetadata::UPPER_FIRST_MASK != 0 {
542 CollationCaseFirst::Upper
543 } else {
544 CollationCaseFirst::Lower
545 }
546 } else {
547 CollationCaseFirst::False
548 }
549 }
550}
551
552#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
579#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
580#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
581#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
582pub struct CollationSpecialPrimaries<'data> {
583 #[cfg_attr(feature = "serde", serde(borrow))]
592 pub last_primaries: ZeroVec<'data, u16>,
593 pub numeric_primary: u8,
595}
596
597#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
598pub(crate) struct CollationSpecialPrimariesValidated<'data> {
599 pub last_primaries: ZeroVec<'data, u16>,
604 pub numeric_primary: u8,
606 pub compressible_bytes: &'data [<u16 as AsULE>::ULE; 16],
609}
610
611impl CollationSpecialPrimariesValidated<'static> {
612 pub(crate) const HARDCODED_COMPRESSIBLE_BYTES_FALLBACK: &'static [<u16 as AsULE>::ULE; 16] = &[
613 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
614 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
615 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
616 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
617 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
618 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
619 <u16 as AsULE>::ULE::from_unsigned(0b1111_1111_1111_1110),
620 <u16 as AsULE>::ULE::from_unsigned(0b1111_1111_1111_1111),
621 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0001),
622 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
623 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
624 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
625 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
626 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
627 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
628 <u16 as AsULE>::ULE::from_unsigned(0b0100_0000_0000_0000),
629 ];
630}
631
632icu_provider::data_struct!(
633 CollationSpecialPrimaries<'_>,
634 #[cfg(feature = "datagen")]
635);
636
637impl CollationSpecialPrimariesValidated<'_> {
638 #[expect(clippy::unwrap_used)]
639 pub(crate) fn last_primary_for_group(&self, max_variable: MaxVariable) -> u32 {
640 (u32::from(self.last_primaries.get(max_variable as usize).unwrap()) << 16) - 1
645 }
646
647 #[allow(dead_code)]
648 pub(crate) fn is_compressible(&self, b: u8) -> bool {
649 #[expect(clippy::indexing_slicing)]
653 let field = u16::from_unaligned(self.compressible_bytes[usize::from(b >> 4)]);
654 let mask = 1 << (b & 0b1111);
655 (field & mask) != 0
656 }
657}
658
659#[cfg(all(feature = "compiled_data", feature = "unstable"))]
667pub fn list_locales() -> impl Iterator<Item = (DataLocale, tinystr::TinyAsciiStr<8>)> {
668 use icu_provider::baked::DataStore;
669 Baked::DATA_COLLATION_METADATA_V1
670 .iter()
671 .chain(Baked::DATA_COLLATION_TAILORING_V1.iter())
672 .chain(Baked::DATA_COLLATION_REORDERING_V1.iter())
673 .chain(Baked::DATA_COLLATION_DIACRITICS_V1.iter())
674 .map(|d| {
675 (
676 d.locale.clone(),
677 tinystr::TinyAsciiStr::<8>::try_from_str(d.marker_attributes.as_str())
678 .expect("Marker attribute invariants upheld"),
679 )
680 })
681}