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;
30use crate::elements::CollationElement32;
31use crate::elements::Tag;
32use crate::elements::EMPTY_U16;
33use crate::elements::FFFD_CE;
34use crate::elements::FFFD_CE32;
35use crate::elements::FFFD_CE32_VALUE;
36use crate::elements::FFFD_CE_VALUE;
37use crate::elements::FFFD_PRIMARY;
38use crate::elements::NO_CE_PRIMARY;
39use crate::preferences::CollationCaseFirst;
40
41use crate::options::MaxVariable;
42
43#[cfg(feature = "compiled_data")]
44#[derive(Debug)]
45pub struct Baked;
53
54#[cfg(feature = "compiled_data")]
55#[allow(unused_imports)]
56const _: () = {
57 use icu_collator_data::*;
58 pub mod icu {
59 pub use crate as collator;
60 pub use icu_collections as collections;
61 pub use icu_locale as locale;
62 }
63 make_provider!(Baked);
64 impl_collation_root_v1!(Baked);
65 impl_collation_tailoring_v1!(Baked);
66 impl_collation_diacritics_v1!(Baked);
67 impl_collation_jamo_v1!(Baked);
68 impl_collation_metadata_v1!(Baked);
69 impl_collation_special_primaries_v1!(Baked);
70 impl_collation_reordering_v1!(Baked);
71};
72
73const SCRIPT_FALLBACK: icu_provider::fallback::LocaleFallbackConfig = {
74 let mut c = icu_provider::fallback::LocaleFallbackConfig::default();
75 c.priority = icu_provider::fallback::LocaleFallbackPriority::Script;
76 c
77};
78
79icu_provider::data_marker!(
80 CollationRootV1,
82 "collation/root/v1",
83 CollationData<'static>,
84 is_singleton = true,
85);
86icu_provider::data_marker!(
87 CollationTailoringV1,
89 "collation/tailoring/v1",
90 CollationData<'static>,
91 fallback_config = SCRIPT_FALLBACK,
92 #[cfg(feature = "datagen")]
93 attributes_domain = "collator",
94);
95icu_provider::data_marker!(
96 CollationDiacriticsV1,
98 "collation/diacritics/v1",
99 CollationDiacritics<'static>,
100 fallback_config = SCRIPT_FALLBACK,
101 #[cfg(feature = "datagen")]
102 attributes_domain = "collator",
103);
104icu_provider::data_marker!(
105 CollationJamoV1,
107 "collation/jamo/v1",
108 CollationJamo<'static>,
109 is_singleton = true,
110);
111icu_provider::data_marker!(
112 CollationReorderingV1,
114 "collation/reordering/v1",
115 CollationReordering<'static>,
116 fallback_config = SCRIPT_FALLBACK,
117 #[cfg(feature = "datagen")]
118 attributes_domain = "collator",
119);
120icu_provider::data_marker!(
121 CollationMetadataV1,
123 "collation/metadata/v1",
124 CollationMetadata,
125 fallback_config = SCRIPT_FALLBACK,
126 #[cfg(feature = "datagen")]
127 attributes_domain = "collator",
128);
129icu_provider::data_marker!(
130 CollationSpecialPrimariesV1,
132 "collation/special/primaries/v1",
133 CollationSpecialPrimaries<'static>,
134 is_singleton = true,
135);
136
137#[cfg(feature = "datagen")]
138pub const MARKERS: &[DataMarkerInfo] = &[
140 CollationRootV1::INFO,
141 CollationTailoringV1::INFO,
142 CollationDiacriticsV1::INFO,
143 CollationJamoV1::INFO,
144 CollationMetadataV1::INFO,
145 CollationReorderingV1::INFO,
146 CollationSpecialPrimariesV1::INFO,
147];
148
149const SINGLE_U32: &ZeroSlice<u32> =
150 zeroslice!(u32; <u32 as AsULE>::ULE::from_unsigned; [FFFD_CE32_VALUE]);
151const SINGLE_U64: &ZeroSlice<u64> =
152 zeroslice!(u64; <u64 as AsULE>::ULE::from_unsigned; [FFFD_CE_VALUE]);
153
154fn data_ce_to_primary(data_ce: u64, c: char) -> u32 {
155 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;
160 offset += (((p >> 8) & 0xFF) as i32) - 2;
162 let mut primary = (((offset % 254) + 2) as u32) << 8;
163 offset /= 254;
164 if is_compressible {
167 offset += (((p >> 16) & 0xFF) as i32) - 4;
168 primary |= (((offset % 251) + 4) as u32) << 16;
169 offset /= 251;
170 } else {
171 offset += (((p >> 16) & 0xFF) as i32) - 2;
172 primary |= (((offset % 254) + 2) as u32) << 16;
173 offset /= 254;
174 }
175 primary | ((p & 0xFF000000) + ((offset as u32) << 24))
176}
177
178#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
186#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
187#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
188#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
189pub struct CollationData<'data> {
190 #[cfg_attr(feature = "serde", serde(borrow))]
193 pub trie: SmallCodePointTrie<'data, u32>,
194 #[cfg_attr(feature = "serde", serde(borrow))]
197 pub ces: ZeroVec<'data, u64>,
198 #[cfg_attr(feature = "serde", serde(borrow))]
201 pub ce32s: ZeroVec<'data, u32>,
202 #[cfg_attr(feature = "serde", serde(borrow))]
204 pub contexts: ZeroVec<'data, u16>,
205}
206
207icu_provider::data_struct!(
208 CollationData<'_>,
209 #[cfg(feature = "datagen")]
210);
211
212impl<'data> CollationData<'data> {
213 pub(crate) fn ce32_for_char(&self, c: char) -> CollationElement32 {
214 CollationElement32::new(self.trie.get(c))
215 }
216 pub(crate) fn get_ce32(&'data self, index: usize) -> CollationElement32 {
217 CollationElement32::new(if let Some(u) = self.ce32s.get(index) {
218 u
219 } else {
220 debug_assert!(false);
222 FFFD_CE32_VALUE
223 })
224 }
225 pub(crate) fn get_ce32s(&'data self, index: usize, len: usize) -> &'data ZeroSlice<u32> {
226 if len > 0 {
227 if let Some(slice) = self.ce32s.get_subslice(index..index + len) {
228 return slice;
229 }
230 }
231 debug_assert!(false);
233 SINGLE_U32
234 }
235 pub(crate) fn get_ces(&'data self, index: usize, len: usize) -> &'data ZeroSlice<u64> {
236 if len > 0 {
237 if let Some(slice) = self.ces.get_subslice(index..index + len) {
238 return slice;
239 }
240 }
241 debug_assert!(false);
243 SINGLE_U64
244 }
245 pub(crate) fn get_primary_from_ces(&'data self, index: usize) -> u32 {
246 if let Some(ce) = self.ces.get(index) {
247 (ce >> 32) as u32
248 } else {
249 debug_assert!(false);
251 FFFD_PRIMARY
252 }
253 }
254 fn get_default_and_trie_impl(
255 &'data self,
256 index: usize,
257 ) -> (CollationElement32, &'data ZeroSlice<u16>) {
258 if let Some(slice) = self.contexts.get_subslice(index..self.contexts.len()) {
259 #[expect(clippy::unwrap_used)]
260 if slice.len() >= 2 {
261 let first = slice.get(0).unwrap();
263 let second = slice.get(1).unwrap();
264 let trie = slice.get_subslice(2..slice.len()).unwrap();
265 return (
266 CollationElement32::new((u32::from(first) << 16) | u32::from(second)),
267 trie,
268 );
269 }
270 }
271 debug_assert!(false);
273 (FFFD_CE32, EMPTY_U16)
274 }
275 pub(crate) fn get_default_and_trie(
276 &'data self,
277 index: usize,
278 ) -> (CollationElement32, Char16TrieIterator<'data>) {
279 let (ce32, trie) = self.get_default_and_trie_impl(index);
280 (ce32, Char16TrieIterator::new(trie))
281 }
282 pub(crate) fn get_default(&'data self, index: usize) -> CollationElement32 {
283 let (ce32, _) = self.get_default_and_trie_impl(index);
284 ce32
285 }
286 pub(crate) fn ce_from_offset_ce32(
287 &self,
288 c: char,
289 ce32: CollationElement32,
290 ) -> CollationElement {
291 debug_assert!(ce32.tag() == Tag::Offset);
292 if let Some(data_ce) = self.ces.get(ce32.index()) {
293 CollationElement::new_from_primary(data_ce_to_primary(data_ce, c))
294 } else {
295 debug_assert!(false);
297 FFFD_CE
298 }
299 }
300}
301
302#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
310#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
311#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
312#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
313pub struct CollationDiacritics<'data> {
314 #[cfg_attr(feature = "serde", serde(borrow))]
319 pub secondaries: ZeroVec<'data, u16>,
320}
321
322icu_provider::data_struct!(
323 CollationDiacritics<'_>,
324 #[cfg(feature = "datagen")]
325);
326
327#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
335#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
336#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
337#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
338pub struct CollationJamo<'data> {
339 #[cfg_attr(feature = "serde", serde(borrow))]
342 pub ce32s: ZeroVec<'data, u32>,
343}
344
345icu_provider::data_struct!(
346 CollationJamo<'_>,
347 #[cfg(feature = "datagen")]
348);
349
350#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
358#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
359#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
360#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
361pub struct CollationReordering<'data> {
362 pub min_high_no_reorder: u32,
366 #[cfg_attr(feature = "serde", serde(borrow))]
374 pub reorder_table: ZeroVec<'data, u8>, #[cfg_attr(feature = "serde", serde(borrow))]
397 pub reorder_ranges: ZeroVec<'data, u32>,
398}
399
400icu_provider::data_struct!(
401 CollationReordering<'_>,
402 #[cfg(feature = "datagen")]
403);
404
405impl CollationReordering<'_> {
406 pub(crate) fn reorder(&self, primary: u32) -> u32 {
407 if let Some(b) = self.reorder_table.get((primary >> 24) as usize) {
408 if b != 0 || primary <= NO_CE_PRIMARY {
409 (u32::from(b) << 24) | (primary & 0x00FFFFFF)
410 } else {
411 self.reorder_ex(primary)
412 }
413 } else {
414 debug_assert!(false);
416 primary
417 }
418 }
419
420 fn reorder_ex(&self, primary: u32) -> u32 {
421 if primary >= self.min_high_no_reorder {
422 return primary;
423 }
424 let q = primary | 0xFFFF;
425 for &range in self.reorder_ranges.as_ule_slice().iter() {
426 let r = u32::from_unaligned(range);
427 if q < r {
428 return primary.wrapping_add(r << 24);
429 }
430 }
431 debug_assert!(false);
433 primary
434 }
435}
436
437#[derive(Debug, PartialEq, Clone, Copy, yoke::Yokeable, zerofrom::ZeroFrom)]
447#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
448#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
449#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
450pub struct CollationMetadata {
451 pub bits: u32,
461}
462
463icu_provider::data_struct!(
464 CollationMetadata,
465 #[cfg(feature = "datagen")]
466);
467
468impl CollationMetadata {
469 const MAX_VARIABLE_MASK: u32 = 0b11;
470 const TAILORED_MASK: u32 = 1 << 3;
471 const TAILORED_DIACRITICS_MASK: u32 = 1 << 4;
472 const REORDERING_MASK: u32 = 1 << 5;
473 const LITHUANIAN_DOT_ABOVE_MASK: u32 = 1 << 6;
474 const BACWARD_SECOND_LEVEL_MASK: u32 = 1 << 7;
475 const ALTERNATE_SHIFTED_MASK: u32 = 1 << 8;
476 const CASE_FIRST_MASK: u32 = 1 << 9;
477 const UPPER_FIRST_MASK: u32 = 1 << 10;
478
479 #[inline(always)]
480 pub(crate) fn max_variable(self) -> MaxVariable {
481 unsafe { core::mem::transmute((self.bits & CollationMetadata::MAX_VARIABLE_MASK) as u8) }
485 }
486
487 #[inline(always)]
488 pub(crate) fn tailored(self) -> bool {
489 self.bits & CollationMetadata::TAILORED_MASK != 0
490 }
491
492 #[inline(always)]
494 pub(crate) fn tailored_diacritics(self) -> bool {
495 self.bits & CollationMetadata::TAILORED_DIACRITICS_MASK != 0
496 }
497
498 #[inline(always)]
500 pub(crate) fn lithuanian_dot_above(self) -> bool {
501 self.bits & CollationMetadata::LITHUANIAN_DOT_ABOVE_MASK != 0
502 }
503
504 #[inline(always)]
506 pub(crate) fn backward_second_level(self) -> bool {
507 self.bits & CollationMetadata::BACWARD_SECOND_LEVEL_MASK != 0
508 }
509
510 #[inline(always)]
511 pub(crate) fn reordering(self) -> bool {
512 self.bits & CollationMetadata::REORDERING_MASK != 0
513 }
514
515 #[inline(always)]
517 pub(crate) fn alternate_shifted(self) -> bool {
518 self.bits & CollationMetadata::ALTERNATE_SHIFTED_MASK != 0
519 }
520
521 #[inline(always)]
522 pub(crate) fn case_first(self) -> CollationCaseFirst {
523 if self.bits & CollationMetadata::CASE_FIRST_MASK != 0 {
524 if self.bits & CollationMetadata::UPPER_FIRST_MASK != 0 {
525 CollationCaseFirst::Upper
526 } else {
527 CollationCaseFirst::Lower
528 }
529 } else {
530 CollationCaseFirst::False
531 }
532 }
533}
534
535#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
562#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
563#[cfg_attr(feature = "datagen", databake(path = icu_collator::provider))]
564#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
565pub struct CollationSpecialPrimaries<'data> {
566 #[cfg_attr(feature = "serde", serde(borrow))]
575 pub last_primaries: ZeroVec<'data, u16>,
576 pub numeric_primary: u8,
578}
579
580#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
581pub(crate) struct CollationSpecialPrimariesValidated<'data> {
582 pub last_primaries: ZeroVec<'data, u16>,
587 pub numeric_primary: u8,
589 pub compressible_bytes: &'data [<u16 as AsULE>::ULE; 16],
592}
593
594impl CollationSpecialPrimariesValidated<'static> {
595 pub(crate) const HARDCODED_COMPRESSIBLE_BYTES_FALLBACK: &'static [<u16 as AsULE>::ULE; 16] = &[
596 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
597 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
598 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
599 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
600 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
601 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
602 <u16 as AsULE>::ULE::from_unsigned(0b1111_1111_1111_1110),
603 <u16 as AsULE>::ULE::from_unsigned(0b1111_1111_1111_1111),
604 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0001),
605 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
606 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
607 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
608 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
609 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
610 <u16 as AsULE>::ULE::from_unsigned(0b0000_0000_0000_0000),
611 <u16 as AsULE>::ULE::from_unsigned(0b0100_0000_0000_0000),
612 ];
613}
614
615icu_provider::data_struct!(
616 CollationSpecialPrimaries<'_>,
617 #[cfg(feature = "datagen")]
618);
619
620impl CollationSpecialPrimariesValidated<'_> {
621 #[expect(clippy::unwrap_used)]
622 pub(crate) fn last_primary_for_group(&self, max_variable: MaxVariable) -> u32 {
623 (u32::from(self.last_primaries.get(max_variable as usize).unwrap()) << 16) - 1
628 }
629
630 #[allow(dead_code)]
631 pub(crate) fn is_compressible(&self, b: u8) -> bool {
632 #[expect(clippy::indexing_slicing)]
636 let field = u16::from_unaligned(self.compressible_bytes[usize::from(b >> 4)]);
637 let mask = 1 << (b & 0b1111);
638 (field & mask) != 0
639 }
640}
641
642#[cfg(all(feature = "compiled_data", feature = "unstable"))]
650pub fn list_locales() -> impl Iterator<Item = (DataLocale, tinystr::TinyAsciiStr<8>)> {
651 use icu_provider::baked::DataStore;
652 Baked::DATA_COLLATION_METADATA_V1
653 .iter()
654 .chain(Baked::DATA_COLLATION_TAILORING_V1.iter())
655 .chain(Baked::DATA_COLLATION_REORDERING_V1.iter())
656 .chain(Baked::DATA_COLLATION_DIACRITICS_V1.iter())
657 .map(|d| {
658 (
659 d.locale.clone(),
660 tinystr::TinyAsciiStr::<8>::try_from_str(d.marker_attributes.as_str())
661 .expect("Marker attribute invariants upheld"),
662 )
663 })
664}