1use alloc::collections::VecDeque;
13use alloc::vec::Vec;
14
15use crate::elements::CharacterAndClassAndTrieValue;
16use crate::elements::CollationElement32;
17use crate::elements::Tag;
18use crate::elements::BACKWARD_COMBINING_MARKER;
19use crate::elements::CE_BUFFER_SIZE;
20use crate::elements::FALLBACK_CE32;
21use crate::elements::HANGUL_N_COUNT;
22use crate::elements::HANGUL_S_BASE;
23use crate::elements::HANGUL_S_COUNT;
24use crate::elements::HANGUL_T_COUNT;
25use crate::elements::IDENTICAL_PREFIX_HANGUL_MARKER_CE32;
26use crate::elements::NON_ROUND_TRIP_MARKER;
27use crate::elements::{
28 char_from_u32, CollationElement, CollationElements, NonPrimary, FFFD_CE32,
29 HANGUL_SYLLABLE_MARKER, HIGH_ZEROS_MASK, JAMO_COUNT, LOW_ZEROS_MASK, NO_CE, NO_CE_PRIMARY,
30 NO_CE_QUATERNARY, NO_CE_SECONDARY, NO_CE_TERTIARY, OPTIMIZED_DIACRITICS_MAX_COUNT,
31 QUATERNARY_MASK,
32};
33use crate::options::CollatorOptionsBitField;
34use crate::options::{
35 AlternateHandling, CollatorOptions, MaxVariable, ResolvedCollatorOptions, Strength,
36};
37use crate::preferences::{CollationCaseFirst, CollationNumericOrdering, CollationType};
38use crate::provider::CollationData;
39use crate::provider::CollationDiacritics;
40use crate::provider::CollationDiacriticsV1;
41use crate::provider::CollationJamo;
42use crate::provider::CollationJamoV1;
43use crate::provider::CollationMetadataV1;
44use crate::provider::CollationReordering;
45use crate::provider::CollationReorderingV1;
46use crate::provider::CollationRootV1;
47use crate::provider::CollationSpecialPrimariesV1;
48use crate::provider::CollationSpecialPrimariesValidated;
49use crate::provider::CollationTailoringV1;
50use core::cmp::Ordering;
51use core::convert::{Infallible, TryFrom};
52use icu_collections::codepointtrie::AbstractCodePointTrie;
53use icu_collections::codepointtrie::CharsWithTrieDefaultForAsciiEx;
54use icu_collections::codepointtrie::CharsWithTrieEx;
55#[cfg(feature = "serde")]
56use icu_collections::codepointtrie::CodePointTrie;
57#[cfg(not(feature = "serde"))]
58use icu_collections::codepointtrie::FastCodePointTrie;
59#[cfg(feature = "latin1")]
60use icu_collections::codepointtrie::Latin1CharsWithTrieEx;
61use icu_collections::codepointtrie::TypedCodePointTrie;
62use icu_collections::codepointtrie::WithTrie;
63use icu_normalizer::provider::DecompositionData;
64use icu_normalizer::provider::DecompositionTables;
65use icu_normalizer::provider::NormalizerNfdDataV1;
66use icu_normalizer::provider::NormalizerNfdTablesV1;
67use icu_provider::marker::ErasedMarker;
68use icu_provider::prelude::*;
69use smallvec::SmallVec;
70use utf16_iter::Utf16CharsWithTrieEx;
71use utf8_iter::Utf8CharsWithTrieDefaultForAsciiEx;
72use utf8_iter::Utf8CharsWithTrieEx;
73use zerovec::ule::AsULE;
74
75#[cfg(feature = "serde")]
76type NormTrie<'trie> = CodePointTrie<'trie, u32>;
77
78#[cfg(not(feature = "serde"))]
79type NormTrie<'trie> = FastCodePointTrie<'trie, u32>;
80
81const LEVEL_SEPARATOR_BYTE: u8 = 1;
83
84const MERGE_SEPARATOR: char = '\u{fffe}';
89const MERGE_SEPARATOR_BYTE: u8 = 2;
90const MERGE_SEPARATOR_PRIMARY: u32 = 0x02000000;
91
92const PRIMARY_COMPRESSION_LOW_BYTE: u8 = 3;
97
98const PRIMARY_COMPRESSION_HIGH_BYTE: u8 = 0xff;
103
104const COMMON_BYTE: u8 = 5;
106const COMMON_WEIGHT16: u16 = 0x0500;
107
108const PRIMARY_LEVEL_FLAG: u8 = 0x01;
110const SECONDARY_LEVEL_FLAG: u8 = 0x02;
111const CASE_LEVEL_FLAG: u8 = 0x04;
112const TERTIARY_LEVEL_FLAG: u8 = 0x08;
113const QUATERNARY_LEVEL_FLAG: u8 = 0x10;
114
115const LEVEL_MASKS: [u8; Strength::Identical as usize + 1] = [
116 PRIMARY_LEVEL_FLAG,
117 PRIMARY_LEVEL_FLAG | SECONDARY_LEVEL_FLAG,
118 PRIMARY_LEVEL_FLAG | SECONDARY_LEVEL_FLAG | TERTIARY_LEVEL_FLAG,
119 PRIMARY_LEVEL_FLAG | SECONDARY_LEVEL_FLAG | TERTIARY_LEVEL_FLAG | QUATERNARY_LEVEL_FLAG,
120 0,
121 0,
122 0,
123 PRIMARY_LEVEL_FLAG | SECONDARY_LEVEL_FLAG | TERTIARY_LEVEL_FLAG | QUATERNARY_LEVEL_FLAG,
124];
125
126const WEIGHT_LOW: usize = 0;
128const WEIGHT_MIDDLE: usize = 1;
129const WEIGHT_HIGH: usize = 2;
130const WEIGHT_MAX_COUNT: usize = 3;
131
132const SEC_COMMON: [u8; 4] = [COMMON_BYTE, COMMON_BYTE + 0x20, COMMON_BYTE + 0x40, 0x21];
134
135const CASE_LOWER_FIRST_COMMON: [u8; 4] = [1, 7, 13, 7];
137
138const CASE_UPPER_FIRST_COMMON: [u8; 4] = [3, 0 , 15, 13];
140
141const TER_ONLY_COMMON: [u8; 4] = [COMMON_BYTE, COMMON_BYTE + 0x60, COMMON_BYTE + 0xc0, 0x61];
143
144const TER_LOWER_FIRST_COMMON: [u8; 4] = [COMMON_BYTE, COMMON_BYTE + 0x20, COMMON_BYTE + 0x40, 0x21];
146
147const TER_UPPER_FIRST_COMMON: [u8; 4] = [
149 COMMON_BYTE + 0x80,
150 COMMON_BYTE + 0x80 + 0x20,
151 COMMON_BYTE + 0x80 + 0x40,
152 0x21,
153];
154
155const QUAT_COMMON: [u8; 4] = [0x1c, 0x1c + 0x70, 0x1c + 0xe0, 0x71];
156const QUAT_SHIFTED_LIMIT_BYTE: u8 = QUAT_COMMON[WEIGHT_LOW] - 1; const SLOPE_MIN: i32 = 3;
160const SLOPE_MAX: i32 = 0xff;
161const SLOPE_MIDDLE: i32 = 0x81;
162const SLOPE_TAIL_COUNT: i32 = SLOPE_MAX - SLOPE_MIN + 1;
163const SLOPE_SINGLE: i32 = 80;
164const SLOPE_LEAD_2: i32 = 42;
165const SLOPE_LEAD_3: i32 = 3;
166
167const SLOPE_REACH_POS_1: i32 = SLOPE_SINGLE;
169const SLOPE_REACH_NEG_1: i32 = -SLOPE_SINGLE;
170
171const SLOPE_REACH_POS_2: i32 = SLOPE_LEAD_2 * SLOPE_TAIL_COUNT + (SLOPE_LEAD_2 - 1);
173const SLOPE_REACH_NEG_2: i32 = -SLOPE_REACH_POS_2 - 1;
174
175const SLOPE_REACH_POS_3: i32 = SLOPE_LEAD_3 * SLOPE_TAIL_COUNT * SLOPE_TAIL_COUNT
177 + (SLOPE_LEAD_3 - 1) * SLOPE_TAIL_COUNT
178 + (SLOPE_TAIL_COUNT - 1);
179const SLOPE_REACH_NEG_3: i32 = -SLOPE_REACH_POS_3 - 1;
180
181const SLOPE_START_POS_2: i32 = SLOPE_MIDDLE + SLOPE_SINGLE + 1;
183const SLOPE_START_POS_3: i32 = SLOPE_START_POS_2 + SLOPE_LEAD_2;
184const SLOPE_START_NEG_2: i32 = SLOPE_MIDDLE + SLOPE_REACH_NEG_1;
185const SLOPE_START_NEG_3: i32 = SLOPE_START_NEG_2 - SLOPE_LEAD_2;
186
187struct AnyQuaternaryAccumulator(u32);
188
189impl AnyQuaternaryAccumulator {
190 #[inline(always)]
191 pub fn new() -> Self {
192 AnyQuaternaryAccumulator(0)
193 }
194 #[inline(always)]
195 pub fn accumulate(&mut self, non_primary: NonPrimary) {
196 self.0 |= non_primary.bits()
197 }
198 #[inline(always)]
199 pub fn has_quaternary(&self) -> bool {
200 self.0 & u32::from(QUATERNARY_MASK) != 0
201 }
202}
203
204#[inline(always)]
207fn in_inclusive_range16(i: u16, start: u16, end: u16) -> bool {
208 i.wrapping_sub(start) <= (end - start)
209}
210
211#[cfg(feature = "latin1")]
219fn split_prefix_latin1<'a, 'b>(left: &'a [u8], right: &'b [u8]) -> (&'a [u8], &'a [u8], &'b [u8]) {
220 let i = left
221 .iter()
222 .zip(right.iter())
223 .take_while(|(l, r)| l == r)
224 .count();
225 if let Some((head, left_tail)) = left.split_at_checked(i) {
226 if let Some(right_tail) = right.get(i..) {
227 return (head, left_tail, right_tail);
228 }
229 }
230 (&[], left, right)
231}
232
233#[cfg(feature = "latin1")]
241fn split_prefix_latin1_utf16<'a, 'b>(
242 left: &'a [u8],
243 right: &'b [u16],
244) -> (&'a [u8], &'a [u8], &'b [u16]) {
245 let i = left
246 .iter()
247 .zip(right.iter())
248 .take_while(|(l, r)| u16::from(**l) == **r)
249 .count();
250 if let Some((head, left_tail)) = left.split_at_checked(i) {
251 if let Some(right_tail) = right.get(i..) {
252 return (head, left_tail, right_tail);
253 }
254 }
255 (&[], left, right)
256}
257
258fn split_prefix_u16<'a, 'b>(
266 left: &'a [u16],
267 right: &'b [u16],
268) -> (&'a [u16], &'a [u16], &'b [u16]) {
269 let mut i = left
270 .iter()
271 .zip(right.iter())
272 .take_while(|(l, r)| l == r)
273 .count();
274 if i != 0 {
275 if let Some(&last) = left.get(i.wrapping_sub(1)) {
276 if in_inclusive_range16(last, 0xD800, 0xDBFF) {
277 i -= 1;
278 }
279 if let Some((head, left_tail)) = left.split_at_checked(i) {
280 if let Some(right_tail) = right.get(i..) {
281 return (head, left_tail, right_tail);
282 }
283 }
284 }
285 }
286 (&[], left, right)
287}
288
289fn split_prefix_u8<'a, 'b>(left: &'a [u8], right: &'b [u8]) -> (&'a [u8], &'a [u8], &'b [u8]) {
297 let mut i = left
298 .iter()
299 .zip(right.iter())
300 .take_while(|(l, r)| l == r)
301 .count();
302 if i != 0 {
303 if let Some(right_first) = right.get(i) {
311 if (right_first & 0b1100_0000) == 0b1000_0000 {
312 i -= 1;
313 }
314 }
315 while i != 0 {
316 if let Some(left_first) = left.get(i) {
317 if (left_first & 0b1100_0000) == 0b1000_0000 {
318 i -= 1;
319 continue;
320 }
321 }
322 break;
323 }
324 if let Some((head, left_tail)) = left.split_at_checked(i) {
325 if let Some(right_tail) = right.get(i..) {
326 return (head, left_tail, right_tail);
327 }
328 }
329 }
330 (&[], left, right)
331}
332
333fn split_prefix<'a, 'b>(left: &'a str, right: &'b str) -> (&'a str, &'a str, &'b str) {
339 let left_bytes = left.as_bytes();
340 let right_bytes = right.as_bytes();
341 let mut i = left_bytes
342 .iter()
343 .zip(right_bytes.iter())
344 .take_while(|(l, r)| l == r)
345 .count();
346 if i != 0 {
347 loop {
364 if let Some(left_first) = left_bytes.get(i) {
365 if (left_first & 0b1100_0000) == 0b1000_0000 {
366 i -= 1;
367 continue;
368 }
369 }
370 break;
371 }
372 if let Some((head, left_tail)) = left.split_at_checked(i) {
376 if let Some(right_tail) = right.get(i..) {
377 return (head, left_tail, right_tail);
378 }
379 }
380 }
381 ("", left, right)
382}
383
384#[derive(Debug)]
387struct LocaleSpecificDataHolder {
388 tailoring: Option<DataPayload<CollationTailoringV1>>,
389 diacritics: DataPayload<CollationDiacriticsV1>,
390 reordering: Option<DataPayload<CollationReorderingV1>>,
391 merged_options: CollatorOptionsBitField,
392 lithuanian_dot_above: bool,
393}
394
395icu_locale_core::preferences::define_preferences!(
396 [Copy]
415 CollatorPreferences,
416 {
417 collation_type: CollationType,
419 case_first: CollationCaseFirst,
422 numeric_ordering: CollationNumericOrdering
426 }
427);
428
429impl LocaleSpecificDataHolder {
430 fn try_new_unstable_internal<D>(
432 provider: &D,
433 prefs: CollatorPreferences,
434 options: CollatorOptions,
435 ) -> Result<Self, DataError>
436 where
437 D: DataProvider<CollationTailoringV1>
438 + DataProvider<CollationDiacriticsV1>
439 + DataProvider<CollationMetadataV1>
440 + DataProvider<CollationReorderingV1>
441 + ?Sized,
442 {
443 let marker_attributes = prefs
444 .collation_type
445 .as_ref()
446 .map(|c| DataMarkerAttributes::from_str_or_panic(c.as_str()))
448 .unwrap_or_default();
449
450 let data_locale = CollationTailoringV1::make_locale(prefs.locale_preferences);
451 let req = DataRequest {
452 id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
453 marker_attributes,
454 &data_locale,
455 ),
456 metadata: {
457 let mut metadata = DataRequestMetadata::default();
458 metadata.silent = true;
459 metadata
460 },
461 };
462
463 let fallback_req = DataRequest {
464 id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
465 Default::default(),
466 &data_locale,
467 ),
468 ..Default::default()
469 };
470
471 let metadata_payload: DataPayload<CollationMetadataV1> = provider
472 .load(req)
473 .or_else(|_| provider.load(fallback_req))?
474 .payload;
475
476 let metadata = metadata_payload.get();
477
478 let tailoring: Option<DataPayload<CollationTailoringV1>> = if metadata.tailored() {
479 Some(
480 provider
481 .load(req)
482 .or_else(|_| provider.load(fallback_req))?
483 .payload,
484 )
485 } else {
486 None
487 };
488
489 let reordering: Option<DataPayload<CollationReorderingV1>> = if metadata.reordering() {
490 Some(
491 provider
492 .load(req)
493 .or_else(|_| provider.load(fallback_req))?
494 .payload,
495 )
496 } else {
497 None
498 };
499
500 if let Some(reordering) = &reordering {
501 if reordering.get().reorder_table.len() != 256 {
502 return Err(DataError::custom("invalid").with_marker(CollationReorderingV1::INFO));
503 }
504 }
505
506 let tailored_diacritics = metadata.tailored_diacritics();
507 let diacritics: DataPayload<CollationDiacriticsV1> = provider
508 .load(if tailored_diacritics {
509 req
510 } else {
511 Default::default()
512 })?
513 .payload;
514
515 if tailored_diacritics {
516 if diacritics.get().secondaries.len() > OPTIMIZED_DIACRITICS_MAX_COUNT {
522 return Err(DataError::custom("invalid").with_marker(CollationDiacriticsV1::INFO));
523 }
524 } else if diacritics.get().secondaries.len() != OPTIMIZED_DIACRITICS_MAX_COUNT {
525 return Err(DataError::custom("invalid").with_marker(CollationDiacriticsV1::INFO));
526 }
527
528 let mut altered_defaults = CollatorOptionsBitField::default();
529
530 if metadata.alternate_shifted() {
531 altered_defaults.set_alternate_handling(Some(AlternateHandling::Shifted));
532 }
533 if metadata.backward_second_level() {
534 altered_defaults.set_backward_second_level(Some(true));
535 }
536
537 altered_defaults.set_case_first(Some(metadata.case_first()));
538 altered_defaults.set_max_variable(Some(metadata.max_variable()));
539
540 let mut merged_options = CollatorOptionsBitField::from(options);
541 merged_options.set_case_first(prefs.case_first);
542 merged_options.set_numeric_from_enum(prefs.numeric_ordering);
543 merged_options.set_defaults(altered_defaults);
544
545 Ok(LocaleSpecificDataHolder {
546 tailoring,
547 diacritics,
548 merged_options,
549 reordering,
550 lithuanian_dot_above: metadata.lithuanian_dot_above(),
551 })
552 }
553}
554
555#[derive(Debug)]
557pub struct Collator {
558 special_primaries: DataPayload<ErasedMarker<CollationSpecialPrimariesValidated<'static>>>,
559 root: DataPayload<CollationRootV1>,
560 tailoring: Option<DataPayload<CollationTailoringV1>>,
561 jamo: DataPayload<CollationJamoV1>,
562 diacritics: DataPayload<CollationDiacriticsV1>,
563 options: CollatorOptionsBitField,
564 reordering: Option<DataPayload<CollationReorderingV1>>,
565 decompositions: DataPayload<NormalizerNfdDataV1>,
566 tables: DataPayload<NormalizerNfdTablesV1>,
567 lithuanian_dot_above: bool,
568}
569
570impl Collator {
571 pub fn as_borrowed(&self) -> CollatorBorrowed<'_> {
573 CollatorBorrowed {
574 special_primaries: self.special_primaries.get(),
575 root: self.root.get(),
576 tailoring: if let Some(t) = self.tailoring.as_ref() {
577 t.get()
578 } else {
579 self.root.get()
580 },
581 jamo: self.jamo.get(),
582 diacritics: self.diacritics.get(),
583 options: self.options,
584 reordering: self.reordering.as_ref().map(|s| s.get()),
585 decompositions: self.decompositions.get(),
586 tables: self.tables.get(),
587 lithuanian_dot_above: self.lithuanian_dot_above,
588 }
589 }
590
591 #[cfg(feature = "compiled_data")]
593 pub fn try_new(
594 prefs: CollatorPreferences,
595 options: CollatorOptions,
596 ) -> Result<CollatorBorrowed<'static>, DataError> {
597 CollatorBorrowed::try_new(prefs, options)
598 }
599
600 icu_provider::gen_buffer_data_constructors!(
601 (prefs: CollatorPreferences, options: CollatorOptions) -> error: DataError,
602 functions: [
603 try_new: skip,
604 try_new_with_buffer_provider,
605 try_new_unstable,
606 Self
607 ]
608 );
609
610 #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
611 pub fn try_new_unstable<D>(
612 provider: &D,
613 prefs: CollatorPreferences,
614 options: CollatorOptions,
615 ) -> Result<Self, DataError>
616 where
617 D: DataProvider<CollationSpecialPrimariesV1>
618 + DataProvider<CollationRootV1>
619 + DataProvider<CollationTailoringV1>
620 + DataProvider<CollationDiacriticsV1>
621 + DataProvider<CollationJamoV1>
622 + DataProvider<CollationMetadataV1>
623 + DataProvider<CollationReorderingV1>
624 + DataProvider<NormalizerNfdDataV1>
625 + DataProvider<NormalizerNfdTablesV1>
626 + ?Sized,
627 {
628 Self::try_new_unstable_internal(
629 provider,
630 provider.load(Default::default())?.payload,
631 provider.load(Default::default())?.payload,
632 provider.load(Default::default())?.payload,
633 provider.load(Default::default())?.payload,
634 provider.load(Default::default())?.payload,
635 prefs,
636 options,
637 )
638 }
639
640 #[expect(clippy::too_many_arguments)]
641 fn try_new_unstable_internal<D>(
642 provider: &D,
643 root: DataPayload<CollationRootV1>,
644 decompositions: DataPayload<NormalizerNfdDataV1>,
645 tables: DataPayload<NormalizerNfdTablesV1>,
646 jamo: DataPayload<CollationJamoV1>,
647 special_primaries: DataPayload<CollationSpecialPrimariesV1>,
648 prefs: CollatorPreferences,
649 options: CollatorOptions,
650 ) -> Result<Self, DataError>
651 where
652 D: DataProvider<CollationRootV1>
653 + DataProvider<CollationTailoringV1>
654 + DataProvider<CollationDiacriticsV1>
655 + DataProvider<CollationMetadataV1>
656 + DataProvider<CollationReorderingV1>
657 + ?Sized,
658 {
659 let locale_dependent =
660 LocaleSpecificDataHolder::try_new_unstable_internal(provider, prefs, options)?;
661
662 if jamo.get().ce32s.len() != JAMO_COUNT {
664 return Err(DataError::custom("invalid").with_marker(CollationJamoV1::INFO));
665 }
666
667 if special_primaries.get().last_primaries.len() <= (MaxVariable::Currency as usize) {
670 return Err(DataError::custom("invalid").with_marker(CollationSpecialPrimariesV1::INFO));
671 }
672 let special_primaries = special_primaries.map_project(|csp, _| {
673 let compressible_bytes = (csp.last_primaries.len()
674 == MaxVariable::Currency as usize + 16)
675 .then(|| {
676 csp.last_primaries
677 .as_maybe_borrowed()?
678 .as_ule_slice()
679 .get((MaxVariable::Currency as usize)..)?
680 .try_into()
681 .ok()
682 })
683 .flatten()
684 .unwrap_or(
685 CollationSpecialPrimariesValidated::HARDCODED_COMPRESSIBLE_BYTES_FALLBACK,
686 );
687
688 CollationSpecialPrimariesValidated {
689 last_primaries: csp.last_primaries.truncated(MaxVariable::Currency as usize),
690 numeric_primary: csp.numeric_primary,
691 compressible_bytes,
692 }
693 });
694
695 Ok(Collator {
696 special_primaries,
697 root,
698 tailoring: locale_dependent.tailoring,
699 jamo,
700 diacritics: locale_dependent.diacritics,
701 options: locale_dependent.merged_options,
702 reordering: locale_dependent.reordering,
703 decompositions,
704 tables,
705 lithuanian_dot_above: locale_dependent.lithuanian_dot_above,
706 })
707 }
708}
709
710macro_rules! quick_primary_compare {
711 ($left_primary:ident,
712 $right_primary:ident,
713 $variable_top:ident,
714 $self:ident,
715 ) => {
716 if ($left_primary != $right_primary)
717 && ($left_primary != 0)
718 && ($right_primary != 0)
719 && !($left_primary < $variable_top && $left_primary > MERGE_SEPARATOR_PRIMARY)
720 && !($right_primary < $variable_top && $right_primary > MERGE_SEPARATOR_PRIMARY)
721 {
722 if let Some(reordering) = &$self.reordering {
723 $left_primary = reordering.reorder($left_primary);
724 $right_primary = reordering.reorder($right_primary);
725 }
726 if $left_primary < $right_primary {
727 return Ordering::Less;
728 }
729 return Ordering::Greater;
730 }
731 };
732}
733
734macro_rules! hangul_syllable_compare {
735 ($left_hangul_offset:ident,
736 $right_hangul_offset:ident,
737 ) => {
738 let left_l = $left_hangul_offset / HANGUL_N_COUNT;
739 let right_l = $right_hangul_offset / HANGUL_N_COUNT;
740 if left_l != right_l {
741 if left_l < right_l {
744 return Ordering::Less;
745 }
746 return Ordering::Greater;
747 }
748 let left_v = ($left_hangul_offset % HANGUL_N_COUNT) / HANGUL_T_COUNT;
749 let right_v = ($right_hangul_offset % HANGUL_N_COUNT) / HANGUL_T_COUNT;
750 if left_v != right_v {
751 if left_v < right_v {
754 return Ordering::Less;
755 }
756 return Ordering::Greater;
757 }
758 let left_t = $left_hangul_offset % HANGUL_T_COUNT;
759 let right_t = $right_hangul_offset % HANGUL_T_COUNT;
760 if left_t != right_t && left_t != 0 && right_t != 0 {
762 if left_t < right_t {
765 return Ordering::Less;
766 }
767 return Ordering::Greater;
768 }
769 };
770}
771
772macro_rules! compare {
773 ($(#[$meta:meta])*,
774 $compare:ident,
775 $left_slice:ty,
776 $right_slice:ty,
777 $split_prefix:ident,
778 $left_to_iter:ident,
779 $right_to_iter:ident,
780 $self:ident,
781 $left_tail:ident,
782 $right_tail:ident,
783 $primary_check:block,
784 $variable_top:ident,
785 ) => {
786 $(#[$meta])*
787 pub fn $compare(&$self, left: &$left_slice, right: &$right_slice) -> Ordering {
788 let (head, $left_tail, $right_tail) = $split_prefix(left, right);
789 if $left_tail.is_empty() && $right_tail.is_empty() {
790 return Ordering::Equal;
791 }
792 let $variable_top = $self.variable_top();
793 if head.is_empty() {
794 $primary_check
795 }
796 let norm_trie = $self.norm_trie();
797 let ret = $self.compare_impl($left_tail.$left_to_iter(norm_trie), $right_tail.$right_to_iter(norm_trie), head.$left_to_iter(norm_trie), $variable_top);
798 if $self.options.strength() == Strength::Identical && ret == Ordering::Equal {
799 return icu_normalizer::new_decomposition($left_tail.$left_to_iter(norm_trie), $self.tables).map(|c| if c != MERGE_SEPARATOR { c as i32 } else { -1i32 }).cmp(
801 icu_normalizer::new_decomposition($right_tail.$right_to_iter(norm_trie), $self.tables).map(|c| if c != MERGE_SEPARATOR { c as i32 } else { -1i32 }),
802 );
803 }
804 ret
805 }
806 }
807}
808
809#[derive(Debug)]
812pub struct CollatorBorrowed<'a> {
813 special_primaries: &'a CollationSpecialPrimariesValidated<'a>,
814 root: &'a CollationData<'a>,
815 tailoring: &'a CollationData<'a>,
816 jamo: &'a CollationJamo<'a>,
817 diacritics: &'a CollationDiacritics<'a>,
818 options: CollatorOptionsBitField,
819 reordering: Option<&'a CollationReordering<'a>>,
820 decompositions: &'a DecompositionData<'a>,
821 tables: &'a DecompositionTables<'a>,
822 lithuanian_dot_above: bool,
823}
824
825impl CollatorBorrowed<'static> {
826 #[cfg(feature = "compiled_data")]
828 pub fn try_new(
829 prefs: CollatorPreferences,
830 options: CollatorOptions,
831 ) -> Result<Self, DataError> {
832 let provider = &crate::provider::Baked;
836 let decompositions = icu_normalizer::provider::Baked::SINGLETON_NORMALIZER_NFD_DATA_V1;
837 let tables = icu_normalizer::provider::Baked::SINGLETON_NORMALIZER_NFD_TABLES_V1;
838 let root = crate::provider::Baked::SINGLETON_COLLATION_ROOT_V1;
839 let jamo = crate::provider::Baked::SINGLETON_COLLATION_JAMO_V1;
840
841 let locale_dependent =
842 LocaleSpecificDataHolder::try_new_unstable_internal(provider, prefs, options)?;
843
844 const _: () = assert!(
846 crate::provider::Baked::SINGLETON_COLLATION_JAMO_V1
847 .ce32s
848 .as_slice()
849 .len()
850 == JAMO_COUNT
851 );
852
853 const _: () = assert!(
856 crate::provider::Baked::SINGLETON_COLLATION_SPECIAL_PRIMARIES_V1
857 .last_primaries
858 .as_slice()
859 .len()
860 > (MaxVariable::Currency as usize)
861 );
862
863 let special_primaries = const {
864 &CollationSpecialPrimariesValidated {
865 last_primaries: zerovec::ZeroSlice::from_ule_slice(
866 crate::provider::Baked::SINGLETON_COLLATION_SPECIAL_PRIMARIES_V1
867 .last_primaries
868 .as_slice()
869 .as_ule_slice()
870 .split_at(MaxVariable::Currency as usize)
871 .0,
872 )
873 .as_zerovec(),
874 numeric_primary: crate::provider::Baked::SINGLETON_COLLATION_SPECIAL_PRIMARIES_V1
875 .numeric_primary,
876 compressible_bytes: {
877 const C: &[<u16 as AsULE>::ULE] =
878 crate::provider::Baked::SINGLETON_COLLATION_SPECIAL_PRIMARIES_V1
879 .last_primaries
880 .as_slice()
881 .as_ule_slice();
882 if C.len() == MaxVariable::Currency as usize + 16 {
883 let i = MaxVariable::Currency as usize;
884 #[allow(clippy::indexing_slicing)] &[
886 C[i],
887 C[i + 1],
888 C[i + 2],
889 C[i + 3],
890 C[i + 4],
891 C[i + 5],
892 C[i + 6],
893 C[i + 7],
894 C[i + 8],
895 C[i + 9],
896 C[i + 10],
897 C[i + 11],
898 C[i + 12],
899 C[i + 13],
900 C[i + 14],
901 C[i + 15],
902 ]
903 } else {
904 CollationSpecialPrimariesValidated::HARDCODED_COMPRESSIBLE_BYTES_FALLBACK
905 }
906 },
907 }
908 };
909
910 #[expect(clippy::unwrap_used)]
913 Ok(CollatorBorrowed {
914 special_primaries,
915 root,
916 tailoring: if let Some(s) = locale_dependent.tailoring {
918 s.get_static().unwrap()
919 } else {
920 root
921 },
922 jamo,
923 diacritics: locale_dependent.diacritics.get_static().unwrap(),
925 options: locale_dependent.merged_options,
926 reordering: locale_dependent.reordering.map(|s| s.get_static().unwrap()),
928 decompositions,
929 tables,
930 lithuanian_dot_above: locale_dependent.lithuanian_dot_above,
931 })
932 }
933
934 pub const fn static_to_owned(self) -> Collator {
939 Collator {
940 special_primaries: DataPayload::from_static_ref(self.special_primaries),
941 root: DataPayload::from_static_ref(self.root),
942 tailoring: Some(DataPayload::from_static_ref(self.tailoring)),
943 jamo: DataPayload::from_static_ref(self.jamo),
944 diacritics: DataPayload::from_static_ref(self.diacritics),
945 options: self.options,
946 reordering: if let Some(s) = self.reordering {
947 Some(DataPayload::from_static_ref(s))
949 } else {
950 None
951 },
952 decompositions: DataPayload::from_static_ref(self.decompositions),
953 tables: DataPayload::from_static_ref(self.tables),
954 lithuanian_dot_above: self.lithuanian_dot_above,
955 }
956 }
957}
958
959macro_rules! collation_elements {
960 ($self:expr, $chars:expr, $numeric_primary:expr) => {{
961 let jamo = <&[<u32 as AsULE>::ULE; JAMO_COUNT]>::try_from($self.jamo.ce32s.as_ule_slice());
962
963 let jamo = jamo.unwrap();
964
965 CollationElements::new(
966 $chars,
967 $self.root,
968 $self.tailoring,
969 jamo,
970 &$self.diacritics.secondaries,
971 $self.tables,
972 $numeric_primary,
973 $self.lithuanian_dot_above,
974 )
975 }};
976}
977
978impl<'data> CollatorBorrowed<'data> {
979 pub fn resolved_options(&self) -> ResolvedCollatorOptions {
982 self.options.into()
983 }
984
985 fn norm_trie(&self) -> &'data NormTrie<'data> {
986 #[allow(clippy::useless_conversion)]
987 <&NormTrie<'data>>::try_from(&self.decompositions.trie)
988 .unwrap_or_else(|_| unreachable!("Incompatible data"))
989 }
990
991 compare!(
992 ,
994 compare,
995 str,
996 str,
997 split_prefix,
998 chars_with_trie_default_for_ascii,
999 chars_with_trie_default_for_ascii,
1000 self,
1001 left_tail,
1002 right_tail,
1003 {
1004 let tailoring_trie = &self.tailoring.trie;
1006 if let Some((left_c, left_u32)) = left_tail.chars_with_trie(tailoring_trie).next() {
1007 if let Some((right_c, right_u32)) = right_tail.chars_with_trie(tailoring_trie).next() {
1011 let left_ce32 = CollationElement32::new(left_u32);
1012 let right_ce32 = CollationElement32::new(right_u32);
1013 if let Some(mut left_primary) = left_ce32.to_primary_in_quick_check(self.tailoring) {
1014 if let Some(mut right_primary) = right_ce32.to_primary_in_quick_check(self.tailoring) {
1015 quick_primary_compare!(left_primary, right_primary, variable_top, self,);
1016 }
1017 }
1018 let right_hangul_offset = u32::from(right_c).wrapping_sub(HANGUL_S_BASE);
1021 if right_hangul_offset < HANGUL_S_COUNT {
1022 let left_hangul_offset = u32::from(left_c).wrapping_sub(HANGUL_S_BASE);
1023 if left_hangul_offset < HANGUL_S_COUNT {
1024 hangul_syllable_compare!(left_hangul_offset, right_hangul_offset,);
1025 }
1026 }
1027 }
1028 }
1029 },
1032 variable_top,
1033 );
1034
1035 compare!(
1036 ,
1040 compare_utf8,
1041 [u8],
1042 [u8],
1043 split_prefix_u8,
1044 chars_with_trie_default_for_ascii,
1045 chars_with_trie_default_for_ascii,
1046 self,
1047 left_tail,
1048 right_tail,
1049 {
1050 let tailoring_trie = &self.tailoring.trie;
1052 if let Some((left_c, left_u32)) = left_tail.chars_with_trie(tailoring_trie).next() {
1053 if let Some((right_c, right_u32)) = right_tail.chars_with_trie(tailoring_trie).next() {
1057 let left_ce32 = CollationElement32::new(left_u32);
1058 let right_ce32 = CollationElement32::new(right_u32);
1059 if let Some(mut left_primary) = left_ce32.to_primary_in_quick_check(self.tailoring) {
1060 if let Some(mut right_primary) = right_ce32.to_primary_in_quick_check(self.tailoring) {
1061 quick_primary_compare!(left_primary, right_primary, variable_top, self,);
1062 }
1063 }
1064 let right_hangul_offset = u32::from(right_c).wrapping_sub(HANGUL_S_BASE);
1067 if right_hangul_offset < HANGUL_S_COUNT {
1068 let left_hangul_offset = u32::from(left_c).wrapping_sub(HANGUL_S_BASE);
1069 if left_hangul_offset < HANGUL_S_COUNT {
1070 hangul_syllable_compare!(left_hangul_offset, right_hangul_offset,);
1071 }
1072 }
1073 }
1074 }
1075 },
1078 variable_top,
1079 );
1080
1081 compare!(
1082 ,
1085 compare_utf16,
1086 [u16],
1087 [u16],
1088 split_prefix_u16,
1089 chars_with_trie,
1090 chars_with_trie,
1091 self,
1092 left_tail,
1093 right_tail,
1094 {
1095 let tailoring_trie = &self.tailoring.trie;
1096 if let Some(left_u) = left_tail.first() {
1097 if let Some(right_u) = right_tail.first() {
1098 let left_u16 = *left_u;
1099 let right_u16 = *right_u;
1100 let left_u32 = tailoring_trie.get16(left_u16);
1101 let right_u32 = tailoring_trie.get16(right_u16);
1102 let left_ce32 = CollationElement32::new(left_u32);
1103 let right_ce32 = CollationElement32::new(right_u32);
1104 if let Some(mut left_primary) = left_ce32.to_primary_in_quick_check(self.tailoring) {
1105 if let Some(mut right_primary) = right_ce32.to_primary_in_quick_check(self.tailoring) {
1106 quick_primary_compare!(left_primary, right_primary, variable_top, self,);
1107 }
1108 }
1109 let left_hangul_offset = u32::from(left_u16).wrapping_sub(HANGUL_S_BASE);
1111 if left_hangul_offset < HANGUL_S_COUNT {
1112 if let Some(right_u) = right_tail.first() {
1113 let right_u16 = *right_u;
1114 let right_hangul_offset = u32::from(right_u16).wrapping_sub(HANGUL_S_BASE);
1115 if right_hangul_offset < HANGUL_S_COUNT {
1116 hangul_syllable_compare!(left_hangul_offset, right_hangul_offset,);
1117 }
1118 }
1119 }
1120 }
1121 }
1122 },
1125 variable_top,
1126 );
1127
1128 compare!(
1129 #[cfg(feature = "latin1")]
1133 ,
1134 compare_latin1,
1135 [u8],
1136 [u8],
1137 split_prefix_latin1,
1138 latin1_chars_with_trie,
1139 latin1_chars_with_trie,
1140 self,
1141 left_tail,
1142 right_tail,
1143 {
1144 let tailoring_trie = &self.tailoring.trie;
1145 if let Some(left_u) = left_tail.first() {
1146 if let Some(right_u) = right_tail.first() {
1147 let left_u8 = *left_u;
1148 let right_u8 = *right_u;
1149 if left_u8 < 0x80 {
1155 let left_u32 = unsafe { tailoring_trie.get7(left_u8) };
1157 let left_ce32 = CollationElement32::new(left_u32);
1158 if right_u8 < 0x80 {
1160 let right_u32 = unsafe { tailoring_trie.get7(right_u8) };
1162 let right_ce32 = CollationElement32::new(right_u32);
1163 if let Some(left_primary) = left_ce32.to_primary_simple() {
1166 if let Some(right_primary) = right_ce32.to_primary_simple() {
1167 if (left_primary != right_primary)
1168 && (left_primary != 0)
1169 && (right_primary != 0)
1170 && !(left_primary < variable_top && left_primary > MERGE_SEPARATOR_PRIMARY)
1171 && !(right_primary < variable_top && right_primary > MERGE_SEPARATOR_PRIMARY)
1172 {
1173 if left_primary < right_primary {
1174 return Ordering::Less;
1175 }
1176 return Ordering::Greater;
1177 }
1178 }
1179 }
1180 }
1181 }
1182 }
1183 }
1184 },
1187 variable_top,
1188 );
1189
1190 compare!(
1191 #[cfg(feature = "latin1")]
1200 ,
1201 compare_latin1_utf16,
1202 [u8],
1203 [u16],
1204 split_prefix_latin1_utf16,
1205 latin1_chars_with_trie,
1206 chars_with_trie,
1207 self,
1208 left_tail,
1209 right_tail,
1210 {
1211 let tailoring_trie = &self.tailoring.trie;
1212 if let Some(left_u) = left_tail.first() {
1213 if let Some(right_u) = right_tail.first() {
1214 let left_u8 = *left_u;
1215 if left_u8 < 0x80 {
1221 let left_u32 = unsafe { tailoring_trie.get7(left_u8) };
1223 let left_ce32 = CollationElement32::new(left_u32);
1224 let right_u16 = *right_u;
1225 let right_u32 = tailoring_trie.get16(right_u16);
1226 let right_ce32 = CollationElement32::new(right_u32);
1227 if let Some(mut left_primary) = left_ce32.to_primary_simple() {
1228 if let Some(mut right_primary) = right_ce32.to_primary_in_quick_check(self.tailoring) {
1231 if (left_primary != right_primary)
1232 && (left_primary != 0)
1233 && (right_primary != 0)
1234 && !(left_primary < variable_top && left_primary > MERGE_SEPARATOR_PRIMARY)
1235 && !(right_primary < variable_top && right_primary > MERGE_SEPARATOR_PRIMARY)
1236 {
1237 if let Some(reordering) = &self.reordering {
1238 left_primary = reordering.reorder(left_primary);
1239 right_primary = reordering.reorder(right_primary);
1240 }
1241 if left_primary < right_primary {
1242 return Ordering::Less;
1243 }
1244 return Ordering::Greater;
1245 }
1246 }
1247 }
1248 }
1249 }
1250 }
1251 },
1254 variable_top,
1255 );
1256
1257 #[inline(always)]
1258 fn numeric_primary(&self) -> Option<u8> {
1259 if self.options.numeric() {
1260 Some(self.special_primaries.numeric_primary)
1261 } else {
1262 None
1263 }
1264 }
1265
1266 #[inline(always)]
1267 fn variable_top(&self) -> u32 {
1268 if self.options.alternate_handling() == AlternateHandling::NonIgnorable {
1269 0
1270 } else {
1271 self.special_primaries
1273 .last_primary_for_group(self.options.max_variable())
1274 + 1
1275 }
1276 }
1277
1278 fn compare_impl<L, R, H, T>(
1284 &'data self,
1285 left_chars: L,
1286 right_chars: R,
1287 mut head_chars: H,
1288 variable_top: u32,
1289 ) -> Ordering
1290 where
1291 L: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + 'data,
1292 R: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + 'data,
1293 H: DoubleEndedIterator<Item = (char, u32)> + 'data,
1294 T: AbstractCodePointTrie<'data, u32> + 'data,
1295 {
1296 let mut left_ces: SmallVec<[CollationElement; CE_BUFFER_SIZE]> = SmallVec::new();
1311 let mut right_ces: SmallVec<[CollationElement; CE_BUFFER_SIZE]> = SmallVec::new();
1312
1313 let mut any_variable = false;
1316
1317 let numeric_primary = self.numeric_primary();
1318
1319 let mut left = collation_elements!(self, left_chars, numeric_primary);
1320 let mut right = collation_elements!(self, right_chars, numeric_primary);
1321
1322 #[expect(clippy::never_loop)]
1343 'prefix: loop {
1344 if let Some((mut head_last_c, head_last_trie_val)) = head_chars.next_back() {
1345 let mut head_last = CharacterAndClassAndTrieValue::new_with_trie_val(
1346 head_last_c,
1347 head_last_trie_val,
1348 );
1349 let mut head_last_ce32 = CollationElement32::default();
1350 let mut head_last_ok = false;
1351 if let Some(left_different) = left.iter_next_before_init() {
1352 left.prepend_upcoming_before_init(left_different.clone());
1353 if let Some(right_different) = right.iter_next_before_init() {
1354 right.prepend_upcoming_before_init(right_different.clone());
1356
1357 #[expect(clippy::never_loop)]
1415 loop {
1416 let decomposition = head_last.trie_val;
1419 if (decomposition
1420 & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER))
1421 == 0
1422 {
1423 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1427 && ((decomposition & LOW_ZEROS_MASK) != 0)
1428 {
1429 head_last_c = char_from_u32(decomposition & 0x7FFF);
1432 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1433 head_last_ce32 = IDENTICAL_PREFIX_HANGUL_MARKER_CE32;
1434 } else {
1435 break;
1436 }
1437 head_last_ok = true;
1438
1439 let left_c;
1440 let right_c;
1441
1442 let decomposition = left_different.trie_val;
1443 if (decomposition
1444 & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER))
1445 == 0
1446 {
1447 left_c = left_different.character();
1448 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1449 && ((decomposition & LOW_ZEROS_MASK) != 0)
1450 {
1451 left_c = char_from_u32(decomposition & 0x7FFF);
1454 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1455 left_c = left_different.character();
1456 if right_different.trie_val == HANGUL_SYLLABLE_MARKER {
1457 let left_hangul_offset =
1460 u32::from(left_c).wrapping_sub(HANGUL_S_BASE);
1461 let right_hangul_offset =
1462 u32::from(right_different.character())
1463 .wrapping_sub(HANGUL_S_BASE);
1464 debug_assert!(left_hangul_offset < HANGUL_S_COUNT);
1466 debug_assert!(right_hangul_offset < HANGUL_S_COUNT);
1467 hangul_syllable_compare!(
1468 left_hangul_offset,
1469 right_hangul_offset,
1470 );
1471 break 'prefix;
1475 }
1476 } else {
1477 break;
1478 }
1479
1480 let decomposition = right_different.trie_val;
1481 if (decomposition
1482 & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER))
1483 == 0
1484 {
1485 right_c = right_different.character();
1486 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1487 && ((decomposition & LOW_ZEROS_MASK) != 0)
1488 {
1489 right_c = char_from_u32(decomposition & 0x7FFF);
1492 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1493 right_c = right_different.character();
1494 } else {
1495 break;
1496 }
1497
1498 if head_last_ce32 == CollationElement32::default() {
1502 head_last_ce32 = self.tailoring.ce32_for_char(head_last_c);
1503 if head_last_ce32 == FALLBACK_CE32 {
1504 head_last_ce32 = self.root.ce32_for_char(head_last_c);
1505 }
1506 if head_last_ce32.tag_checked() == Some(Tag::Contraction)
1507 && head_last_ce32.at_least_one_suffix_contains_starter()
1508 {
1509 break;
1510 }
1511 }
1512 let mut left_data = self.tailoring;
1513 let mut left_ce32 = self.tailoring.ce32_for_char(left_c);
1514 if left_ce32 == FALLBACK_CE32 {
1515 left_ce32 = self.root.ce32_for_char(left_c);
1516 left_data = self.root;
1517 }
1518 let mut right_data = self.tailoring;
1519 let mut right_ce32 = self.tailoring.ce32_for_char(right_c);
1520 if right_ce32 == FALLBACK_CE32 {
1521 right_ce32 = self.root.ce32_for_char(right_c);
1522 right_data = self.root;
1523 }
1524
1525 if let Some(mut left_primary) = left_ce32
1531 .to_primary_in_quick_check_numeric(
1532 left_data,
1533 numeric_primary.is_some(),
1534 )
1535 {
1536 if let Some(mut right_primary) = right_ce32
1537 .to_primary_in_quick_check_numeric(
1538 right_data,
1539 numeric_primary.is_some(),
1540 )
1541 {
1542 quick_primary_compare!(
1543 left_primary,
1544 right_primary,
1545 variable_top,
1546 self,
1547 );
1548 }
1549 }
1550
1551 if left_ce32.tag_checked() == Some(Tag::Prefix)
1552 || right_ce32.tag_checked() == Some(Tag::Prefix)
1553 {
1554 break;
1558 }
1559
1560 if numeric_primary.is_some()
1561 && head_last_ce32.tag_checked() == Some(Tag::Digit)
1562 && ((left_ce32.tag_checked() == Some(Tag::Digit)
1563 && left_ce32.digit() == 0)
1564 || (right_ce32.tag_checked() == Some(Tag::Digit)
1565 && right_ce32.digit() == 0))
1566 {
1567 break;
1569 }
1570
1571 break 'prefix;
1576 }
1577 }
1578 }
1579 let mut tail_first_c;
1580 let mut tail_first_ce32;
1581 let mut tail_first_ok;
1582 loop {
1583 left.prepend_upcoming_before_init(head_last.clone());
1585 right.prepend_upcoming_before_init(head_last.clone());
1586
1587 tail_first_c = head_last_c;
1588 tail_first_ce32 = head_last_ce32;
1589 tail_first_ok = head_last_ok;
1590
1591 let Some((head_last_c_new, decomposition)) = head_chars.next_back() else {
1592 break 'prefix;
1595 };
1596 head_last_c = head_last_c_new;
1597 head_last = CharacterAndClassAndTrieValue::new_with_trie_val(
1598 head_last_c,
1599 decomposition,
1600 );
1601 head_last_ce32 = CollationElement32::default();
1602 head_last_ok = false;
1603
1604 if (decomposition & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER)) == 0 {
1605 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1609 && ((decomposition & LOW_ZEROS_MASK) != 0)
1610 {
1611 head_last_c = char_from_u32(decomposition & 0x7FFF);
1614 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1615 head_last_ce32 = FFFD_CE32;
1616 } else {
1617 continue;
1618 }
1619 head_last_ok = true;
1620 if !tail_first_ok {
1621 continue;
1622 }
1623 if head_last_ce32 == CollationElement32::default() {
1626 head_last_ce32 = self.tailoring.ce32_for_char(head_last_c);
1627 if head_last_ce32 == FALLBACK_CE32 {
1628 head_last_ce32 = self.root.ce32_for_char(head_last_c);
1629 }
1630 if head_last_ce32.tag_checked() == Some(Tag::Contraction)
1631 && head_last_ce32.at_least_one_suffix_contains_starter()
1632 {
1633 continue;
1634 }
1635 }
1636 if tail_first_ce32 == CollationElement32::default() {
1640 tail_first_ce32 = self.tailoring.ce32_for_char(tail_first_c);
1641 if tail_first_ce32 == FALLBACK_CE32 {
1642 tail_first_ce32 = self.root.ce32_for_char(tail_first_c);
1643 }
1644 } if tail_first_ce32.tag_checked() == Some(Tag::Prefix) {
1646 continue;
1647 }
1648 if numeric_primary.is_some()
1649 && head_last_ce32.tag_checked() == Some(Tag::Digit)
1650 && tail_first_ce32.tag_checked() == Some(Tag::Digit)
1651 && tail_first_ce32.digit() == 0
1652 {
1653 continue;
1655 }
1656 break 'prefix;
1658 }
1659 } else {
1660 break 'prefix;
1662 }
1663 }
1665
1666 left.init();
1669 right.init();
1670
1671 loop {
1672 let mut left_primary;
1673 'left_primary_loop: loop {
1674 let ce = left.next();
1675 left_primary = ce.primary();
1676 if !(left_primary < variable_top && left_primary > MERGE_SEPARATOR_PRIMARY) {
1679 left_ces.push(ce);
1680 } else {
1681 any_variable = true;
1684 left_ces.push(ce.clone_with_non_primary_zeroed());
1689 loop {
1690 let ce = left.next();
1692 left_primary = ce.primary();
1693 if left_primary != 0
1694 && !(left_primary < variable_top
1695 && left_primary > MERGE_SEPARATOR_PRIMARY)
1696 {
1697 left_ces.push(ce);
1699 break 'left_primary_loop;
1700 }
1701 left_ces.push(ce.clone_with_non_primary_zeroed());
1704 }
1705 }
1706 if left_primary != 0 {
1707 break;
1708 }
1709 }
1710 let mut right_primary;
1711 'right_primary_loop: loop {
1712 let ce = right.next();
1713 right_primary = ce.primary();
1714 if !(right_primary < variable_top && right_primary > MERGE_SEPARATOR_PRIMARY) {
1717 right_ces.push(ce);
1718 } else {
1719 any_variable = true;
1722 right_ces.push(ce.clone_with_non_primary_zeroed());
1727 loop {
1728 let ce = right.next();
1730 right_primary = ce.primary();
1731 if right_primary != 0
1732 && !(right_primary < variable_top
1733 && right_primary > MERGE_SEPARATOR_PRIMARY)
1734 {
1735 right_ces.push(ce);
1737 break 'right_primary_loop;
1738 }
1739 right_ces.push(ce.clone_with_non_primary_zeroed());
1742 }
1743 }
1744 if right_primary != 0 {
1745 break;
1746 }
1747 }
1748 if left_primary != right_primary {
1749 if let Some(reordering) = &self.reordering {
1750 left_primary = reordering.reorder(left_primary);
1751 right_primary = reordering.reorder(right_primary);
1752 }
1753 if left_primary < right_primary {
1754 return Ordering::Less;
1755 }
1756 return Ordering::Greater;
1757 }
1758 if left_primary == NO_CE_PRIMARY {
1759 break;
1760 }
1761 }
1762
1763 debug_assert_eq!(left_ces.last(), Some(&NO_CE));
1767 debug_assert_eq!(right_ces.last(), Some(&NO_CE));
1768
1769 if self.options.strength() >= Strength::Secondary {
1781 if !self.options.backward_second_level() {
1782 let mut left_iter = left_ces.iter();
1783 let mut right_iter = right_ces.iter();
1784 let mut left_secondary;
1785 let mut right_secondary;
1786 loop {
1787 loop {
1788 left_secondary = left_iter.next().unwrap_or_default().secondary();
1789 if left_secondary != 0 {
1790 break;
1791 }
1792 }
1793 loop {
1794 right_secondary = right_iter.next().unwrap_or_default().secondary();
1795 if right_secondary != 0 {
1796 break;
1797 }
1798 }
1799 if left_secondary != right_secondary {
1800 if left_secondary < right_secondary {
1801 return Ordering::Less;
1802 }
1803 return Ordering::Greater;
1804 }
1805 if left_secondary == NO_CE_SECONDARY {
1806 break;
1807 }
1808 }
1809 } else {
1810 let mut left_remaining = &left_ces[..];
1811 let mut right_remaining = &right_ces[..];
1812 loop {
1813 if left_remaining.is_empty() {
1814 debug_assert!(right_remaining.is_empty());
1815 break;
1816 }
1817 let (left_prefix, right_prefix) = {
1818 let mut left_iter = left_remaining.iter();
1819 loop {
1820 let left_primary = left_iter.next().unwrap_or_default().primary();
1821 if left_primary != 0 && left_primary <= MERGE_SEPARATOR_PRIMARY {
1822 break;
1823 }
1824 debug_assert_ne!(left_primary, NO_CE_PRIMARY);
1825 }
1826 let left_new_remaining = left_iter.as_slice();
1827 #[expect(clippy::indexing_slicing)]
1829 let left_prefix =
1830 &left_remaining[..left_remaining.len() - 1 - left_new_remaining.len()];
1831 left_remaining = left_new_remaining;
1832
1833 let mut right_iter = right_remaining.iter();
1834 loop {
1835 let right_primary = right_iter.next().unwrap_or_default().primary();
1836 if right_primary != 0 && right_primary <= MERGE_SEPARATOR_PRIMARY {
1837 break;
1838 }
1839 debug_assert_ne!(right_primary, NO_CE_PRIMARY);
1840 }
1841 let right_new_remaining = right_iter.as_slice();
1842 #[expect(clippy::indexing_slicing)]
1844 let right_prefix = &right_remaining
1845 [..right_remaining.len() - 1 - right_new_remaining.len()];
1846 right_remaining = right_new_remaining;
1847
1848 (left_prefix, right_prefix)
1849 };
1850 let mut left_iter = left_prefix.iter();
1851 let mut right_iter = right_prefix.iter();
1852
1853 let mut left_secondary;
1854 let mut right_secondary;
1855 loop {
1856 loop {
1857 left_secondary = left_iter.next_back().unwrap_or_default().secondary();
1858 if left_secondary != 0 {
1859 break;
1860 }
1861 }
1862 loop {
1863 right_secondary =
1864 right_iter.next_back().unwrap_or_default().secondary();
1865 if right_secondary != 0 {
1866 break;
1867 }
1868 }
1869 if left_secondary != right_secondary {
1870 if left_secondary < right_secondary {
1871 return Ordering::Less;
1872 }
1873 return Ordering::Greater;
1874 }
1875 if left_secondary == NO_CE_SECONDARY {
1876 break;
1877 }
1878 }
1879 }
1880 }
1881 }
1882
1883 if self.options.case_level() {
1884 let mut left_non_primary;
1885 let mut right_non_primary;
1886 let mut left_case;
1887 let mut right_case;
1888 let mut left_iter = left_ces.iter();
1889 let mut right_iter = right_ces.iter();
1890 if self.options.strength() == Strength::Primary {
1891 loop {
1897 loop {
1898 let ce = left_iter.next().unwrap_or_default();
1899 left_non_primary = ce.non_primary();
1900 if !ce.either_half_zero() {
1901 break;
1902 }
1903 }
1904 left_case = left_non_primary.case();
1905 loop {
1906 let ce = right_iter.next().unwrap_or_default();
1907 right_non_primary = ce.non_primary();
1908 if !ce.either_half_zero() {
1909 break;
1910 }
1911 }
1912 right_case = right_non_primary.case();
1913 if left_case != right_case {
1917 if !self.options.upper_first() {
1918 if left_case < right_case {
1919 return Ordering::Less;
1920 }
1921 return Ordering::Greater;
1922 }
1923 if left_case < right_case {
1924 return Ordering::Greater;
1925 }
1926 return Ordering::Less;
1927 }
1928 if left_non_primary.secondary() == NO_CE_SECONDARY {
1929 break;
1930 }
1931 }
1932 } else {
1933 loop {
1948 loop {
1949 left_non_primary = left_iter.next().unwrap_or_default().non_primary();
1950 if left_non_primary.secondary() != 0 {
1951 break;
1952 }
1953 }
1954 left_case = left_non_primary.case();
1955 loop {
1956 right_non_primary = right_iter.next().unwrap_or_default().non_primary();
1957 if right_non_primary.secondary() != 0 {
1958 break;
1959 }
1960 }
1961 right_case = right_non_primary.case();
1962 if left_case != right_case {
1966 if !self.options.upper_first() {
1967 if left_case < right_case {
1968 return Ordering::Less;
1969 }
1970 return Ordering::Greater;
1971 }
1972 if left_case < right_case {
1973 return Ordering::Greater;
1974 }
1975 return Ordering::Less;
1976 }
1977 if left_non_primary.secondary() == NO_CE_SECONDARY {
1978 break;
1979 }
1980 }
1981 }
1982 }
1983
1984 if let Some(tertiary_mask) = self.options.tertiary_mask() {
1985 let mut any_quaternaries = AnyQuaternaryAccumulator::new();
1986 let mut left_iter = left_ces.iter();
1987 let mut right_iter = right_ces.iter();
1988 loop {
1989 let mut left_non_primary;
1990 let mut left_tertiary;
1991 loop {
1992 left_non_primary = left_iter.next().unwrap_or_default().non_primary();
1993 any_quaternaries.accumulate(left_non_primary);
1994 debug_assert!(
1995 left_non_primary.tertiary() != 0 || left_non_primary.case_quaternary() == 0
1996 );
1997 left_tertiary = left_non_primary.tertiary_case_quarternary(tertiary_mask);
1998 if left_tertiary != 0 {
1999 break;
2000 }
2001 }
2002
2003 let mut right_non_primary;
2004 let mut right_tertiary;
2005 loop {
2006 right_non_primary = right_iter.next().unwrap_or_default().non_primary();
2007 any_quaternaries.accumulate(right_non_primary);
2008 debug_assert!(
2009 right_non_primary.tertiary() != 0
2010 || right_non_primary.case_quaternary() == 0
2011 );
2012 right_tertiary = right_non_primary.tertiary_case_quarternary(tertiary_mask);
2013 if right_tertiary != 0 {
2014 break;
2015 }
2016 }
2017
2018 if left_tertiary != right_tertiary {
2019 if self.options.upper_first() {
2020 if left_tertiary > NO_CE_TERTIARY {
2027 if left_non_primary.secondary() != 0 {
2028 left_tertiary ^= 0xC000;
2029 } else {
2030 left_tertiary += 0x4000;
2031 }
2032 }
2033 if right_tertiary > NO_CE_TERTIARY {
2034 if right_non_primary.secondary() != 0 {
2035 right_tertiary ^= 0xC000;
2036 } else {
2037 right_tertiary += 0x4000;
2038 }
2039 }
2040 }
2041 if left_tertiary < right_tertiary {
2042 return Ordering::Less;
2043 }
2044 return Ordering::Greater;
2045 }
2046
2047 if left_tertiary == NO_CE_TERTIARY {
2048 break;
2049 }
2050 }
2051 if !any_variable && !any_quaternaries.has_quaternary() {
2052 return Ordering::Equal;
2053 }
2054 } else {
2055 return Ordering::Equal;
2056 }
2057
2058 if self.options.strength() <= Strength::Tertiary {
2059 return Ordering::Equal;
2060 }
2061
2062 let mut left_iter = left_ces.iter();
2063 let mut right_iter = right_ces.iter();
2064 loop {
2065 let mut left_quaternary;
2066 loop {
2067 let ce = left_iter.next().unwrap_or_default();
2068 if ce.tertiary_ignorable() {
2069 left_quaternary = ce.primary();
2070 } else {
2071 left_quaternary = ce.quaternary();
2072 }
2073 if left_quaternary != 0 {
2074 break;
2075 }
2076 }
2077 let mut right_quaternary;
2078 loop {
2079 let ce = right_iter.next().unwrap_or_default();
2080 if ce.tertiary_ignorable() {
2081 right_quaternary = ce.primary();
2082 } else {
2083 right_quaternary = ce.quaternary();
2084 }
2085 if right_quaternary != 0 {
2086 break;
2087 }
2088 }
2089 if left_quaternary != right_quaternary {
2090 if let Some(reordering) = &self.reordering {
2091 left_quaternary = reordering.reorder(left_quaternary);
2092 right_quaternary = reordering.reorder(right_quaternary);
2093 }
2094 if left_quaternary < right_quaternary {
2095 return Ordering::Less;
2096 }
2097 return Ordering::Greater;
2098 }
2099 if left_quaternary == NO_CE_PRIMARY {
2100 break;
2101 }
2102 }
2103
2104 Ordering::Equal
2105 }
2106
2107 fn sort_key_levels(&self) -> u8 {
2108 #[expect(clippy::indexing_slicing)]
2109 let mut levels = LEVEL_MASKS[self.options.strength() as usize];
2110 if self.options.case_level() {
2111 levels |= CASE_LEVEL_FLAG;
2112 }
2113 levels
2114 }
2115
2116 pub fn write_sort_key_to<S>(&self, s: &str, sink: &mut S) -> Result<S::Output, S::Error>
2165 where
2166 S: CollationKeySink + ?Sized,
2167 S::State: Default,
2168 {
2169 self.write_sort_key_impl(s.chars_with_trie_default_for_ascii(self.norm_trie()), sink)
2170 }
2171
2172 pub fn write_sort_key_utf8_to<S>(&self, s: &[u8], sink: &mut S) -> Result<S::Output, S::Error>
2176 where
2177 S: CollationKeySink + ?Sized,
2178 S::State: Default,
2179 {
2180 self.write_sort_key_impl(s.chars_with_trie_default_for_ascii(self.norm_trie()), sink)
2181 }
2182
2183 pub fn write_sort_key_utf16_to<S>(&self, s: &[u16], sink: &mut S) -> Result<S::Output, S::Error>
2187 where
2188 S: CollationKeySink + ?Sized,
2189 S::State: Default,
2190 {
2191 self.write_sort_key_impl(s.chars_with_trie(self.norm_trie()), sink)
2192 }
2193
2194 fn write_sort_key_impl<I, T, S>(
2195 &'data self,
2196 iter: I,
2197 sink: &mut S,
2198 ) -> Result<S::Output, S::Error>
2199 where
2200 I: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + Clone + 'data,
2201 T: AbstractCodePointTrie<'data, u32> + 'data,
2202 S: CollationKeySink + ?Sized,
2203 S::State: Default,
2204 {
2205 let identical = if self.options.strength() == Strength::Identical {
2206 Some(iter.clone())
2207 } else {
2208 None
2209 };
2210
2211 let mut state = S::State::default();
2212 self.write_sort_key_up_to_quaternary(iter, sink, &mut state)?;
2213
2214 if let Some(iter) = identical {
2215 sink.write_byte(&mut state, LEVEL_SEPARATOR_BYTE)?;
2216
2217 let mut iter = icu_normalizer::new_decomposition(iter, self.tables);
2218 let _ = iter.next(); write_identical_level(iter, sink, &mut state)?;
2220 }
2221
2222 sink.finish(state)
2223 }
2224
2225 fn write_sort_key_up_to_quaternary<I, S, T>(
2230 &'data self,
2231 iter: I,
2232 sink: &mut S,
2233 state: &mut S::State,
2234 ) -> Result<(), S::Error>
2235 where
2236 I: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + Clone + 'data,
2237 T: AbstractCodePointTrie<'data, u32> + 'data,
2238 S: CollationKeySink + ?Sized,
2239 {
2240 let levels = self.sort_key_levels();
2242
2243 let mut iter = collation_elements!(self, iter, self.numeric_primary());
2244 iter.init();
2245 let variable_top = self.variable_top();
2246
2247 let tertiary_mask = self.options.tertiary_mask().unwrap_or_default();
2248
2249 let mut cases = SortKeyLevel::default();
2250 let mut secondaries = SortKeyLevel::default();
2251 let mut tertiaries = SortKeyLevel::default();
2252 let mut quaternaries = SortKeyLevel::default();
2253
2254 let mut prev_reordered_primary = 0;
2255 let mut common_cases = 0usize;
2256 let mut common_secondaries = 0usize;
2257 let mut common_tertiaries = 0usize;
2258 let mut common_quaternaries = 0usize;
2259 let mut prev_secondary = 0;
2260 let mut sec_segment_start = 0;
2261
2262 loop {
2263 let mut ce = iter.next();
2264 let mut p = ce.primary();
2265 if p < variable_top && p > MERGE_SEPARATOR_PRIMARY {
2266 if common_quaternaries != 0 {
2269 common_quaternaries -= 1;
2270 while common_quaternaries >= QUAT_COMMON[WEIGHT_MAX_COUNT] as _ {
2271 quaternaries.append_byte(QUAT_COMMON[WEIGHT_MIDDLE]);
2272 common_quaternaries -= QUAT_COMMON[WEIGHT_MAX_COUNT] as usize;
2273 }
2274 quaternaries.append_byte(QUAT_COMMON[WEIGHT_LOW] + common_quaternaries as u8);
2276 common_quaternaries = 0;
2277 }
2278
2279 loop {
2280 if levels & QUATERNARY_LEVEL_FLAG != 0 {
2281 if let Some(reordering) = &self.reordering {
2282 p = reordering.reorder(p);
2283 }
2284 if (p >> 24) as u8 >= QUAT_SHIFTED_LIMIT_BYTE {
2285 quaternaries.append_byte(QUAT_SHIFTED_LIMIT_BYTE);
2288 }
2289 quaternaries.append_weight_32(p);
2290 }
2291 loop {
2292 ce = iter.next();
2293 p = ce.primary();
2294 if p != 0 {
2295 break;
2296 }
2297 }
2298 if !(p < variable_top && p > MERGE_SEPARATOR_PRIMARY) {
2299 break;
2300 }
2301 }
2302 }
2303
2304 if p > NO_CE_PRIMARY && levels & PRIMARY_LEVEL_FLAG != 0 {
2308 let is_compressible = self.special_primaries.is_compressible((p >> 24) as _);
2310 if let Some(reordering) = &self.reordering {
2311 p = reordering.reorder(p);
2312 }
2313 let p1 = (p >> 24) as u8;
2314 if !is_compressible || p1 != (prev_reordered_primary >> 24) as u8 {
2315 if prev_reordered_primary != 0 {
2316 if p < prev_reordered_primary {
2317 if p1 > MERGE_SEPARATOR_BYTE {
2320 sink.write(state, &[PRIMARY_COMPRESSION_LOW_BYTE])?;
2321 }
2322 } else {
2323 sink.write(state, &[PRIMARY_COMPRESSION_HIGH_BYTE])?;
2324 }
2325 }
2326 sink.write_byte(state, p1)?;
2327 prev_reordered_primary = if is_compressible { p } else { 0 };
2328 }
2329
2330 let p2 = (p >> 16) as u8;
2331 if p2 != 0 {
2332 let (b0, b1, b2) = (p2, (p >> 8) as _, p as _);
2333 sink.write_byte(state, b0)?;
2334 if b1 != 0 {
2335 sink.write_byte(state, b1)?;
2336 if b2 != 0 {
2337 sink.write_byte(state, b2)?;
2338 }
2339 }
2340 }
2341 }
2342
2343 let non_primary = ce.non_primary();
2344 if non_primary.ignorable() {
2345 continue; }
2347
2348 macro_rules! handle_common {
2349 ($key:ident, $w:ident, $common:ident, $weights:ident, $lim:expr) => {
2350 if $common != 0 {
2351 $common -= 1;
2352 while $common >= $weights[WEIGHT_MAX_COUNT] as _ {
2353 $key.append_byte($weights[WEIGHT_MIDDLE]);
2354 $common -= $weights[WEIGHT_MAX_COUNT] as usize;
2355 }
2356 let b = if $w < $lim {
2357 $weights[WEIGHT_LOW] + ($common as u8)
2358 } else {
2359 $weights[WEIGHT_HIGH] - ($common as u8)
2360 };
2361 $key.append_byte(b);
2362 $common = 0;
2363 }
2364 };
2365 ($key:ident, $w:ident, $common:ident, $weights:ident) => {
2366 handle_common!($key, $w, $common, $weights, COMMON_WEIGHT16);
2367 };
2368 }
2369
2370 if levels & SECONDARY_LEVEL_FLAG != 0 {
2371 let s = non_primary.secondary();
2372 if s == 0 {
2373 } else if s == COMMON_WEIGHT16
2375 && (!self.options.backward_second_level() || p != MERGE_SEPARATOR_PRIMARY)
2376 {
2377 common_secondaries += 1;
2380 } else if !self.options.backward_second_level() {
2381 handle_common!(secondaries, s, common_secondaries, SEC_COMMON);
2382 secondaries.append_weight_16(s);
2383 } else {
2384 if common_secondaries != 0 {
2385 common_secondaries -= 1;
2386 let remainder = common_secondaries % SEC_COMMON[WEIGHT_MAX_COUNT] as usize;
2388 let b = if prev_secondary < COMMON_WEIGHT16 {
2389 SEC_COMMON[WEIGHT_LOW] + remainder as u8
2390 } else {
2391 SEC_COMMON[WEIGHT_HIGH] - remainder as u8
2392 };
2393 secondaries.append_byte(b);
2394 common_secondaries -= remainder;
2395 while common_secondaries > 0 {
2397 secondaries.append_byte(SEC_COMMON[WEIGHT_MIDDLE]);
2399 common_secondaries -= SEC_COMMON[WEIGHT_MAX_COUNT] as usize;
2400 }
2401 }
2403 if 0 < p && p <= MERGE_SEPARATOR_PRIMARY {
2404 let secs = &mut secondaries.buf;
2407 let last = secs.len() - 1;
2408 if sec_segment_start < last {
2409 let mut q = sec_segment_start;
2410 let mut r = last;
2411
2412 #[expect(clippy::indexing_slicing)]
2414 while q < r {
2415 let b = secs[q];
2416 secs[q] = secs[r];
2417 q += 1;
2418 secs[r] = b;
2419 r -= 1;
2420 }
2421 }
2422 let b = if p == NO_CE_PRIMARY {
2423 LEVEL_SEPARATOR_BYTE
2424 } else {
2425 MERGE_SEPARATOR_BYTE
2426 };
2427 secondaries.append_byte(b);
2428 prev_secondary = 0;
2429 sec_segment_start = secondaries.len();
2430 } else {
2431 secondaries.append_reverse_weight_16(s);
2432 prev_secondary = s;
2433 }
2434 }
2435 }
2436
2437 if levels & CASE_LEVEL_FLAG != 0 {
2438 if self.options.strength() == Strength::Primary && p == 0
2439 || non_primary.bits() <= 0xffff
2440 {
2441 } else {
2445 let mut c = ((non_primary.bits() >> 8) & 0xff) as u8;
2447 debug_assert_ne!(c & 0xc0, 0xc0);
2448 if c & 0xc0 == 0 && c > LEVEL_SEPARATOR_BYTE {
2449 common_cases += 1;
2450 } else {
2451 if !self.options.upper_first() {
2452 if common_cases != 0 && (c > LEVEL_SEPARATOR_BYTE || !cases.is_empty())
2457 {
2458 common_cases -= 1;
2459 while common_cases >= CASE_LOWER_FIRST_COMMON[WEIGHT_MAX_COUNT] as _
2460 {
2461 cases.append_byte(CASE_LOWER_FIRST_COMMON[WEIGHT_MIDDLE] << 4);
2462 common_cases -=
2463 CASE_LOWER_FIRST_COMMON[WEIGHT_MAX_COUNT] as usize;
2464 }
2465 let b = if c <= LEVEL_SEPARATOR_BYTE {
2466 CASE_LOWER_FIRST_COMMON[WEIGHT_LOW] + common_cases as u8
2467 } else {
2468 CASE_LOWER_FIRST_COMMON[WEIGHT_HIGH] - common_cases as u8
2469 };
2470 cases.append_byte(b << 4);
2471 common_cases = 0;
2472 }
2473 if c > LEVEL_SEPARATOR_BYTE {
2474 c = (CASE_LOWER_FIRST_COMMON[WEIGHT_HIGH] + (c >> 6)) << 4;
2476 }
2477 } else {
2478 if common_cases != 0 {
2483 common_cases -= 1;
2484 while common_cases >= CASE_UPPER_FIRST_COMMON[WEIGHT_MAX_COUNT] as _
2485 {
2486 cases.append_byte(CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] << 4);
2487 common_cases -=
2488 CASE_UPPER_FIRST_COMMON[WEIGHT_MAX_COUNT] as usize;
2489 }
2490 cases.append_byte(
2491 (CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] + common_cases as u8) << 4,
2492 );
2493 common_cases = 0;
2494 }
2495 if c > LEVEL_SEPARATOR_BYTE {
2496 c = (CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] - (c >> 6)) << 4;
2498 }
2499 }
2500 cases.append_byte(c);
2503 }
2504 }
2505 }
2506
2507 if levels & TERTIARY_LEVEL_FLAG != 0 {
2508 let mut t = non_primary.tertiary_case_quarternary(tertiary_mask);
2509 debug_assert_ne!(non_primary.bits() & 0xc000, 0xc000);
2510 if t == COMMON_WEIGHT16 {
2511 common_tertiaries += 1;
2512 } else if tertiary_mask & 0x8000 == 0 {
2513 handle_common!(tertiaries, t, common_tertiaries, TER_ONLY_COMMON);
2516 if t > COMMON_WEIGHT16 {
2517 t += 0xc000;
2518 }
2519 tertiaries.append_weight_16(t);
2520 } else if !self.options.upper_first() {
2521 handle_common!(tertiaries, t, common_tertiaries, TER_LOWER_FIRST_COMMON);
2524 if t > COMMON_WEIGHT16 {
2525 t += 0x4000;
2526 }
2527 tertiaries.append_weight_16(t);
2528 } else {
2529 if t <= NO_CE_TERTIARY {
2542 } else if non_primary.bits() > 0xffff {
2544 t ^= 0xc000;
2546 if t < (TER_UPPER_FIRST_COMMON[WEIGHT_HIGH] as u16) << 8 {
2547 t -= 0x4000;
2548 }
2549 } else {
2550 debug_assert!((0x8600..=0xbfff).contains(&t));
2552 t += 0x4000;
2553 }
2554 handle_common!(
2555 tertiaries,
2556 t,
2557 common_tertiaries,
2558 TER_UPPER_FIRST_COMMON,
2559 (TER_UPPER_FIRST_COMMON[WEIGHT_LOW] as u16) << 8
2560 );
2561 tertiaries.append_weight_16(t);
2562 }
2563 }
2564
2565 if levels & QUATERNARY_LEVEL_FLAG != 0 {
2566 let q = (non_primary.bits() & 0xffff) as u16;
2567 if q & 0xc0 == 0 && q > NO_CE_QUATERNARY {
2568 common_quaternaries += 1;
2569 } else if q == NO_CE_QUATERNARY
2570 && self.options.alternate_handling() == AlternateHandling::NonIgnorable
2571 && quaternaries.is_empty()
2572 {
2573 quaternaries.append_byte(LEVEL_SEPARATOR_BYTE);
2581 } else {
2582 let q = if q == NO_CE_QUATERNARY {
2583 LEVEL_SEPARATOR_BYTE
2584 } else {
2585 (0xfc + ((q >> 6) & 3)) as u8
2586 };
2587 handle_common!(
2588 quaternaries,
2589 q,
2590 common_quaternaries,
2591 QUAT_COMMON,
2592 QUAT_COMMON[WEIGHT_LOW]
2593 );
2594 quaternaries.append_byte(q);
2595 }
2596 }
2597
2598 if (non_primary.bits() >> 24) as u8 == LEVEL_SEPARATOR_BYTE {
2599 break; }
2601 }
2602
2603 macro_rules! write_level {
2604 ($key:ident, $flag:ident) => {
2605 if levels & $flag != 0 {
2606 sink.write(state, &[LEVEL_SEPARATOR_BYTE])?;
2607 sink.write(state, &$key.buf)?;
2608 }
2609 };
2610 }
2611
2612 write_level!(secondaries, SECONDARY_LEVEL_FLAG);
2613
2614 if levels & CASE_LEVEL_FLAG != 0 {
2615 sink.write(state, &[LEVEL_SEPARATOR_BYTE])?;
2616
2617 let mut b = 0;
2619 if let Some((last, head)) = cases.buf.split_last() {
2620 debug_assert_eq!(*last, 1); for c in head {
2622 debug_assert_eq!(*c & 0xf, 0);
2623 debug_assert_ne!(*c, 0);
2624 if b == 0 {
2625 b = *c;
2626 } else {
2627 sink.write_byte(state, b | (*c >> 4))?;
2628 b = 0;
2629 }
2630 }
2631 } else {
2632 debug_assert!(false);
2633 }
2634 if b != 0 {
2635 sink.write_byte(state, b)?;
2636 }
2637 }
2638
2639 write_level!(tertiaries, TERTIARY_LEVEL_FLAG);
2640 write_level!(quaternaries, QUATERNARY_LEVEL_FLAG);
2641
2642 Ok(())
2643 }
2644}
2645
2646#[derive(Debug, PartialEq, Eq)]
2648pub struct TooSmall {
2649 pub length: usize,
2651}
2652
2653impl TooSmall {
2654 pub fn new(length: usize) -> Self {
2655 Self { length }
2656 }
2657}
2658
2659pub trait CollationKeySink {
2673 type Error;
2675
2676 type State;
2678
2679 type Output;
2681
2682 fn write(&mut self, state: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error>;
2684
2685 fn write_byte(&mut self, state: &mut Self::State, b: u8) -> Result<(), Self::Error> {
2687 self.write(state, &[b])
2688 }
2689
2690 fn finish(&mut self, state: Self::State) -> Result<Self::Output, Self::Error>;
2693}
2694
2695impl CollationKeySink for Vec<u8> {
2696 type Error = Infallible;
2697 type State = ();
2698 type Output = ();
2699
2700 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2701 self.extend_from_slice(buf);
2702 Ok(())
2703 }
2704
2705 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2706 Ok(())
2707 }
2708}
2709
2710impl CollationKeySink for VecDeque<u8> {
2711 type Error = Infallible;
2712 type State = ();
2713 type Output = ();
2714
2715 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2716 self.extend(buf.iter());
2717 Ok(())
2718 }
2719
2720 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2721 Ok(())
2722 }
2723}
2724
2725impl<const N: usize> CollationKeySink for SmallVec<[u8; N]> {
2726 type Error = Infallible;
2727 type State = ();
2728 type Output = ();
2729
2730 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2731 self.extend_from_slice(buf);
2732 Ok(())
2733 }
2734
2735 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2736 Ok(())
2737 }
2738}
2739
2740impl CollationKeySink for [u8] {
2741 type Error = TooSmall;
2742 type State = usize;
2743 type Output = usize;
2744
2745 fn write(&mut self, offset: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2746 if *offset + buf.len() <= self.len() {
2747 #[expect(clippy::indexing_slicing)]
2749 self[*offset..*offset + buf.len()].copy_from_slice(buf);
2750 }
2751 *offset += buf.len();
2752 Ok(())
2753 }
2754
2755 fn finish(&mut self, offset: Self::State) -> Result<Self::Output, Self::Error> {
2756 if offset <= self.len() {
2757 Ok(offset)
2758 } else {
2759 Err(TooSmall::new(offset))
2760 }
2761 }
2762}
2763
2764#[derive(Default)]
2765struct SortKeyLevel {
2766 buf: SmallVec<[u8; 40]>,
2767}
2768
2769impl SortKeyLevel {
2770 fn len(&self) -> usize {
2771 self.buf.len()
2772 }
2773
2774 fn is_empty(&self) -> bool {
2775 self.buf.is_empty()
2776 }
2777
2778 fn append_byte(&mut self, x: u8) {
2779 self.buf.push(x);
2780 }
2781
2782 fn append_weight_16(&mut self, w: u16) {
2783 debug_assert_ne!(w, 0);
2784 let b0 = (w >> 8) as u8;
2785 let b1 = w as u8;
2786 self.append_byte(b0);
2787 if b1 != 0 {
2788 self.append_byte(b1);
2789 }
2790 }
2791
2792 fn append_reverse_weight_16(&mut self, w: u16) {
2793 debug_assert_ne!(w, 0);
2794 let b0 = (w >> 8) as u8;
2795 let b1 = w as u8;
2796 if b1 != 0 {
2797 self.append_byte(b1);
2798 }
2799 self.append_byte(b0);
2800 }
2801
2802 fn append_weight_32(&mut self, w: u32) {
2803 debug_assert_ne!(w, 0);
2804 let b0 = (w >> 24) as u8;
2805 let b1 = (w >> 16) as u8;
2806 let b2 = (w >> 8) as u8;
2807 let b3 = w as u8;
2808 self.append_byte(b0);
2809 if b1 != 0 {
2810 self.append_byte(b1);
2811 if b2 != 0 {
2812 self.append_byte(b2);
2813 if b3 != 0 {
2814 self.append_byte(b3);
2815 }
2816 }
2817 }
2818 }
2819}
2820
2821macro_rules! negdivmod {
2827 ($n:ident, $d:ident, $m:ident) => {
2828 $m = $n % $d;
2829 $n /= $d;
2830 if $m < 0 {
2831 $n -= 1;
2832 $m += $d;
2833 }
2834 };
2835}
2836
2837fn write_diff<S>(mut diff: i32, sink: &mut S, state: &mut S::State) -> Result<(), S::Error>
2838where
2839 S: CollationKeySink + ?Sized,
2840{
2841 let mut out = |b| sink.write_byte(state, b);
2842
2843 if diff >= SLOPE_REACH_NEG_1 {
2844 if diff <= SLOPE_REACH_POS_1 {
2845 out((SLOPE_MIDDLE + diff) as _)?;
2846 } else if diff <= SLOPE_REACH_POS_2 {
2847 out((SLOPE_START_POS_2 + (diff / SLOPE_TAIL_COUNT)) as _)?;
2848 out((SLOPE_MIN + diff % SLOPE_TAIL_COUNT) as _)?;
2849 } else if diff <= SLOPE_REACH_POS_3 {
2850 let p2 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2851 diff /= SLOPE_TAIL_COUNT;
2852 let p1 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2853 let p0 = SLOPE_START_POS_3 + (diff / SLOPE_TAIL_COUNT);
2854 out(p0 as _)?;
2855 out(p1 as _)?;
2856 out(p2 as _)?;
2857 } else {
2858 let p3 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2859 diff /= SLOPE_TAIL_COUNT;
2860 let p2 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2861 diff /= SLOPE_TAIL_COUNT;
2862 let p1 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2863 out(SLOPE_MAX as _)?;
2864 out(p1 as _)?;
2865 out(p2 as _)?;
2866 out(p3 as _)?;
2867 }
2868 } else {
2869 let mut m;
2870
2871 if diff >= SLOPE_REACH_NEG_2 {
2872 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2873 out((SLOPE_START_NEG_2 + diff) as _)?;
2874 out((SLOPE_MIN + m) as _)?;
2875 } else if diff >= SLOPE_REACH_NEG_3 {
2876 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2877 let p2 = SLOPE_MIN + m;
2878 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2879 let p1 = SLOPE_MIN + m;
2880 let p0 = SLOPE_START_NEG_3 + diff;
2881 out(p0 as _)?;
2882 out(p1 as _)?;
2883 out(p2 as _)?;
2884 } else {
2885 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2886 let p3 = SLOPE_MIN + m;
2887 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2888 let p2 = SLOPE_MIN + m;
2889 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2890 let p1 = SLOPE_MIN + m;
2891 let _ = diff;
2892 out(SLOPE_MIN as _)?;
2893 out(p1 as _)?;
2894 out(p2 as _)?;
2895 out(p3 as _)?;
2896 }
2897 }
2898
2899 Ok(())
2900}
2901
2902fn write_identical_level<I, S>(iter: I, sink: &mut S, state: &mut S::State) -> Result<(), S::Error>
2903where
2904 I: Iterator<Item = char>,
2905 S: CollationKeySink + ?Sized,
2906{
2907 let mut prev = 0i32;
2908
2909 for c in iter {
2910 if !(0x4e00..=0xa000).contains(&prev) {
2911 prev = (prev & !0x7f) - SLOPE_REACH_NEG_1;
2912 } else {
2913 prev = 0x9fff - SLOPE_REACH_POS_2;
2915 }
2916
2917 if c == MERGE_SEPARATOR {
2918 sink.write_byte(state, MERGE_SEPARATOR_BYTE)?;
2919 prev = 0;
2920 } else {
2921 let c = c as i32;
2922 write_diff(c - prev, sink, state)?;
2923 prev = c;
2924 }
2925 }
2926 Ok(())
2927}
2928
2929#[cfg(test)]
2930mod test {
2931 use super::*;
2932 use icu_locale::locale;
2933
2934 type Key = Vec<u8>;
2935
2936 fn collator_en(strength: Strength) -> CollatorBorrowed<'static> {
2937 let locale = locale!("en").into();
2938 let mut options = CollatorOptions::default();
2939 options.strength = Some(strength);
2940 Collator::try_new(locale, options).unwrap()
2941 }
2942
2943 fn collator_en_case_level(strength: Strength) -> CollatorBorrowed<'static> {
2944 let locale = locale!("en").into();
2945 let mut options = CollatorOptions::default();
2946 options.strength = Some(strength);
2947 options.case_level = Some(crate::options::CaseLevel::On);
2948 Collator::try_new(locale, options).unwrap()
2949 }
2950
2951 fn keys(strength: Strength) -> (Key, Key, Key) {
2952 let collator = collator_en(strength);
2953
2954 let mut k0 = Vec::new();
2955 let Ok(()) = collator.write_sort_key_to("aabc", &mut k0);
2956 let mut k1 = Vec::new();
2957 let Ok(()) = collator.write_sort_key_to("aAbc", &mut k1);
2958 let mut k2 = Vec::new();
2959 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k2);
2960
2961 (k0, k1, k2)
2962 }
2963
2964 #[test]
2965 fn sort_key_primary() {
2966 let (k0, k1, k2) = keys(Strength::Primary);
2967 assert_eq!(k0, k1);
2968 assert_eq!(k1, k2);
2969 }
2970
2971 #[test]
2972 fn sort_key_secondary() {
2973 let (k0, k1, k2) = keys(Strength::Secondary);
2974 assert_eq!(k0, k1);
2975 assert!(k1 < k2);
2976 }
2977
2978 #[test]
2979 fn sort_key_tertiary() {
2980 let (k0, k1, k2) = keys(Strength::Tertiary);
2981 assert!(k0 < k1);
2982 assert!(k1 < k2);
2983 }
2984
2985 fn collator_ja(strength: Strength) -> CollatorBorrowed<'static> {
2986 let locale = locale!("ja").into();
2987 let mut options = CollatorOptions::default();
2988 options.strength = Some(strength);
2989 Collator::try_new(locale, options).unwrap()
2990 }
2991
2992 fn keys_ja_strs(strength: Strength, s0: &str, s1: &str) -> (Key, Key) {
2993 let collator = collator_ja(strength);
2994
2995 let mut k0 = Vec::new();
2996 let Ok(()) = collator.write_sort_key_to(s0, &mut k0);
2997 let mut k1 = Vec::new();
2998 let Ok(()) = collator.write_sort_key_to(s1, &mut k1);
2999
3000 (k0, k1)
3001 }
3002
3003 fn keys_ja(strength: Strength) -> (Key, Key) {
3004 keys_ja_strs(strength, "あ", "ア")
3005 }
3006
3007 #[test]
3008 fn sort_keys_ja_to_quaternary() {
3009 let (k0, k1) = keys_ja(Strength::Primary);
3010 assert_eq!(k0, k1);
3011 let (k0, k1) = keys_ja(Strength::Secondary);
3012 assert_eq!(k0, k1);
3013 let (k0, k1) = keys_ja(Strength::Tertiary);
3014 assert_eq!(k0, k1);
3015 let (k0, k1) = keys_ja(Strength::Quaternary);
3016 assert!(k0 < k1);
3017 }
3018
3019 #[test]
3020 fn sort_keys_ja_identical() {
3021 let (k0, k1) = keys_ja_strs(Strength::Quaternary, "ア", "ア");
3022 assert_eq!(k0, k1);
3023 let (k0, k1) = keys_ja_strs(Strength::Identical, "ア", "ア");
3024 assert!(k0 < k1);
3025 }
3026
3027 #[test]
3028 fn sort_keys_utf16() {
3029 let collator = collator_en(Strength::Identical);
3030
3031 const STR8: &[u8] = b"hello world!";
3032 let mut k8 = Vec::new();
3033 let Ok(()) = collator.write_sort_key_utf8_to(STR8, &mut k8);
3034
3035 const STR16: &[u16] = &[
3036 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
3037 ];
3038 let mut k16 = Vec::new();
3039 let Ok(()) = collator.write_sort_key_utf16_to(STR16, &mut k16);
3040 assert_eq!(k8, k16);
3041 }
3042
3043 #[test]
3044 fn sort_keys_invalid() {
3045 let collator = collator_en(Strength::Identical);
3046
3047 let mut k = Vec::new();
3049 let Ok(()) = collator.write_sort_key_utf8_to(b"\xf0\x90", &mut k);
3050 let mut k = Vec::new();
3051 let Ok(()) = collator.write_sort_key_utf16_to(&[0xdd1e], &mut k);
3052 }
3053
3054 #[test]
3055 fn sort_key_to_vecdeque() {
3056 let collator = collator_en(Strength::Identical);
3057
3058 let mut k0 = Vec::new();
3059 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k0);
3060 let mut k1 = VecDeque::new();
3061 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k1);
3062 assert!(k0.iter().eq(k1.iter()));
3063 }
3064
3065 #[test]
3066 fn sort_key_to_slice() {
3067 let collator = collator_en(Strength::Identical);
3068
3069 let mut k0 = Vec::new();
3070 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k0);
3071 let mut k1 = [0u8; 100];
3072 let len = collator.write_sort_key_to("áAbc", &mut k1[..]).unwrap();
3073 assert_eq!(len, k0.len());
3074 assert!(k0.iter().eq(k1[..len].iter()));
3075 }
3076
3077 #[test]
3078 fn sort_key_to_slice_no_space() {
3079 let collator = collator_en(Strength::Identical);
3080 let mut k = [0u8; 0];
3081 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3082 assert!(matches!(res, Err(TooSmall { .. })));
3083 }
3084
3085 #[test]
3086 fn sort_key_to_slice_too_long() {
3087 let collator = collator_en(Strength::Identical);
3089 let mut k = [0u8; 5];
3090 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3091 assert!(matches!(res, Err(TooSmall { .. })));
3092 }
3093
3094 #[test]
3095 fn sort_key_to_slice_identical_too_long() {
3096 let collator = collator_en(Strength::Identical);
3098 let mut k = [0u8; 22];
3099 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3100 assert!(matches!(res, Err(TooSmall { .. })));
3101 }
3102
3103 #[test]
3104 fn sort_key_just_right() {
3105 let collator = collator_en(Strength::Identical);
3107 let mut k = [0u8; 0];
3108 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3109 let len = res.unwrap_err().length;
3110
3111 let mut k = vec![0u8; len - 1];
3113 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3114 let len = res.unwrap_err().length;
3115
3116 let mut k = vec![0u8; len];
3118 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3119 assert_eq!(res, Ok(len));
3120 }
3121
3122 #[test]
3123 fn sort_key_utf16_slice_too_small() {
3124 let collator = collator_en(Strength::Identical);
3125 const STR16: &[u16] = &[0x68, 0x65, 0x6c, 0x6c, 0x6f];
3126 let mut k = [0u8; 4];
3127 let res = collator.write_sort_key_utf16_to(STR16, &mut k[..]);
3128 assert!(matches!(res, Err(TooSmall { .. })));
3129 }
3130
3131 #[test]
3132 fn sort_key_very_long() {
3133 let collator = collator_en(Strength::Secondary);
3134 let mut k = Vec::new();
3135 let Ok(()) = collator.write_sort_key_to(&"a".repeat(300), &mut k);
3136 }
3137
3138 #[test]
3139 fn sort_key_case_level() {
3140 let collator = collator_en_case_level(Strength::Tertiary);
3141 let mut k = Vec::new();
3142 let Ok(()) = collator.write_sort_key_to("aBc", &mut k);
3143 }
3144
3145 #[test]
3146 fn sort_key_case_level_empty() {
3147 let collator = collator_en_case_level(Strength::Tertiary);
3148 let mut k = Vec::new();
3149 let Ok(()) = collator.write_sort_key_to("", &mut k);
3150 }
3151
3152 fn check_sort_key_less(a: &[u16], b: &[u16]) {
3153 let collator = collator_en(Strength::Identical);
3154 let mut ak = Vec::new();
3155 let Ok(()) = collator.write_sort_key_utf16_to(a, &mut ak);
3156 let mut bk = Vec::new();
3157 let Ok(()) = collator.write_sort_key_utf16_to(b, &mut bk);
3158 assert!(ak < bk, "failed: {a:04x?} - {b:04x?}");
3159 }
3160
3161 #[test]
3162 fn sort_key_fffe_bug_6811() {
3163 check_sort_key_less(
3164 &[0xfffe, 0x0001, 0x0002, 0x0003],
3165 &[0x0001, 0xfffe, 0x0002, 0x0003],
3166 );
3167 check_sort_key_less(
3168 &[0x0001, 0xfffe, 0x0002, 0x0003],
3169 &[0x0001, 0x0002, 0xfffe, 0x0003],
3170 );
3171 check_sort_key_less(
3172 &[0x0001, 0x0002, 0xfffe, 0x0003],
3173 &[0x0001, 0x0002, 0x0003, 0xfffe],
3174 );
3175 check_sort_key_less(&[0xfffe, 0x0000, 0x0000], &[0x0000, 0xfffe, 0x0000]);
3176 check_sort_key_less(&[0x0000, 0xfffe, 0x0000], &[0x0000, 0x0000, 0xfffe]);
3177 }
3178}