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 || ((left_ce32.tag_checked() == Some(Tag::Digit))
1567 ^ (right_ce32.tag_checked() == Some(Tag::Digit))))
1568 {
1569 break;
1573 }
1574
1575 break 'prefix;
1580 }
1581 }
1582 }
1583 let mut tail_first_c;
1584 let mut tail_first_ce32;
1585 let mut tail_first_ok;
1586 loop {
1587 left.prepend_upcoming_before_init(head_last.clone());
1589 right.prepend_upcoming_before_init(head_last.clone());
1590
1591 tail_first_c = head_last_c;
1592 tail_first_ce32 = head_last_ce32;
1593 tail_first_ok = head_last_ok;
1594
1595 let Some((head_last_c_new, decomposition)) = head_chars.next_back() else {
1596 break 'prefix;
1599 };
1600 head_last_c = head_last_c_new;
1601 head_last = CharacterAndClassAndTrieValue::new_with_trie_val(
1602 head_last_c,
1603 decomposition,
1604 );
1605 head_last_ce32 = CollationElement32::default();
1606 head_last_ok = false;
1607
1608 if (decomposition & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER)) == 0 {
1609 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1613 && ((decomposition & LOW_ZEROS_MASK) != 0)
1614 {
1615 head_last_c = char_from_u32(decomposition & 0x7FFF);
1618 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1619 head_last_ce32 = FFFD_CE32;
1620 } else {
1621 continue;
1622 }
1623 head_last_ok = true;
1624 if !tail_first_ok {
1625 continue;
1626 }
1627 if head_last_ce32 == CollationElement32::default() {
1630 head_last_ce32 = self.tailoring.ce32_for_char(head_last_c);
1631 if head_last_ce32 == FALLBACK_CE32 {
1632 head_last_ce32 = self.root.ce32_for_char(head_last_c);
1633 }
1634 if head_last_ce32.tag_checked() == Some(Tag::Contraction)
1635 && head_last_ce32.at_least_one_suffix_contains_starter()
1636 {
1637 continue;
1638 }
1639 }
1640 if tail_first_ce32 == CollationElement32::default() {
1644 tail_first_ce32 = self.tailoring.ce32_for_char(tail_first_c);
1645 if tail_first_ce32 == FALLBACK_CE32 {
1646 tail_first_ce32 = self.root.ce32_for_char(tail_first_c);
1647 }
1648 } if tail_first_ce32.tag_checked() == Some(Tag::Prefix) {
1650 continue;
1651 }
1652 if numeric_primary.is_some()
1653 && head_last_ce32.tag_checked() == Some(Tag::Digit)
1654 && tail_first_ce32.tag_checked() == Some(Tag::Digit)
1655 && tail_first_ce32.digit() == 0
1656 {
1657 continue;
1659 }
1660 break 'prefix;
1662 }
1663 } else {
1664 break 'prefix;
1666 }
1667 }
1669
1670 left.init();
1673 right.init();
1674
1675 loop {
1676 let mut left_primary;
1677 'left_primary_loop: loop {
1678 let ce = left.next();
1679 left_primary = ce.primary();
1680 if !(left_primary < variable_top && left_primary > MERGE_SEPARATOR_PRIMARY) {
1683 left_ces.push(ce);
1684 } else {
1685 any_variable = true;
1688 left_ces.push(ce.clone_with_non_primary_zeroed());
1693 loop {
1694 let ce = left.next();
1696 left_primary = ce.primary();
1697 if left_primary != 0
1698 && !(left_primary < variable_top
1699 && left_primary > MERGE_SEPARATOR_PRIMARY)
1700 {
1701 left_ces.push(ce);
1703 break 'left_primary_loop;
1704 }
1705 left_ces.push(ce.clone_with_non_primary_zeroed());
1708 }
1709 }
1710 if left_primary != 0 {
1711 break;
1712 }
1713 }
1714 let mut right_primary;
1715 'right_primary_loop: loop {
1716 let ce = right.next();
1717 right_primary = ce.primary();
1718 if !(right_primary < variable_top && right_primary > MERGE_SEPARATOR_PRIMARY) {
1721 right_ces.push(ce);
1722 } else {
1723 any_variable = true;
1726 right_ces.push(ce.clone_with_non_primary_zeroed());
1731 loop {
1732 let ce = right.next();
1734 right_primary = ce.primary();
1735 if right_primary != 0
1736 && !(right_primary < variable_top
1737 && right_primary > MERGE_SEPARATOR_PRIMARY)
1738 {
1739 right_ces.push(ce);
1741 break 'right_primary_loop;
1742 }
1743 right_ces.push(ce.clone_with_non_primary_zeroed());
1746 }
1747 }
1748 if right_primary != 0 {
1749 break;
1750 }
1751 }
1752 if left_primary != right_primary {
1753 if let Some(reordering) = &self.reordering {
1754 left_primary = reordering.reorder(left_primary);
1755 right_primary = reordering.reorder(right_primary);
1756 }
1757 if left_primary < right_primary {
1758 return Ordering::Less;
1759 }
1760 return Ordering::Greater;
1761 }
1762 if left_primary == NO_CE_PRIMARY {
1763 break;
1764 }
1765 }
1766
1767 debug_assert_eq!(left_ces.last(), Some(&NO_CE));
1771 debug_assert_eq!(right_ces.last(), Some(&NO_CE));
1772
1773 if self.options.strength() >= Strength::Secondary {
1785 if !self.options.backward_second_level() {
1786 let mut left_iter = left_ces.iter();
1787 let mut right_iter = right_ces.iter();
1788 let mut left_secondary;
1789 let mut right_secondary;
1790 loop {
1791 loop {
1792 left_secondary = left_iter.next().unwrap_or_default().secondary();
1793 if left_secondary != 0 {
1794 break;
1795 }
1796 }
1797 loop {
1798 right_secondary = right_iter.next().unwrap_or_default().secondary();
1799 if right_secondary != 0 {
1800 break;
1801 }
1802 }
1803 if left_secondary != right_secondary {
1804 if left_secondary < right_secondary {
1805 return Ordering::Less;
1806 }
1807 return Ordering::Greater;
1808 }
1809 if left_secondary == NO_CE_SECONDARY {
1810 break;
1811 }
1812 }
1813 } else {
1814 let mut left_remaining = &left_ces[..];
1815 let mut right_remaining = &right_ces[..];
1816 loop {
1817 if left_remaining.is_empty() {
1818 debug_assert!(right_remaining.is_empty());
1819 break;
1820 }
1821 let (left_prefix, right_prefix) = {
1822 let mut left_iter = left_remaining.iter();
1823 loop {
1824 let left_primary = left_iter.next().unwrap_or_default().primary();
1825 if left_primary != 0 && left_primary <= MERGE_SEPARATOR_PRIMARY {
1826 break;
1827 }
1828 debug_assert_ne!(left_primary, NO_CE_PRIMARY);
1829 }
1830 let left_new_remaining = left_iter.as_slice();
1831 #[expect(clippy::indexing_slicing)]
1833 let left_prefix =
1834 &left_remaining[..left_remaining.len() - 1 - left_new_remaining.len()];
1835 left_remaining = left_new_remaining;
1836
1837 let mut right_iter = right_remaining.iter();
1838 loop {
1839 let right_primary = right_iter.next().unwrap_or_default().primary();
1840 if right_primary != 0 && right_primary <= MERGE_SEPARATOR_PRIMARY {
1841 break;
1842 }
1843 debug_assert_ne!(right_primary, NO_CE_PRIMARY);
1844 }
1845 let right_new_remaining = right_iter.as_slice();
1846 #[expect(clippy::indexing_slicing)]
1848 let right_prefix = &right_remaining
1849 [..right_remaining.len() - 1 - right_new_remaining.len()];
1850 right_remaining = right_new_remaining;
1851
1852 (left_prefix, right_prefix)
1853 };
1854 let mut left_iter = left_prefix.iter();
1855 let mut right_iter = right_prefix.iter();
1856
1857 let mut left_secondary;
1858 let mut right_secondary;
1859 loop {
1860 loop {
1861 left_secondary = left_iter.next_back().unwrap_or_default().secondary();
1862 if left_secondary != 0 {
1863 break;
1864 }
1865 }
1866 loop {
1867 right_secondary =
1868 right_iter.next_back().unwrap_or_default().secondary();
1869 if right_secondary != 0 {
1870 break;
1871 }
1872 }
1873 if left_secondary != right_secondary {
1874 if left_secondary < right_secondary {
1875 return Ordering::Less;
1876 }
1877 return Ordering::Greater;
1878 }
1879 if left_secondary == NO_CE_SECONDARY {
1880 break;
1881 }
1882 }
1883 }
1884 }
1885 }
1886
1887 if self.options.case_level() {
1888 let mut left_non_primary;
1889 let mut right_non_primary;
1890 let mut left_case;
1891 let mut right_case;
1892 let mut left_iter = left_ces.iter();
1893 let mut right_iter = right_ces.iter();
1894 if self.options.strength() == Strength::Primary {
1895 loop {
1901 loop {
1902 let ce = left_iter.next().unwrap_or_default();
1903 left_non_primary = ce.non_primary();
1904 if !ce.either_half_zero() {
1905 break;
1906 }
1907 }
1908 left_case = left_non_primary.case();
1909 loop {
1910 let ce = right_iter.next().unwrap_or_default();
1911 right_non_primary = ce.non_primary();
1912 if !ce.either_half_zero() {
1913 break;
1914 }
1915 }
1916 right_case = right_non_primary.case();
1917 if left_case != right_case {
1921 if !self.options.upper_first() {
1922 if left_case < right_case {
1923 return Ordering::Less;
1924 }
1925 return Ordering::Greater;
1926 }
1927 if left_case < right_case {
1928 return Ordering::Greater;
1929 }
1930 return Ordering::Less;
1931 }
1932 if left_non_primary.secondary() == NO_CE_SECONDARY {
1933 break;
1934 }
1935 }
1936 } else {
1937 loop {
1952 loop {
1953 left_non_primary = left_iter.next().unwrap_or_default().non_primary();
1954 if left_non_primary.secondary() != 0 {
1955 break;
1956 }
1957 }
1958 left_case = left_non_primary.case();
1959 loop {
1960 right_non_primary = right_iter.next().unwrap_or_default().non_primary();
1961 if right_non_primary.secondary() != 0 {
1962 break;
1963 }
1964 }
1965 right_case = right_non_primary.case();
1966 if left_case != right_case {
1970 if !self.options.upper_first() {
1971 if left_case < right_case {
1972 return Ordering::Less;
1973 }
1974 return Ordering::Greater;
1975 }
1976 if left_case < right_case {
1977 return Ordering::Greater;
1978 }
1979 return Ordering::Less;
1980 }
1981 if left_non_primary.secondary() == NO_CE_SECONDARY {
1982 break;
1983 }
1984 }
1985 }
1986 }
1987
1988 if let Some(tertiary_mask) = self.options.tertiary_mask() {
1989 let mut any_quaternaries = AnyQuaternaryAccumulator::new();
1990 let mut left_iter = left_ces.iter();
1991 let mut right_iter = right_ces.iter();
1992 loop {
1993 let mut left_non_primary;
1994 let mut left_tertiary;
1995 loop {
1996 left_non_primary = left_iter.next().unwrap_or_default().non_primary();
1997 any_quaternaries.accumulate(left_non_primary);
1998 debug_assert!(
1999 left_non_primary.tertiary() != 0 || left_non_primary.case_quaternary() == 0
2000 );
2001 left_tertiary = left_non_primary.tertiary_case_quarternary(tertiary_mask);
2002 if left_tertiary != 0 {
2003 break;
2004 }
2005 }
2006
2007 let mut right_non_primary;
2008 let mut right_tertiary;
2009 loop {
2010 right_non_primary = right_iter.next().unwrap_or_default().non_primary();
2011 any_quaternaries.accumulate(right_non_primary);
2012 debug_assert!(
2013 right_non_primary.tertiary() != 0
2014 || right_non_primary.case_quaternary() == 0
2015 );
2016 right_tertiary = right_non_primary.tertiary_case_quarternary(tertiary_mask);
2017 if right_tertiary != 0 {
2018 break;
2019 }
2020 }
2021
2022 if left_tertiary != right_tertiary {
2023 if self.options.upper_first() {
2024 if left_tertiary > NO_CE_TERTIARY {
2031 if left_non_primary.secondary() != 0 {
2032 left_tertiary ^= 0xC000;
2033 } else {
2034 left_tertiary += 0x4000;
2035 }
2036 }
2037 if right_tertiary > NO_CE_TERTIARY {
2038 if right_non_primary.secondary() != 0 {
2039 right_tertiary ^= 0xC000;
2040 } else {
2041 right_tertiary += 0x4000;
2042 }
2043 }
2044 }
2045 if left_tertiary < right_tertiary {
2046 return Ordering::Less;
2047 }
2048 return Ordering::Greater;
2049 }
2050
2051 if left_tertiary == NO_CE_TERTIARY {
2052 break;
2053 }
2054 }
2055 if !any_variable && !any_quaternaries.has_quaternary() {
2056 return Ordering::Equal;
2057 }
2058 } else {
2059 return Ordering::Equal;
2060 }
2061
2062 if self.options.strength() <= Strength::Tertiary {
2063 return Ordering::Equal;
2064 }
2065
2066 let mut left_iter = left_ces.iter();
2067 let mut right_iter = right_ces.iter();
2068 loop {
2069 let mut left_quaternary;
2070 loop {
2071 let ce = left_iter.next().unwrap_or_default();
2072 if ce.tertiary_ignorable() {
2073 left_quaternary = ce.primary();
2074 } else {
2075 left_quaternary = ce.quaternary();
2076 }
2077 if left_quaternary != 0 {
2078 break;
2079 }
2080 }
2081 let mut right_quaternary;
2082 loop {
2083 let ce = right_iter.next().unwrap_or_default();
2084 if ce.tertiary_ignorable() {
2085 right_quaternary = ce.primary();
2086 } else {
2087 right_quaternary = ce.quaternary();
2088 }
2089 if right_quaternary != 0 {
2090 break;
2091 }
2092 }
2093 if left_quaternary != right_quaternary {
2094 if let Some(reordering) = &self.reordering {
2095 left_quaternary = reordering.reorder(left_quaternary);
2096 right_quaternary = reordering.reorder(right_quaternary);
2097 }
2098 if left_quaternary < right_quaternary {
2099 return Ordering::Less;
2100 }
2101 return Ordering::Greater;
2102 }
2103 if left_quaternary == NO_CE_PRIMARY {
2104 break;
2105 }
2106 }
2107
2108 Ordering::Equal
2109 }
2110
2111 fn sort_key_levels(&self) -> u8 {
2112 #[expect(clippy::indexing_slicing)]
2113 let mut levels = LEVEL_MASKS[self.options.strength() as usize];
2114 if self.options.case_level() {
2115 levels |= CASE_LEVEL_FLAG;
2116 }
2117 levels
2118 }
2119
2120 pub fn write_sort_key_to<S>(&self, s: &str, sink: &mut S) -> Result<S::Output, S::Error>
2169 where
2170 S: CollationKeySink + ?Sized,
2171 S::State: Default,
2172 {
2173 self.write_sort_key_impl(s.chars_with_trie_default_for_ascii(self.norm_trie()), sink)
2174 }
2175
2176 pub fn write_sort_key_utf8_to<S>(&self, s: &[u8], sink: &mut S) -> Result<S::Output, S::Error>
2180 where
2181 S: CollationKeySink + ?Sized,
2182 S::State: Default,
2183 {
2184 self.write_sort_key_impl(s.chars_with_trie_default_for_ascii(self.norm_trie()), sink)
2185 }
2186
2187 pub fn write_sort_key_utf16_to<S>(&self, s: &[u16], sink: &mut S) -> Result<S::Output, S::Error>
2191 where
2192 S: CollationKeySink + ?Sized,
2193 S::State: Default,
2194 {
2195 self.write_sort_key_impl(s.chars_with_trie(self.norm_trie()), sink)
2196 }
2197
2198 fn write_sort_key_impl<I, T, S>(
2199 &'data self,
2200 iter: I,
2201 sink: &mut S,
2202 ) -> Result<S::Output, S::Error>
2203 where
2204 I: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + Clone + 'data,
2205 T: AbstractCodePointTrie<'data, u32> + 'data,
2206 S: CollationKeySink + ?Sized,
2207 S::State: Default,
2208 {
2209 let identical = if self.options.strength() == Strength::Identical {
2210 Some(iter.clone())
2211 } else {
2212 None
2213 };
2214
2215 let mut state = S::State::default();
2216 self.write_sort_key_up_to_quaternary(iter, sink, &mut state)?;
2217
2218 if let Some(iter) = identical {
2219 sink.write_byte(&mut state, LEVEL_SEPARATOR_BYTE)?;
2220
2221 let mut iter = icu_normalizer::new_decomposition(iter, self.tables);
2222 let _ = iter.next(); write_identical_level(iter, sink, &mut state)?;
2224 }
2225
2226 sink.finish(state)
2227 }
2228
2229 fn write_sort_key_up_to_quaternary<I, S, T>(
2234 &'data self,
2235 iter: I,
2236 sink: &mut S,
2237 state: &mut S::State,
2238 ) -> Result<(), S::Error>
2239 where
2240 I: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + Clone + 'data,
2241 T: AbstractCodePointTrie<'data, u32> + 'data,
2242 S: CollationKeySink + ?Sized,
2243 {
2244 let levels = self.sort_key_levels();
2246
2247 let mut iter = collation_elements!(self, iter, self.numeric_primary());
2248 iter.init();
2249 let variable_top = self.variable_top();
2250
2251 let tertiary_mask = self.options.tertiary_mask().unwrap_or_default();
2252
2253 let mut cases = SortKeyLevel::default();
2254 let mut secondaries = SortKeyLevel::default();
2255 let mut tertiaries = SortKeyLevel::default();
2256 let mut quaternaries = SortKeyLevel::default();
2257
2258 let mut prev_reordered_primary = 0;
2259 let mut common_cases = 0usize;
2260 let mut common_secondaries = 0usize;
2261 let mut common_tertiaries = 0usize;
2262 let mut common_quaternaries = 0usize;
2263 let mut prev_secondary = 0;
2264 let mut sec_segment_start = 0;
2265
2266 loop {
2267 let mut ce = iter.next();
2268 let mut p = ce.primary();
2269 if p < variable_top && p > MERGE_SEPARATOR_PRIMARY {
2270 if common_quaternaries != 0 {
2273 common_quaternaries -= 1;
2274 while common_quaternaries >= QUAT_COMMON[WEIGHT_MAX_COUNT] as _ {
2275 quaternaries.append_byte(QUAT_COMMON[WEIGHT_MIDDLE]);
2276 common_quaternaries -= QUAT_COMMON[WEIGHT_MAX_COUNT] as usize;
2277 }
2278 quaternaries.append_byte(QUAT_COMMON[WEIGHT_LOW] + common_quaternaries as u8);
2280 common_quaternaries = 0;
2281 }
2282
2283 loop {
2284 if levels & QUATERNARY_LEVEL_FLAG != 0 {
2285 if let Some(reordering) = &self.reordering {
2286 p = reordering.reorder(p);
2287 }
2288 if (p >> 24) as u8 >= QUAT_SHIFTED_LIMIT_BYTE {
2289 quaternaries.append_byte(QUAT_SHIFTED_LIMIT_BYTE);
2292 }
2293 quaternaries.append_weight_32(p);
2294 }
2295 loop {
2296 ce = iter.next();
2297 p = ce.primary();
2298 if p != 0 {
2299 break;
2300 }
2301 }
2302 if !(p < variable_top && p > MERGE_SEPARATOR_PRIMARY) {
2303 break;
2304 }
2305 }
2306 }
2307
2308 if p > NO_CE_PRIMARY && levels & PRIMARY_LEVEL_FLAG != 0 {
2312 let is_compressible = self.special_primaries.is_compressible((p >> 24) as _);
2314 if let Some(reordering) = &self.reordering {
2315 p = reordering.reorder(p);
2316 }
2317 let p1 = (p >> 24) as u8;
2318 if !is_compressible || p1 != (prev_reordered_primary >> 24) as u8 {
2319 if prev_reordered_primary != 0 {
2320 if p < prev_reordered_primary {
2321 if p1 > MERGE_SEPARATOR_BYTE {
2324 sink.write(state, &[PRIMARY_COMPRESSION_LOW_BYTE])?;
2325 }
2326 } else {
2327 sink.write(state, &[PRIMARY_COMPRESSION_HIGH_BYTE])?;
2328 }
2329 }
2330 sink.write_byte(state, p1)?;
2331 prev_reordered_primary = if is_compressible { p } else { 0 };
2332 }
2333
2334 let p2 = (p >> 16) as u8;
2335 if p2 != 0 {
2336 let (b0, b1, b2) = (p2, (p >> 8) as _, p as _);
2337 sink.write_byte(state, b0)?;
2338 if b1 != 0 {
2339 sink.write_byte(state, b1)?;
2340 if b2 != 0 {
2341 sink.write_byte(state, b2)?;
2342 }
2343 }
2344 }
2345 }
2346
2347 let non_primary = ce.non_primary();
2348 if non_primary.ignorable() {
2349 continue; }
2351
2352 macro_rules! handle_common {
2353 ($key:ident, $w:ident, $common:ident, $weights:ident, $lim:expr) => {
2354 if $common != 0 {
2355 $common -= 1;
2356 while $common >= $weights[WEIGHT_MAX_COUNT] as _ {
2357 $key.append_byte($weights[WEIGHT_MIDDLE]);
2358 $common -= $weights[WEIGHT_MAX_COUNT] as usize;
2359 }
2360 let b = if $w < $lim {
2361 $weights[WEIGHT_LOW] + ($common as u8)
2362 } else {
2363 $weights[WEIGHT_HIGH] - ($common as u8)
2364 };
2365 $key.append_byte(b);
2366 $common = 0;
2367 }
2368 };
2369 ($key:ident, $w:ident, $common:ident, $weights:ident) => {
2370 handle_common!($key, $w, $common, $weights, COMMON_WEIGHT16);
2371 };
2372 }
2373
2374 if levels & SECONDARY_LEVEL_FLAG != 0 {
2375 let s = non_primary.secondary();
2376 if s == 0 {
2377 } else if s == COMMON_WEIGHT16
2379 && (!self.options.backward_second_level() || p != MERGE_SEPARATOR_PRIMARY)
2380 {
2381 common_secondaries += 1;
2384 } else if !self.options.backward_second_level() {
2385 handle_common!(secondaries, s, common_secondaries, SEC_COMMON);
2386 secondaries.append_weight_16(s);
2387 } else {
2388 if common_secondaries != 0 {
2389 common_secondaries -= 1;
2390 let remainder = common_secondaries % SEC_COMMON[WEIGHT_MAX_COUNT] as usize;
2392 let b = if prev_secondary < COMMON_WEIGHT16 {
2393 SEC_COMMON[WEIGHT_LOW] + remainder as u8
2394 } else {
2395 SEC_COMMON[WEIGHT_HIGH] - remainder as u8
2396 };
2397 secondaries.append_byte(b);
2398 common_secondaries -= remainder;
2399 while common_secondaries > 0 {
2401 secondaries.append_byte(SEC_COMMON[WEIGHT_MIDDLE]);
2403 common_secondaries -= SEC_COMMON[WEIGHT_MAX_COUNT] as usize;
2404 }
2405 }
2407 if 0 < p && p <= MERGE_SEPARATOR_PRIMARY {
2408 let secs = &mut secondaries.buf;
2411 let last = secs.len() - 1;
2412 if sec_segment_start < last {
2413 let mut q = sec_segment_start;
2414 let mut r = last;
2415
2416 #[expect(clippy::indexing_slicing)]
2418 while q < r {
2419 let b = secs[q];
2420 secs[q] = secs[r];
2421 q += 1;
2422 secs[r] = b;
2423 r -= 1;
2424 }
2425 }
2426 let b = if p == NO_CE_PRIMARY {
2427 LEVEL_SEPARATOR_BYTE
2428 } else {
2429 MERGE_SEPARATOR_BYTE
2430 };
2431 secondaries.append_byte(b);
2432 prev_secondary = 0;
2433 sec_segment_start = secondaries.len();
2434 } else {
2435 secondaries.append_reverse_weight_16(s);
2436 prev_secondary = s;
2437 }
2438 }
2439 }
2440
2441 if levels & CASE_LEVEL_FLAG != 0 {
2442 if self.options.strength() == Strength::Primary && p == 0
2443 || non_primary.bits() <= 0xffff
2444 {
2445 } else {
2449 let mut c = ((non_primary.bits() >> 8) & 0xff) as u8;
2451 debug_assert_ne!(c & 0xc0, 0xc0);
2452 if c & 0xc0 == 0 && c > LEVEL_SEPARATOR_BYTE {
2453 common_cases += 1;
2454 } else {
2455 if !self.options.upper_first() {
2456 if common_cases != 0 && (c > LEVEL_SEPARATOR_BYTE || !cases.is_empty())
2461 {
2462 common_cases -= 1;
2463 while common_cases >= CASE_LOWER_FIRST_COMMON[WEIGHT_MAX_COUNT] as _
2464 {
2465 cases.append_byte(CASE_LOWER_FIRST_COMMON[WEIGHT_MIDDLE] << 4);
2466 common_cases -=
2467 CASE_LOWER_FIRST_COMMON[WEIGHT_MAX_COUNT] as usize;
2468 }
2469 let b = if c <= LEVEL_SEPARATOR_BYTE {
2470 CASE_LOWER_FIRST_COMMON[WEIGHT_LOW] + common_cases as u8
2471 } else {
2472 CASE_LOWER_FIRST_COMMON[WEIGHT_HIGH] - common_cases as u8
2473 };
2474 cases.append_byte(b << 4);
2475 common_cases = 0;
2476 }
2477 if c > LEVEL_SEPARATOR_BYTE {
2478 c = (CASE_LOWER_FIRST_COMMON[WEIGHT_HIGH] + (c >> 6)) << 4;
2480 }
2481 } else {
2482 if common_cases != 0 {
2487 common_cases -= 1;
2488 while common_cases >= CASE_UPPER_FIRST_COMMON[WEIGHT_MAX_COUNT] as _
2489 {
2490 cases.append_byte(CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] << 4);
2491 common_cases -=
2492 CASE_UPPER_FIRST_COMMON[WEIGHT_MAX_COUNT] as usize;
2493 }
2494 cases.append_byte(
2495 (CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] + common_cases as u8) << 4,
2496 );
2497 common_cases = 0;
2498 }
2499 if c > LEVEL_SEPARATOR_BYTE {
2500 c = (CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] - (c >> 6)) << 4;
2502 }
2503 }
2504 cases.append_byte(c);
2507 }
2508 }
2509 }
2510
2511 if levels & TERTIARY_LEVEL_FLAG != 0 {
2512 let mut t = non_primary.tertiary_case_quarternary(tertiary_mask);
2513 debug_assert_ne!(non_primary.bits() & 0xc000, 0xc000);
2514 if t == COMMON_WEIGHT16 {
2515 common_tertiaries += 1;
2516 } else if tertiary_mask & 0x8000 == 0 {
2517 handle_common!(tertiaries, t, common_tertiaries, TER_ONLY_COMMON);
2520 if t > COMMON_WEIGHT16 {
2521 t += 0xc000;
2522 }
2523 tertiaries.append_weight_16(t);
2524 } else if !self.options.upper_first() {
2525 handle_common!(tertiaries, t, common_tertiaries, TER_LOWER_FIRST_COMMON);
2528 if t > COMMON_WEIGHT16 {
2529 t += 0x4000;
2530 }
2531 tertiaries.append_weight_16(t);
2532 } else {
2533 if t <= NO_CE_TERTIARY {
2546 } else if non_primary.bits() > 0xffff {
2548 t ^= 0xc000;
2550 if t < (TER_UPPER_FIRST_COMMON[WEIGHT_HIGH] as u16) << 8 {
2551 t -= 0x4000;
2552 }
2553 } else {
2554 debug_assert!((0x8600..=0xbfff).contains(&t));
2556 t += 0x4000;
2557 }
2558 handle_common!(
2559 tertiaries,
2560 t,
2561 common_tertiaries,
2562 TER_UPPER_FIRST_COMMON,
2563 (TER_UPPER_FIRST_COMMON[WEIGHT_LOW] as u16) << 8
2564 );
2565 tertiaries.append_weight_16(t);
2566 }
2567 }
2568
2569 if levels & QUATERNARY_LEVEL_FLAG != 0 {
2570 let q = (non_primary.bits() & 0xffff) as u16;
2571 if q & 0xc0 == 0 && q > NO_CE_QUATERNARY {
2572 common_quaternaries += 1;
2573 } else if q == NO_CE_QUATERNARY
2574 && self.options.alternate_handling() == AlternateHandling::NonIgnorable
2575 && quaternaries.is_empty()
2576 {
2577 quaternaries.append_byte(LEVEL_SEPARATOR_BYTE);
2585 } else {
2586 let q = if q == NO_CE_QUATERNARY {
2587 LEVEL_SEPARATOR_BYTE
2588 } else {
2589 (0xfc + ((q >> 6) & 3)) as u8
2590 };
2591 handle_common!(
2592 quaternaries,
2593 q,
2594 common_quaternaries,
2595 QUAT_COMMON,
2596 QUAT_COMMON[WEIGHT_LOW]
2597 );
2598 quaternaries.append_byte(q);
2599 }
2600 }
2601
2602 if (non_primary.bits() >> 24) as u8 == LEVEL_SEPARATOR_BYTE {
2603 break; }
2605 }
2606
2607 macro_rules! write_level {
2608 ($key:ident, $flag:ident) => {
2609 if levels & $flag != 0 {
2610 sink.write(state, &[LEVEL_SEPARATOR_BYTE])?;
2611 sink.write(state, &$key.buf)?;
2612 }
2613 };
2614 }
2615
2616 write_level!(secondaries, SECONDARY_LEVEL_FLAG);
2617
2618 if levels & CASE_LEVEL_FLAG != 0 {
2619 sink.write(state, &[LEVEL_SEPARATOR_BYTE])?;
2620
2621 let mut b = 0;
2623 if let Some((last, head)) = cases.buf.split_last() {
2624 debug_assert_eq!(*last, 1); for c in head {
2626 debug_assert_eq!(*c & 0xf, 0);
2627 debug_assert_ne!(*c, 0);
2628 if b == 0 {
2629 b = *c;
2630 } else {
2631 sink.write_byte(state, b | (*c >> 4))?;
2632 b = 0;
2633 }
2634 }
2635 } else {
2636 debug_assert!(false);
2637 }
2638 if b != 0 {
2639 sink.write_byte(state, b)?;
2640 }
2641 }
2642
2643 write_level!(tertiaries, TERTIARY_LEVEL_FLAG);
2644 write_level!(quaternaries, QUATERNARY_LEVEL_FLAG);
2645
2646 Ok(())
2647 }
2648}
2649
2650#[derive(Debug, PartialEq, Eq)]
2652pub struct TooSmall {
2653 pub length: usize,
2655}
2656
2657impl TooSmall {
2658 pub fn new(length: usize) -> Self {
2659 Self { length }
2660 }
2661}
2662
2663pub trait CollationKeySink {
2677 type Error;
2679
2680 type State;
2682
2683 type Output;
2685
2686 fn write(&mut self, state: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error>;
2688
2689 fn write_byte(&mut self, state: &mut Self::State, b: u8) -> Result<(), Self::Error> {
2691 self.write(state, &[b])
2692 }
2693
2694 fn finish(&mut self, state: Self::State) -> Result<Self::Output, Self::Error>;
2697}
2698
2699impl CollationKeySink for Vec<u8> {
2700 type Error = Infallible;
2701 type State = ();
2702 type Output = ();
2703
2704 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2705 self.extend_from_slice(buf);
2706 Ok(())
2707 }
2708
2709 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2710 Ok(())
2711 }
2712}
2713
2714impl CollationKeySink for VecDeque<u8> {
2715 type Error = Infallible;
2716 type State = ();
2717 type Output = ();
2718
2719 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2720 self.extend(buf.iter());
2721 Ok(())
2722 }
2723
2724 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2725 Ok(())
2726 }
2727}
2728
2729impl<const N: usize> CollationKeySink for SmallVec<[u8; N]> {
2730 type Error = Infallible;
2731 type State = ();
2732 type Output = ();
2733
2734 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2735 self.extend_from_slice(buf);
2736 Ok(())
2737 }
2738
2739 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2740 Ok(())
2741 }
2742}
2743
2744impl CollationKeySink for [u8] {
2745 type Error = TooSmall;
2746 type State = usize;
2747 type Output = usize;
2748
2749 fn write(&mut self, offset: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2750 if *offset + buf.len() <= self.len() {
2751 #[expect(clippy::indexing_slicing)]
2753 self[*offset..*offset + buf.len()].copy_from_slice(buf);
2754 }
2755 *offset += buf.len();
2756 Ok(())
2757 }
2758
2759 fn finish(&mut self, offset: Self::State) -> Result<Self::Output, Self::Error> {
2760 if offset <= self.len() {
2761 Ok(offset)
2762 } else {
2763 Err(TooSmall::new(offset))
2764 }
2765 }
2766}
2767
2768#[derive(Default)]
2769struct SortKeyLevel {
2770 buf: SmallVec<[u8; 40]>,
2771}
2772
2773impl SortKeyLevel {
2774 fn len(&self) -> usize {
2775 self.buf.len()
2776 }
2777
2778 fn is_empty(&self) -> bool {
2779 self.buf.is_empty()
2780 }
2781
2782 fn append_byte(&mut self, x: u8) {
2783 self.buf.push(x);
2784 }
2785
2786 fn append_weight_16(&mut self, w: u16) {
2787 debug_assert_ne!(w, 0);
2788 let b0 = (w >> 8) as u8;
2789 let b1 = w as u8;
2790 self.append_byte(b0);
2791 if b1 != 0 {
2792 self.append_byte(b1);
2793 }
2794 }
2795
2796 fn append_reverse_weight_16(&mut self, w: u16) {
2797 debug_assert_ne!(w, 0);
2798 let b0 = (w >> 8) as u8;
2799 let b1 = w as u8;
2800 if b1 != 0 {
2801 self.append_byte(b1);
2802 }
2803 self.append_byte(b0);
2804 }
2805
2806 fn append_weight_32(&mut self, w: u32) {
2807 debug_assert_ne!(w, 0);
2808 let b0 = (w >> 24) as u8;
2809 let b1 = (w >> 16) as u8;
2810 let b2 = (w >> 8) as u8;
2811 let b3 = w as u8;
2812 self.append_byte(b0);
2813 if b1 != 0 {
2814 self.append_byte(b1);
2815 if b2 != 0 {
2816 self.append_byte(b2);
2817 if b3 != 0 {
2818 self.append_byte(b3);
2819 }
2820 }
2821 }
2822 }
2823}
2824
2825macro_rules! negdivmod {
2831 ($n:ident, $d:ident, $m:ident) => {
2832 $m = $n % $d;
2833 $n /= $d;
2834 if $m < 0 {
2835 $n -= 1;
2836 $m += $d;
2837 }
2838 };
2839}
2840
2841fn write_diff<S>(mut diff: i32, sink: &mut S, state: &mut S::State) -> Result<(), S::Error>
2842where
2843 S: CollationKeySink + ?Sized,
2844{
2845 let mut out = |b| sink.write_byte(state, b);
2846
2847 if diff >= SLOPE_REACH_NEG_1 {
2848 if diff <= SLOPE_REACH_POS_1 {
2849 out((SLOPE_MIDDLE + diff) as _)?;
2850 } else if diff <= SLOPE_REACH_POS_2 {
2851 out((SLOPE_START_POS_2 + (diff / SLOPE_TAIL_COUNT)) as _)?;
2852 out((SLOPE_MIN + diff % SLOPE_TAIL_COUNT) as _)?;
2853 } else if diff <= SLOPE_REACH_POS_3 {
2854 let p2 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2855 diff /= SLOPE_TAIL_COUNT;
2856 let p1 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2857 let p0 = SLOPE_START_POS_3 + (diff / SLOPE_TAIL_COUNT);
2858 out(p0 as _)?;
2859 out(p1 as _)?;
2860 out(p2 as _)?;
2861 } else {
2862 let p3 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2863 diff /= SLOPE_TAIL_COUNT;
2864 let p2 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2865 diff /= SLOPE_TAIL_COUNT;
2866 let p1 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2867 out(SLOPE_MAX as _)?;
2868 out(p1 as _)?;
2869 out(p2 as _)?;
2870 out(p3 as _)?;
2871 }
2872 } else {
2873 let mut m;
2874
2875 if diff >= SLOPE_REACH_NEG_2 {
2876 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2877 out((SLOPE_START_NEG_2 + diff) as _)?;
2878 out((SLOPE_MIN + m) as _)?;
2879 } else if diff >= SLOPE_REACH_NEG_3 {
2880 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2881 let p2 = SLOPE_MIN + m;
2882 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2883 let p1 = SLOPE_MIN + m;
2884 let p0 = SLOPE_START_NEG_3 + diff;
2885 out(p0 as _)?;
2886 out(p1 as _)?;
2887 out(p2 as _)?;
2888 } else {
2889 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2890 let p3 = SLOPE_MIN + m;
2891 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2892 let p2 = SLOPE_MIN + m;
2893 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2894 let p1 = SLOPE_MIN + m;
2895 let _ = diff;
2896 out(SLOPE_MIN as _)?;
2897 out(p1 as _)?;
2898 out(p2 as _)?;
2899 out(p3 as _)?;
2900 }
2901 }
2902
2903 Ok(())
2904}
2905
2906fn write_identical_level<I, S>(iter: I, sink: &mut S, state: &mut S::State) -> Result<(), S::Error>
2907where
2908 I: Iterator<Item = char>,
2909 S: CollationKeySink + ?Sized,
2910{
2911 let mut prev = 0i32;
2912
2913 for c in iter {
2914 if !(0x4e00..=0xa000).contains(&prev) {
2915 prev = (prev & !0x7f) - SLOPE_REACH_NEG_1;
2916 } else {
2917 prev = 0x9fff - SLOPE_REACH_POS_2;
2919 }
2920
2921 if c == MERGE_SEPARATOR {
2922 sink.write_byte(state, MERGE_SEPARATOR_BYTE)?;
2923 prev = 0;
2924 } else {
2925 let c = c as i32;
2926 write_diff(c - prev, sink, state)?;
2927 prev = c;
2928 }
2929 }
2930 Ok(())
2931}
2932
2933#[cfg(test)]
2934mod test {
2935 use super::*;
2936 use icu_locale::locale;
2937
2938 type Key = Vec<u8>;
2939
2940 fn collator_en(strength: Strength) -> CollatorBorrowed<'static> {
2941 let locale = locale!("en").into();
2942 let mut options = CollatorOptions::default();
2943 options.strength = Some(strength);
2944 Collator::try_new(locale, options).unwrap()
2945 }
2946
2947 fn collator_en_case_level(strength: Strength) -> CollatorBorrowed<'static> {
2948 let locale = locale!("en").into();
2949 let mut options = CollatorOptions::default();
2950 options.strength = Some(strength);
2951 options.case_level = Some(crate::options::CaseLevel::On);
2952 Collator::try_new(locale, options).unwrap()
2953 }
2954
2955 fn keys(strength: Strength) -> (Key, Key, Key) {
2956 let collator = collator_en(strength);
2957
2958 let mut k0 = Vec::new();
2959 let Ok(()) = collator.write_sort_key_to("aabc", &mut k0);
2960 let mut k1 = Vec::new();
2961 let Ok(()) = collator.write_sort_key_to("aAbc", &mut k1);
2962 let mut k2 = Vec::new();
2963 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k2);
2964
2965 (k0, k1, k2)
2966 }
2967
2968 #[test]
2969 fn sort_key_primary() {
2970 let (k0, k1, k2) = keys(Strength::Primary);
2971 assert_eq!(k0, k1);
2972 assert_eq!(k1, k2);
2973 }
2974
2975 #[test]
2976 fn sort_key_secondary() {
2977 let (k0, k1, k2) = keys(Strength::Secondary);
2978 assert_eq!(k0, k1);
2979 assert!(k1 < k2);
2980 }
2981
2982 #[test]
2983 fn sort_key_tertiary() {
2984 let (k0, k1, k2) = keys(Strength::Tertiary);
2985 assert!(k0 < k1);
2986 assert!(k1 < k2);
2987 }
2988
2989 fn collator_ja(strength: Strength) -> CollatorBorrowed<'static> {
2990 let locale = locale!("ja").into();
2991 let mut options = CollatorOptions::default();
2992 options.strength = Some(strength);
2993 Collator::try_new(locale, options).unwrap()
2994 }
2995
2996 fn keys_ja_strs(strength: Strength, s0: &str, s1: &str) -> (Key, Key) {
2997 let collator = collator_ja(strength);
2998
2999 let mut k0 = Vec::new();
3000 let Ok(()) = collator.write_sort_key_to(s0, &mut k0);
3001 let mut k1 = Vec::new();
3002 let Ok(()) = collator.write_sort_key_to(s1, &mut k1);
3003
3004 (k0, k1)
3005 }
3006
3007 fn keys_ja(strength: Strength) -> (Key, Key) {
3008 keys_ja_strs(strength, "あ", "ア")
3009 }
3010
3011 #[test]
3012 fn sort_keys_ja_to_quaternary() {
3013 let (k0, k1) = keys_ja(Strength::Primary);
3014 assert_eq!(k0, k1);
3015 let (k0, k1) = keys_ja(Strength::Secondary);
3016 assert_eq!(k0, k1);
3017 let (k0, k1) = keys_ja(Strength::Tertiary);
3018 assert_eq!(k0, k1);
3019 let (k0, k1) = keys_ja(Strength::Quaternary);
3020 assert!(k0 < k1);
3021 }
3022
3023 #[test]
3024 fn sort_keys_ja_identical() {
3025 let (k0, k1) = keys_ja_strs(Strength::Quaternary, "ア", "ア");
3026 assert_eq!(k0, k1);
3027 let (k0, k1) = keys_ja_strs(Strength::Identical, "ア", "ア");
3028 assert!(k0 < k1);
3029 }
3030
3031 #[test]
3032 fn sort_keys_utf16() {
3033 let collator = collator_en(Strength::Identical);
3034
3035 const STR8: &[u8] = b"hello world!";
3036 let mut k8 = Vec::new();
3037 let Ok(()) = collator.write_sort_key_utf8_to(STR8, &mut k8);
3038
3039 const STR16: &[u16] = &[
3040 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
3041 ];
3042 let mut k16 = Vec::new();
3043 let Ok(()) = collator.write_sort_key_utf16_to(STR16, &mut k16);
3044 assert_eq!(k8, k16);
3045 }
3046
3047 #[test]
3048 fn sort_keys_invalid() {
3049 let collator = collator_en(Strength::Identical);
3050
3051 let mut k = Vec::new();
3053 let Ok(()) = collator.write_sort_key_utf8_to(b"\xf0\x90", &mut k);
3054 let mut k = Vec::new();
3055 let Ok(()) = collator.write_sort_key_utf16_to(&[0xdd1e], &mut k);
3056 }
3057
3058 #[test]
3059 fn sort_key_to_vecdeque() {
3060 let collator = collator_en(Strength::Identical);
3061
3062 let mut k0 = Vec::new();
3063 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k0);
3064 let mut k1 = VecDeque::new();
3065 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k1);
3066 assert!(k0.iter().eq(k1.iter()));
3067 }
3068
3069 #[test]
3070 fn sort_key_to_slice() {
3071 let collator = collator_en(Strength::Identical);
3072
3073 let mut k0 = Vec::new();
3074 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k0);
3075 let mut k1 = [0u8; 100];
3076 let len = collator.write_sort_key_to("áAbc", &mut k1[..]).unwrap();
3077 assert_eq!(len, k0.len());
3078 assert!(k0.iter().eq(k1[..len].iter()));
3079 }
3080
3081 #[test]
3082 fn sort_key_to_slice_no_space() {
3083 let collator = collator_en(Strength::Identical);
3084 let mut k = [0u8; 0];
3085 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3086 assert!(matches!(res, Err(TooSmall { .. })));
3087 }
3088
3089 #[test]
3090 fn sort_key_to_slice_too_long() {
3091 let collator = collator_en(Strength::Identical);
3093 let mut k = [0u8; 5];
3094 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3095 assert!(matches!(res, Err(TooSmall { .. })));
3096 }
3097
3098 #[test]
3099 fn sort_key_to_slice_identical_too_long() {
3100 let collator = collator_en(Strength::Identical);
3102 let mut k = [0u8; 22];
3103 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3104 assert!(matches!(res, Err(TooSmall { .. })));
3105 }
3106
3107 #[test]
3108 fn sort_key_just_right() {
3109 let collator = collator_en(Strength::Identical);
3111 let mut k = [0u8; 0];
3112 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3113 let len = res.unwrap_err().length;
3114
3115 let mut k = vec![0u8; len - 1];
3117 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3118 let len = res.unwrap_err().length;
3119
3120 let mut k = vec![0u8; len];
3122 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3123 assert_eq!(res, Ok(len));
3124 }
3125
3126 #[test]
3127 fn sort_key_utf16_slice_too_small() {
3128 let collator = collator_en(Strength::Identical);
3129 const STR16: &[u16] = &[0x68, 0x65, 0x6c, 0x6c, 0x6f];
3130 let mut k = [0u8; 4];
3131 let res = collator.write_sort_key_utf16_to(STR16, &mut k[..]);
3132 assert!(matches!(res, Err(TooSmall { .. })));
3133 }
3134
3135 #[test]
3136 fn sort_key_very_long() {
3137 let collator = collator_en(Strength::Secondary);
3138 let mut k = Vec::new();
3139 let Ok(()) = collator.write_sort_key_to(&"a".repeat(300), &mut k);
3140 }
3141
3142 #[test]
3143 fn sort_key_case_level() {
3144 let collator = collator_en_case_level(Strength::Tertiary);
3145 let mut k = Vec::new();
3146 let Ok(()) = collator.write_sort_key_to("aBc", &mut k);
3147 }
3148
3149 #[test]
3150 fn sort_key_case_level_empty() {
3151 let collator = collator_en_case_level(Strength::Tertiary);
3152 let mut k = Vec::new();
3153 let Ok(()) = collator.write_sort_key_to("", &mut k);
3154 }
3155
3156 fn check_sort_key_less(a: &[u16], b: &[u16]) {
3157 let collator = collator_en(Strength::Identical);
3158 let mut ak = Vec::new();
3159 let Ok(()) = collator.write_sort_key_utf16_to(a, &mut ak);
3160 let mut bk = Vec::new();
3161 let Ok(()) = collator.write_sort_key_utf16_to(b, &mut bk);
3162 assert!(ak < bk, "failed: {a:04x?} - {b:04x?}");
3163 }
3164
3165 #[test]
3166 fn sort_key_fffe_bug_6811() {
3167 check_sort_key_less(
3168 &[0xfffe, 0x0001, 0x0002, 0x0003],
3169 &[0x0001, 0xfffe, 0x0002, 0x0003],
3170 );
3171 check_sort_key_less(
3172 &[0x0001, 0xfffe, 0x0002, 0x0003],
3173 &[0x0001, 0x0002, 0xfffe, 0x0003],
3174 );
3175 check_sort_key_less(
3176 &[0x0001, 0x0002, 0xfffe, 0x0003],
3177 &[0x0001, 0x0002, 0x0003, 0xfffe],
3178 );
3179 check_sort_key_less(&[0xfffe, 0x0000, 0x0000], &[0x0000, 0xfffe, 0x0000]);
3180 check_sort_key_less(&[0x0000, 0xfffe, 0x0000], &[0x0000, 0x0000, 0xfffe]);
3181 }
3182}