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 let mut left_primary = left_ce32.to_primary_in_quick_check(self.tailoring);
1014 let mut right_primary = right_ce32.to_primary_in_quick_check(self.tailoring);
1017 quick_primary_compare!(left_primary, right_primary, variable_top, self,);
1018 let right_hangul_offset = u32::from(right_c).wrapping_sub(HANGUL_S_BASE);
1020 if right_hangul_offset < HANGUL_S_COUNT {
1021 let left_hangul_offset = u32::from(left_c).wrapping_sub(HANGUL_S_BASE);
1022 if left_hangul_offset < HANGUL_S_COUNT {
1023 hangul_syllable_compare!(left_hangul_offset, right_hangul_offset,);
1024 }
1025 }
1026 }
1027 }
1028 },
1031 variable_top,
1032 );
1033
1034 compare!(
1035 ,
1039 compare_utf8,
1040 [u8],
1041 [u8],
1042 split_prefix_u8,
1043 chars_with_trie_default_for_ascii,
1044 chars_with_trie_default_for_ascii,
1045 self,
1046 left_tail,
1047 right_tail,
1048 {
1049 let tailoring_trie = &self.tailoring.trie;
1051 if let Some((left_c, left_u32)) = left_tail.chars_with_trie(tailoring_trie).next() {
1052 if let Some((right_c, right_u32)) = right_tail.chars_with_trie(tailoring_trie).next() {
1056 let left_ce32 = CollationElement32::new(left_u32);
1057 let right_ce32 = CollationElement32::new(right_u32);
1058 let mut left_primary = left_ce32.to_primary_in_quick_check(self.tailoring);
1059 let mut right_primary = right_ce32.to_primary_in_quick_check(self.tailoring);
1062 quick_primary_compare!(left_primary, right_primary, variable_top, self,);
1063 let right_hangul_offset = u32::from(right_c).wrapping_sub(HANGUL_S_BASE);
1065 if right_hangul_offset < HANGUL_S_COUNT {
1066 let left_hangul_offset = u32::from(left_c).wrapping_sub(HANGUL_S_BASE);
1067 if left_hangul_offset < HANGUL_S_COUNT {
1068 hangul_syllable_compare!(left_hangul_offset, right_hangul_offset,);
1069 }
1070 }
1071 }
1072 }
1073 },
1076 variable_top,
1077 );
1078
1079 compare!(
1080 ,
1083 compare_utf16,
1084 [u16],
1085 [u16],
1086 split_prefix_u16,
1087 chars_with_trie,
1088 chars_with_trie,
1089 self,
1090 left_tail,
1091 right_tail,
1092 {
1093 let tailoring_trie = &self.tailoring.trie;
1094 if let Some(left_u) = left_tail.first() {
1095 if let Some(right_u) = right_tail.first() {
1096 let left_u16 = *left_u;
1097 let right_u16 = *right_u;
1098 let left_u32 = tailoring_trie.get16(left_u16);
1099 let right_u32 = tailoring_trie.get16(right_u16);
1100 let left_ce32 = CollationElement32::new(left_u32);
1101 let right_ce32 = CollationElement32::new(right_u32);
1102 let mut left_primary = left_ce32.to_primary_in_quick_check(self.tailoring);
1103 let mut right_primary = right_ce32.to_primary_in_quick_check(self.tailoring);
1106 quick_primary_compare!(left_primary, right_primary, variable_top, self,);
1107 let left_hangul_offset = u32::from(left_u16).wrapping_sub(HANGUL_S_BASE);
1109 if left_hangul_offset < HANGUL_S_COUNT {
1110 let right_hangul_offset = u32::from(right_u16).wrapping_sub(HANGUL_S_BASE);
1111 if right_hangul_offset < HANGUL_S_COUNT {
1112 hangul_syllable_compare!(left_hangul_offset, right_hangul_offset,);
1113 }
1114 }
1115 }
1116 }
1117 },
1120 variable_top,
1121 );
1122
1123 compare!(
1124 #[cfg(feature = "latin1")]
1128 ,
1129 compare_latin1,
1130 [u8],
1131 [u8],
1132 split_prefix_latin1,
1133 latin1_chars_with_trie,
1134 latin1_chars_with_trie,
1135 self,
1136 left_tail,
1137 right_tail,
1138 {
1139 let tailoring_trie = &self.tailoring.trie;
1140 if let Some(left_u) = left_tail.first() {
1141 if let Some(right_u) = right_tail.first() {
1142 let left_u8 = *left_u;
1143 let right_u8 = *right_u;
1144 if left_u8 < 0x80 {
1150 let left_u32 = unsafe { tailoring_trie.get7(left_u8) };
1152 let left_ce32 = CollationElement32::new(left_u32);
1153 if right_u8 < 0x80 {
1155 let right_u32 = unsafe { tailoring_trie.get7(right_u8) };
1157 let right_ce32 = CollationElement32::new(right_u32);
1158 let left_primary = left_ce32.to_primary_simple();
1161 let right_primary = right_ce32.to_primary_simple();
1164 if (left_primary != right_primary)
1165 && (left_primary != 0)
1166 && (right_primary != 0)
1167 && !(left_primary < variable_top && left_primary > MERGE_SEPARATOR_PRIMARY)
1168 && !(right_primary < variable_top && right_primary > MERGE_SEPARATOR_PRIMARY)
1169 {
1170 if left_primary < right_primary {
1171 return Ordering::Less;
1172 }
1173 return Ordering::Greater;
1174 }
1175 }
1176 }
1177 }
1178 }
1179 },
1182 variable_top,
1183 );
1184
1185 compare!(
1186 #[cfg(feature = "latin1")]
1195 ,
1196 compare_latin1_utf16,
1197 [u8],
1198 [u16],
1199 split_prefix_latin1_utf16,
1200 latin1_chars_with_trie,
1201 chars_with_trie,
1202 self,
1203 left_tail,
1204 right_tail,
1205 {
1206 let tailoring_trie = &self.tailoring.trie;
1207 if let Some(left_u) = left_tail.first() {
1208 if let Some(right_u) = right_tail.first() {
1209 let left_u8 = *left_u;
1210 if left_u8 < 0x80 {
1216 let left_u32 = unsafe { tailoring_trie.get7(left_u8) };
1218 let left_ce32 = CollationElement32::new(left_u32);
1219 let right_u16 = *right_u;
1220 let right_u32 = tailoring_trie.get16(right_u16);
1221 let right_ce32 = CollationElement32::new(right_u32);
1222 let mut left_primary = left_ce32.to_primary_simple();
1223 let mut right_primary = right_ce32.to_primary_in_quick_check(self.tailoring);
1230 if (left_primary != right_primary)
1231 && (left_primary != 0)
1232 && (right_primary != 0)
1233 && !(left_primary < variable_top && left_primary > MERGE_SEPARATOR_PRIMARY)
1234 && !(right_primary < variable_top && right_primary > MERGE_SEPARATOR_PRIMARY)
1235 {
1236 if let Some(reordering) = &self.reordering {
1237 left_primary = reordering.reorder(left_primary);
1238 right_primary = reordering.reorder(right_primary);
1239 }
1240 if left_primary < right_primary {
1241 return Ordering::Less;
1242 }
1243 return Ordering::Greater;
1244 }
1245 }
1246 }
1247 }
1248 },
1251 variable_top,
1252 );
1253
1254 #[inline(always)]
1255 fn numeric_primary(&self) -> Option<u8> {
1256 if self.options.numeric() {
1257 Some(self.special_primaries.numeric_primary)
1258 } else {
1259 None
1260 }
1261 }
1262
1263 #[inline(always)]
1264 fn variable_top(&self) -> u32 {
1265 if self.options.alternate_handling() == AlternateHandling::NonIgnorable {
1266 0
1267 } else {
1268 self.special_primaries
1270 .last_primary_for_group(self.options.max_variable())
1271 + 1
1272 }
1273 }
1274
1275 fn compare_impl<L, R, H, T>(
1281 &'data self,
1282 left_chars: L,
1283 right_chars: R,
1284 mut head_chars: H,
1285 variable_top: u32,
1286 ) -> Ordering
1287 where
1288 L: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + 'data,
1289 R: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + 'data,
1290 H: DoubleEndedIterator<Item = (char, u32)> + 'data,
1291 T: AbstractCodePointTrie<'data, u32> + 'data,
1292 {
1293 let mut left_ces: SmallVec<[CollationElement; CE_BUFFER_SIZE]> = SmallVec::new();
1308 let mut right_ces: SmallVec<[CollationElement; CE_BUFFER_SIZE]> = SmallVec::new();
1309
1310 let mut any_variable = false;
1313
1314 let numeric_primary = self.numeric_primary();
1315
1316 let mut left = collation_elements!(self, left_chars, numeric_primary);
1317 let mut right = collation_elements!(self, right_chars, numeric_primary);
1318
1319 #[expect(clippy::never_loop)]
1340 'prefix: loop {
1341 if let Some((mut head_last_c, head_last_trie_val)) = head_chars.next_back() {
1342 let mut head_last = CharacterAndClassAndTrieValue::new_with_trie_val(
1343 head_last_c,
1344 head_last_trie_val,
1345 );
1346 let mut head_last_ce32 = CollationElement32::default();
1347 let mut head_last_ok = false;
1348 if let Some(left_different) = left.iter_next_before_init() {
1349 left.prepend_upcoming_before_init(left_different.clone());
1350 if let Some(right_different) = right.iter_next_before_init() {
1351 right.prepend_upcoming_before_init(right_different.clone());
1353
1354 #[expect(clippy::never_loop)]
1412 loop {
1413 let decomposition = head_last.trie_val;
1416 if (decomposition
1417 & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER))
1418 == 0
1419 {
1420 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1424 && ((decomposition & LOW_ZEROS_MASK) != 0)
1425 {
1426 head_last_c = char_from_u32(decomposition & 0x7FFF);
1429 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1430 head_last_ce32 = IDENTICAL_PREFIX_HANGUL_MARKER_CE32;
1431 } else {
1432 break;
1433 }
1434 head_last_ok = true;
1435
1436 let left_c;
1437 let right_c;
1438
1439 let decomposition = left_different.trie_val;
1440 if (decomposition
1441 & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER))
1442 == 0
1443 {
1444 left_c = left_different.character();
1445 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1446 && ((decomposition & LOW_ZEROS_MASK) != 0)
1447 {
1448 left_c = char_from_u32(decomposition & 0x7FFF);
1451 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1452 left_c = left_different.character();
1453 if right_different.trie_val == HANGUL_SYLLABLE_MARKER {
1454 let left_hangul_offset =
1457 u32::from(left_c).wrapping_sub(HANGUL_S_BASE);
1458 let right_hangul_offset =
1459 u32::from(right_different.character())
1460 .wrapping_sub(HANGUL_S_BASE);
1461 debug_assert!(left_hangul_offset < HANGUL_S_COUNT);
1463 debug_assert!(right_hangul_offset < HANGUL_S_COUNT);
1464 hangul_syllable_compare!(
1465 left_hangul_offset,
1466 right_hangul_offset,
1467 );
1468 break 'prefix;
1472 }
1473 } else {
1474 break;
1475 }
1476
1477 let decomposition = right_different.trie_val;
1478 if (decomposition
1479 & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER))
1480 == 0
1481 {
1482 right_c = right_different.character();
1483 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1484 && ((decomposition & LOW_ZEROS_MASK) != 0)
1485 {
1486 right_c = char_from_u32(decomposition & 0x7FFF);
1489 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1490 right_c = right_different.character();
1491 } else {
1492 break;
1493 }
1494
1495 if head_last_ce32 == CollationElement32::default() {
1499 head_last_ce32 = self.tailoring.ce32_for_char(head_last_c);
1500 if head_last_ce32 == FALLBACK_CE32 {
1501 head_last_ce32 = self.root.ce32_for_char(head_last_c);
1502 }
1503 if head_last_ce32.tag_checked() == Some(Tag::Contraction)
1504 && head_last_ce32.at_least_one_suffix_contains_starter()
1505 {
1506 break;
1507 }
1508 }
1509 let mut left_data = self.tailoring;
1510 let mut left_ce32 = self.tailoring.ce32_for_char(left_c);
1511 if left_ce32 == FALLBACK_CE32 {
1512 left_ce32 = self.root.ce32_for_char(left_c);
1513 left_data = self.root;
1514 }
1515 let mut right_data = self.tailoring;
1516 let mut right_ce32 = self.tailoring.ce32_for_char(right_c);
1517 if right_ce32 == FALLBACK_CE32 {
1518 right_ce32 = self.root.ce32_for_char(right_c);
1519 right_data = self.root;
1520 }
1521
1522 let mut left_primary = left_ce32.to_primary_in_quick_check_numeric(
1528 left_data,
1529 numeric_primary.is_some(),
1530 );
1531 let mut right_primary = right_ce32.to_primary_in_quick_check_numeric(
1535 right_data,
1536 numeric_primary.is_some(),
1537 );
1538 quick_primary_compare!(left_primary, right_primary, variable_top, self,);
1539
1540 if left_ce32.tag_checked() == Some(Tag::Prefix)
1541 || right_ce32.tag_checked() == Some(Tag::Prefix)
1542 {
1543 break;
1547 }
1548
1549 if numeric_primary.is_some()
1550 && head_last_ce32.tag_checked() == Some(Tag::Digit)
1551 && ((left_ce32.tag_checked() == Some(Tag::Digit)
1552 && left_ce32.digit() == 0)
1553 || (right_ce32.tag_checked() == Some(Tag::Digit)
1554 && right_ce32.digit() == 0)
1555 || ((left_ce32.tag_checked() == Some(Tag::Digit))
1556 ^ (right_ce32.tag_checked() == Some(Tag::Digit))))
1557 {
1558 break;
1562 }
1563
1564 break 'prefix;
1569 }
1570 }
1571 }
1572 let mut tail_first_c;
1573 let mut tail_first_ce32;
1574 let mut tail_first_ok;
1575 loop {
1576 left.prepend_upcoming_before_init(head_last.clone());
1578 right.prepend_upcoming_before_init(head_last.clone());
1579
1580 tail_first_c = head_last_c;
1581 tail_first_ce32 = head_last_ce32;
1582 tail_first_ok = head_last_ok;
1583
1584 let Some((head_last_c_new, decomposition)) = head_chars.next_back() else {
1585 break 'prefix;
1588 };
1589 head_last_c = head_last_c_new;
1590 head_last = CharacterAndClassAndTrieValue::new_with_trie_val(
1591 head_last_c,
1592 decomposition,
1593 );
1594 head_last_ce32 = CollationElement32::default();
1595 head_last_ok = false;
1596
1597 if (decomposition & !(BACKWARD_COMBINING_MARKER | NON_ROUND_TRIP_MARKER)) == 0 {
1598 } else if ((decomposition & HIGH_ZEROS_MASK) != 0)
1602 && ((decomposition & LOW_ZEROS_MASK) != 0)
1603 {
1604 head_last_c = char_from_u32(decomposition & 0x7FFF);
1607 } else if decomposition == HANGUL_SYLLABLE_MARKER {
1608 head_last_ce32 = FFFD_CE32;
1609 } else {
1610 continue;
1611 }
1612 head_last_ok = true;
1613 if !tail_first_ok {
1614 continue;
1615 }
1616 if head_last_ce32 == CollationElement32::default() {
1619 head_last_ce32 = self.tailoring.ce32_for_char(head_last_c);
1620 if head_last_ce32 == FALLBACK_CE32 {
1621 head_last_ce32 = self.root.ce32_for_char(head_last_c);
1622 }
1623 if head_last_ce32.tag_checked() == Some(Tag::Contraction)
1624 && head_last_ce32.at_least_one_suffix_contains_starter()
1625 {
1626 continue;
1627 }
1628 }
1629 if tail_first_ce32 == CollationElement32::default() {
1633 tail_first_ce32 = self.tailoring.ce32_for_char(tail_first_c);
1634 if tail_first_ce32 == FALLBACK_CE32 {
1635 tail_first_ce32 = self.root.ce32_for_char(tail_first_c);
1636 }
1637 } if tail_first_ce32.tag_checked() == Some(Tag::Prefix) {
1639 continue;
1640 }
1641 if numeric_primary.is_some()
1642 && head_last_ce32.tag_checked() == Some(Tag::Digit)
1643 && tail_first_ce32.tag_checked() == Some(Tag::Digit)
1644 && tail_first_ce32.digit() == 0
1645 {
1646 continue;
1648 }
1649 break 'prefix;
1651 }
1652 } else {
1653 break 'prefix;
1655 }
1656 }
1658
1659 left.init();
1662 right.init();
1663
1664 loop {
1665 let mut left_primary;
1666 'left_primary_loop: loop {
1667 let ce = left.next();
1668 left_primary = ce.primary();
1669 if !(left_primary < variable_top && left_primary > MERGE_SEPARATOR_PRIMARY) {
1672 left_ces.push(ce);
1673 } else {
1674 any_variable = true;
1677 left_ces.push(ce.clone_with_non_primary_zeroed());
1682 loop {
1683 let ce = left.next();
1685 left_primary = ce.primary();
1686 if left_primary != 0
1687 && !(left_primary < variable_top
1688 && left_primary > MERGE_SEPARATOR_PRIMARY)
1689 {
1690 left_ces.push(ce);
1692 break 'left_primary_loop;
1693 }
1694 left_ces.push(ce.clone_with_non_primary_zeroed());
1697 }
1698 }
1699 if left_primary != 0 {
1700 break;
1701 }
1702 }
1703 let mut right_primary;
1704 'right_primary_loop: loop {
1705 let ce = right.next();
1706 right_primary = ce.primary();
1707 if !(right_primary < variable_top && right_primary > MERGE_SEPARATOR_PRIMARY) {
1710 right_ces.push(ce);
1711 } else {
1712 any_variable = true;
1715 right_ces.push(ce.clone_with_non_primary_zeroed());
1720 loop {
1721 let ce = right.next();
1723 right_primary = ce.primary();
1724 if right_primary != 0
1725 && !(right_primary < variable_top
1726 && right_primary > MERGE_SEPARATOR_PRIMARY)
1727 {
1728 right_ces.push(ce);
1730 break 'right_primary_loop;
1731 }
1732 right_ces.push(ce.clone_with_non_primary_zeroed());
1735 }
1736 }
1737 if right_primary != 0 {
1738 break;
1739 }
1740 }
1741 if left_primary != right_primary {
1742 if let Some(reordering) = &self.reordering {
1743 left_primary = reordering.reorder(left_primary);
1744 right_primary = reordering.reorder(right_primary);
1745 }
1746 if left_primary < right_primary {
1747 return Ordering::Less;
1748 }
1749 return Ordering::Greater;
1750 }
1751 if left_primary == NO_CE_PRIMARY {
1752 break;
1753 }
1754 }
1755
1756 debug_assert_eq!(left_ces.last(), Some(&NO_CE));
1760 debug_assert_eq!(right_ces.last(), Some(&NO_CE));
1761
1762 if self.options.strength() >= Strength::Secondary {
1774 if !self.options.backward_second_level() {
1775 let mut left_iter = left_ces.iter();
1776 let mut right_iter = right_ces.iter();
1777 let mut left_secondary;
1778 let mut right_secondary;
1779 loop {
1780 loop {
1781 left_secondary = left_iter.next().unwrap_or_default().secondary();
1782 if left_secondary != 0 {
1783 break;
1784 }
1785 }
1786 loop {
1787 right_secondary = right_iter.next().unwrap_or_default().secondary();
1788 if right_secondary != 0 {
1789 break;
1790 }
1791 }
1792 if left_secondary != right_secondary {
1793 if left_secondary < right_secondary {
1794 return Ordering::Less;
1795 }
1796 return Ordering::Greater;
1797 }
1798 if left_secondary == NO_CE_SECONDARY {
1799 break;
1800 }
1801 }
1802 } else {
1803 let mut left_remaining = &left_ces[..];
1804 let mut right_remaining = &right_ces[..];
1805 loop {
1806 if left_remaining.is_empty() {
1807 debug_assert!(right_remaining.is_empty());
1808 break;
1809 }
1810 let (left_prefix, right_prefix) = {
1811 let mut left_iter = left_remaining.iter();
1812 loop {
1813 let left_primary = left_iter.next().unwrap_or_default().primary();
1814 if left_primary != 0 && left_primary <= MERGE_SEPARATOR_PRIMARY {
1815 break;
1816 }
1817 debug_assert_ne!(left_primary, NO_CE_PRIMARY);
1818 }
1819 let left_new_remaining = left_iter.as_slice();
1820 #[expect(clippy::indexing_slicing)]
1822 let left_prefix =
1823 &left_remaining[..left_remaining.len() - 1 - left_new_remaining.len()];
1824 left_remaining = left_new_remaining;
1825
1826 let mut right_iter = right_remaining.iter();
1827 loop {
1828 let right_primary = right_iter.next().unwrap_or_default().primary();
1829 if right_primary != 0 && right_primary <= MERGE_SEPARATOR_PRIMARY {
1830 break;
1831 }
1832 debug_assert_ne!(right_primary, NO_CE_PRIMARY);
1833 }
1834 let right_new_remaining = right_iter.as_slice();
1835 #[expect(clippy::indexing_slicing)]
1837 let right_prefix = &right_remaining
1838 [..right_remaining.len() - 1 - right_new_remaining.len()];
1839 right_remaining = right_new_remaining;
1840
1841 (left_prefix, right_prefix)
1842 };
1843 let mut left_iter = left_prefix.iter();
1844 let mut right_iter = right_prefix.iter();
1845
1846 let mut left_secondary;
1847 let mut right_secondary;
1848 loop {
1849 loop {
1850 left_secondary = left_iter.next_back().unwrap_or_default().secondary();
1851 if left_secondary != 0 {
1852 break;
1853 }
1854 }
1855 loop {
1856 right_secondary =
1857 right_iter.next_back().unwrap_or_default().secondary();
1858 if right_secondary != 0 {
1859 break;
1860 }
1861 }
1862 if left_secondary != right_secondary {
1863 if left_secondary < right_secondary {
1864 return Ordering::Less;
1865 }
1866 return Ordering::Greater;
1867 }
1868 if left_secondary == NO_CE_SECONDARY {
1869 break;
1870 }
1871 }
1872 }
1873 }
1874 }
1875
1876 if self.options.case_level() {
1877 let mut left_non_primary;
1878 let mut right_non_primary;
1879 let mut left_case;
1880 let mut right_case;
1881 let mut left_iter = left_ces.iter();
1882 let mut right_iter = right_ces.iter();
1883 if self.options.strength() == Strength::Primary {
1884 loop {
1890 loop {
1891 let ce = left_iter.next().unwrap_or_default();
1892 left_non_primary = ce.non_primary();
1893 if !ce.either_half_zero() {
1894 break;
1895 }
1896 }
1897 left_case = left_non_primary.case();
1898 loop {
1899 let ce = right_iter.next().unwrap_or_default();
1900 right_non_primary = ce.non_primary();
1901 if !ce.either_half_zero() {
1902 break;
1903 }
1904 }
1905 right_case = right_non_primary.case();
1906 if left_case != right_case {
1910 if !self.options.upper_first() {
1911 if left_case < right_case {
1912 return Ordering::Less;
1913 }
1914 return Ordering::Greater;
1915 }
1916 if left_case < right_case {
1917 return Ordering::Greater;
1918 }
1919 return Ordering::Less;
1920 }
1921 if left_non_primary.secondary() == NO_CE_SECONDARY {
1922 break;
1923 }
1924 }
1925 } else {
1926 loop {
1941 loop {
1942 left_non_primary = left_iter.next().unwrap_or_default().non_primary();
1943 if left_non_primary.secondary() != 0 {
1944 break;
1945 }
1946 }
1947 left_case = left_non_primary.case();
1948 loop {
1949 right_non_primary = right_iter.next().unwrap_or_default().non_primary();
1950 if right_non_primary.secondary() != 0 {
1951 break;
1952 }
1953 }
1954 right_case = right_non_primary.case();
1955 if left_case != right_case {
1959 if !self.options.upper_first() {
1960 if left_case < right_case {
1961 return Ordering::Less;
1962 }
1963 return Ordering::Greater;
1964 }
1965 if left_case < right_case {
1966 return Ordering::Greater;
1967 }
1968 return Ordering::Less;
1969 }
1970 if left_non_primary.secondary() == NO_CE_SECONDARY {
1971 break;
1972 }
1973 }
1974 }
1975 }
1976
1977 if let Some(tertiary_mask) = self.options.tertiary_mask() {
1978 let mut any_quaternaries = AnyQuaternaryAccumulator::new();
1979 let mut left_iter = left_ces.iter();
1980 let mut right_iter = right_ces.iter();
1981 loop {
1982 let mut left_non_primary;
1983 let mut left_tertiary;
1984 loop {
1985 left_non_primary = left_iter.next().unwrap_or_default().non_primary();
1986 any_quaternaries.accumulate(left_non_primary);
1987 debug_assert!(
1988 left_non_primary.tertiary() != 0 || left_non_primary.case_quaternary() == 0
1989 );
1990 left_tertiary = left_non_primary.tertiary_case_quarternary(tertiary_mask);
1991 if left_tertiary != 0 {
1992 break;
1993 }
1994 }
1995
1996 let mut right_non_primary;
1997 let mut right_tertiary;
1998 loop {
1999 right_non_primary = right_iter.next().unwrap_or_default().non_primary();
2000 any_quaternaries.accumulate(right_non_primary);
2001 debug_assert!(
2002 right_non_primary.tertiary() != 0
2003 || right_non_primary.case_quaternary() == 0
2004 );
2005 right_tertiary = right_non_primary.tertiary_case_quarternary(tertiary_mask);
2006 if right_tertiary != 0 {
2007 break;
2008 }
2009 }
2010
2011 if left_tertiary != right_tertiary {
2012 if self.options.upper_first() {
2013 if left_tertiary > NO_CE_TERTIARY {
2020 if left_non_primary.secondary() != 0 {
2021 left_tertiary ^= 0xC000;
2022 } else {
2023 left_tertiary += 0x4000;
2024 }
2025 }
2026 if right_tertiary > NO_CE_TERTIARY {
2027 if right_non_primary.secondary() != 0 {
2028 right_tertiary ^= 0xC000;
2029 } else {
2030 right_tertiary += 0x4000;
2031 }
2032 }
2033 }
2034 if left_tertiary < right_tertiary {
2035 return Ordering::Less;
2036 }
2037 return Ordering::Greater;
2038 }
2039
2040 if left_tertiary == NO_CE_TERTIARY {
2041 break;
2042 }
2043 }
2044 if !any_variable && !any_quaternaries.has_quaternary() {
2045 return Ordering::Equal;
2046 }
2047 } else {
2048 return Ordering::Equal;
2049 }
2050
2051 if self.options.strength() <= Strength::Tertiary {
2052 return Ordering::Equal;
2053 }
2054
2055 let mut left_iter = left_ces.iter();
2056 let mut right_iter = right_ces.iter();
2057 loop {
2058 let mut left_quaternary;
2059 loop {
2060 let ce = left_iter.next().unwrap_or_default();
2061 if ce.tertiary_ignorable() {
2062 left_quaternary = ce.primary();
2063 } else {
2064 left_quaternary = ce.quaternary();
2065 }
2066 if left_quaternary != 0 {
2067 break;
2068 }
2069 }
2070 let mut right_quaternary;
2071 loop {
2072 let ce = right_iter.next().unwrap_or_default();
2073 if ce.tertiary_ignorable() {
2074 right_quaternary = ce.primary();
2075 } else {
2076 right_quaternary = ce.quaternary();
2077 }
2078 if right_quaternary != 0 {
2079 break;
2080 }
2081 }
2082 if left_quaternary != right_quaternary {
2083 if let Some(reordering) = &self.reordering {
2084 left_quaternary = reordering.reorder(left_quaternary);
2085 right_quaternary = reordering.reorder(right_quaternary);
2086 }
2087 if left_quaternary < right_quaternary {
2088 return Ordering::Less;
2089 }
2090 return Ordering::Greater;
2091 }
2092 if left_quaternary == NO_CE_PRIMARY {
2093 break;
2094 }
2095 }
2096
2097 Ordering::Equal
2098 }
2099
2100 fn sort_key_levels(&self) -> u8 {
2101 #[expect(clippy::indexing_slicing)]
2102 let mut levels = LEVEL_MASKS[self.options.strength() as usize];
2103 if self.options.case_level() {
2104 levels |= CASE_LEVEL_FLAG;
2105 }
2106 levels
2107 }
2108
2109 pub fn write_sort_key_to<S>(&self, s: &str, sink: &mut S) -> Result<S::Output, S::Error>
2158 where
2159 S: CollationKeySink + ?Sized,
2160 S::State: Default,
2161 {
2162 self.write_sort_key_impl(s.chars_with_trie_default_for_ascii(self.norm_trie()), sink)
2163 }
2164
2165 pub fn write_sort_key_utf8_to<S>(&self, s: &[u8], 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_utf16_to<S>(&self, s: &[u16], 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(self.norm_trie()), sink)
2185 }
2186
2187 fn write_sort_key_impl<I, T, S>(
2188 &'data self,
2189 iter: I,
2190 sink: &mut S,
2191 ) -> Result<S::Output, S::Error>
2192 where
2193 I: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + Clone + 'data,
2194 T: AbstractCodePointTrie<'data, u32> + 'data,
2195 S: CollationKeySink + ?Sized,
2196 S::State: Default,
2197 {
2198 let identical = if self.options.strength() == Strength::Identical {
2199 Some(iter.clone())
2200 } else {
2201 None
2202 };
2203
2204 let mut state = S::State::default();
2205 self.write_sort_key_up_to_quaternary(iter, sink, &mut state)?;
2206
2207 if let Some(iter) = identical {
2208 sink.write_byte(&mut state, LEVEL_SEPARATOR_BYTE)?;
2209
2210 let mut iter = icu_normalizer::new_decomposition(iter, self.tables);
2211 let _ = iter.next(); write_identical_level(iter, sink, &mut state)?;
2213 }
2214
2215 sink.finish(state)
2216 }
2217
2218 fn write_sort_key_up_to_quaternary<I, S, T>(
2223 &'data self,
2224 iter: I,
2225 sink: &mut S,
2226 state: &mut S::State,
2227 ) -> Result<(), S::Error>
2228 where
2229 I: Iterator<Item = (char, u32)> + WithTrie<'data, T, u32> + Clone + 'data,
2230 T: AbstractCodePointTrie<'data, u32> + 'data,
2231 S: CollationKeySink + ?Sized,
2232 {
2233 let levels = self.sort_key_levels();
2235
2236 let mut iter = collation_elements!(self, iter, self.numeric_primary());
2237 iter.init();
2238 let variable_top = self.variable_top();
2239
2240 let tertiary_mask = self.options.tertiary_mask().unwrap_or_default();
2241
2242 let mut cases = SortKeyLevel::default();
2243 let mut secondaries = SortKeyLevel::default();
2244 let mut tertiaries = SortKeyLevel::default();
2245 let mut quaternaries = SortKeyLevel::default();
2246
2247 let mut prev_reordered_primary = 0;
2248 let mut common_cases = 0usize;
2249 let mut common_secondaries = 0usize;
2250 let mut common_tertiaries = 0usize;
2251 let mut common_quaternaries = 0usize;
2252 let mut prev_secondary = 0;
2253 let mut sec_segment_start = 0;
2254
2255 loop {
2256 let mut ce = iter.next();
2257 let mut p = ce.primary();
2258 if p < variable_top && p > MERGE_SEPARATOR_PRIMARY {
2259 if common_quaternaries != 0 {
2262 common_quaternaries -= 1;
2263 while common_quaternaries >= QUAT_COMMON[WEIGHT_MAX_COUNT] as _ {
2264 quaternaries.append_byte(QUAT_COMMON[WEIGHT_MIDDLE]);
2265 common_quaternaries -= QUAT_COMMON[WEIGHT_MAX_COUNT] as usize;
2266 }
2267 quaternaries.append_byte(QUAT_COMMON[WEIGHT_LOW] + common_quaternaries as u8);
2269 common_quaternaries = 0;
2270 }
2271
2272 loop {
2273 if levels & QUATERNARY_LEVEL_FLAG != 0 {
2274 if let Some(reordering) = &self.reordering {
2275 p = reordering.reorder(p);
2276 }
2277 if (p >> 24) as u8 >= QUAT_SHIFTED_LIMIT_BYTE {
2278 quaternaries.append_byte(QUAT_SHIFTED_LIMIT_BYTE);
2281 }
2282 quaternaries.append_weight_32(p);
2283 }
2284 loop {
2285 ce = iter.next();
2286 p = ce.primary();
2287 if p != 0 {
2288 break;
2289 }
2290 }
2291 if !(p < variable_top && p > MERGE_SEPARATOR_PRIMARY) {
2292 break;
2293 }
2294 }
2295 }
2296
2297 if p > NO_CE_PRIMARY && levels & PRIMARY_LEVEL_FLAG != 0 {
2301 let is_compressible = self.special_primaries.is_compressible((p >> 24) as _);
2303 if let Some(reordering) = &self.reordering {
2304 p = reordering.reorder(p);
2305 }
2306 let p1 = (p >> 24) as u8;
2307 if !is_compressible || p1 != (prev_reordered_primary >> 24) as u8 {
2308 if prev_reordered_primary != 0 {
2309 if p < prev_reordered_primary {
2310 if p1 > MERGE_SEPARATOR_BYTE {
2313 sink.write(state, &[PRIMARY_COMPRESSION_LOW_BYTE])?;
2314 }
2315 } else {
2316 sink.write(state, &[PRIMARY_COMPRESSION_HIGH_BYTE])?;
2317 }
2318 }
2319 sink.write_byte(state, p1)?;
2320 prev_reordered_primary = if is_compressible { p } else { 0 };
2321 }
2322
2323 let p2 = (p >> 16) as u8;
2324 if p2 != 0 {
2325 let (b0, b1, b2) = (p2, (p >> 8) as _, p as _);
2326 sink.write_byte(state, b0)?;
2327 if b1 != 0 {
2328 sink.write_byte(state, b1)?;
2329 if b2 != 0 {
2330 sink.write_byte(state, b2)?;
2331 }
2332 }
2333 }
2334 }
2335
2336 let non_primary = ce.non_primary();
2337 if non_primary.ignorable() {
2338 continue; }
2340
2341 macro_rules! handle_common {
2342 ($key:ident, $w:ident, $common:ident, $weights:ident, $lim:expr) => {
2343 if $common != 0 {
2344 $common -= 1;
2345 while $common >= $weights[WEIGHT_MAX_COUNT] as _ {
2346 $key.append_byte($weights[WEIGHT_MIDDLE]);
2347 $common -= $weights[WEIGHT_MAX_COUNT] as usize;
2348 }
2349 let b = if $w < $lim {
2350 $weights[WEIGHT_LOW] + ($common as u8)
2351 } else {
2352 $weights[WEIGHT_HIGH] - ($common as u8)
2353 };
2354 $key.append_byte(b);
2355 $common = 0;
2356 }
2357 };
2358 ($key:ident, $w:ident, $common:ident, $weights:ident) => {
2359 handle_common!($key, $w, $common, $weights, COMMON_WEIGHT16);
2360 };
2361 }
2362
2363 if levels & SECONDARY_LEVEL_FLAG != 0 {
2364 let s = non_primary.secondary();
2365 if s == 0 {
2366 } else if s == COMMON_WEIGHT16
2368 && (!self.options.backward_second_level() || p != MERGE_SEPARATOR_PRIMARY)
2369 {
2370 common_secondaries += 1;
2373 } else if !self.options.backward_second_level() {
2374 handle_common!(secondaries, s, common_secondaries, SEC_COMMON);
2375 secondaries.append_weight_16(s);
2376 } else {
2377 if common_secondaries != 0 {
2378 common_secondaries -= 1;
2379 let remainder = common_secondaries % SEC_COMMON[WEIGHT_MAX_COUNT] as usize;
2381 let b = if prev_secondary < COMMON_WEIGHT16 {
2382 SEC_COMMON[WEIGHT_LOW] + remainder as u8
2383 } else {
2384 SEC_COMMON[WEIGHT_HIGH] - remainder as u8
2385 };
2386 secondaries.append_byte(b);
2387 common_secondaries -= remainder;
2388 while common_secondaries > 0 {
2390 secondaries.append_byte(SEC_COMMON[WEIGHT_MIDDLE]);
2392 common_secondaries -= SEC_COMMON[WEIGHT_MAX_COUNT] as usize;
2393 }
2394 }
2396 if 0 < p && p <= MERGE_SEPARATOR_PRIMARY {
2397 let secs = &mut secondaries.buf;
2400 let last = secs.len() - 1;
2401 if sec_segment_start < last {
2402 let mut q = sec_segment_start;
2403 let mut r = last;
2404
2405 #[expect(clippy::indexing_slicing)]
2407 while q < r {
2408 let b = secs[q];
2409 secs[q] = secs[r];
2410 q += 1;
2411 secs[r] = b;
2412 r -= 1;
2413 }
2414 }
2415 let b = if p == NO_CE_PRIMARY {
2416 LEVEL_SEPARATOR_BYTE
2417 } else {
2418 MERGE_SEPARATOR_BYTE
2419 };
2420 secondaries.append_byte(b);
2421 prev_secondary = 0;
2422 sec_segment_start = secondaries.len();
2423 } else {
2424 secondaries.append_reverse_weight_16(s);
2425 prev_secondary = s;
2426 }
2427 }
2428 }
2429
2430 if levels & CASE_LEVEL_FLAG != 0 {
2431 if self.options.strength() == Strength::Primary && p == 0
2432 || non_primary.bits() <= 0xffff
2433 {
2434 } else {
2438 let mut c = ((non_primary.bits() >> 8) & 0xff) as u8;
2440 debug_assert_ne!(c & 0xc0, 0xc0);
2441 if c & 0xc0 == 0 && c > LEVEL_SEPARATOR_BYTE {
2442 common_cases += 1;
2443 } else {
2444 if !self.options.upper_first() {
2445 if common_cases != 0 && (c > LEVEL_SEPARATOR_BYTE || !cases.is_empty())
2450 {
2451 common_cases -= 1;
2452 while common_cases >= CASE_LOWER_FIRST_COMMON[WEIGHT_MAX_COUNT] as _
2453 {
2454 cases.append_byte(CASE_LOWER_FIRST_COMMON[WEIGHT_MIDDLE] << 4);
2455 common_cases -=
2456 CASE_LOWER_FIRST_COMMON[WEIGHT_MAX_COUNT] as usize;
2457 }
2458 let b = if c <= LEVEL_SEPARATOR_BYTE {
2459 CASE_LOWER_FIRST_COMMON[WEIGHT_LOW] + common_cases as u8
2460 } else {
2461 CASE_LOWER_FIRST_COMMON[WEIGHT_HIGH] - common_cases as u8
2462 };
2463 cases.append_byte(b << 4);
2464 common_cases = 0;
2465 }
2466 if c > LEVEL_SEPARATOR_BYTE {
2467 c = (CASE_LOWER_FIRST_COMMON[WEIGHT_HIGH] + (c >> 6)) << 4;
2469 }
2470 } else {
2471 if common_cases != 0 {
2476 common_cases -= 1;
2477 while common_cases >= CASE_UPPER_FIRST_COMMON[WEIGHT_MAX_COUNT] as _
2478 {
2479 cases.append_byte(CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] << 4);
2480 common_cases -=
2481 CASE_UPPER_FIRST_COMMON[WEIGHT_MAX_COUNT] as usize;
2482 }
2483 cases.append_byte(
2484 (CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] + common_cases as u8) << 4,
2485 );
2486 common_cases = 0;
2487 }
2488 if c > LEVEL_SEPARATOR_BYTE {
2489 c = (CASE_UPPER_FIRST_COMMON[WEIGHT_LOW] - (c >> 6)) << 4;
2491 }
2492 }
2493 cases.append_byte(c);
2496 }
2497 }
2498 }
2499
2500 if levels & TERTIARY_LEVEL_FLAG != 0 {
2501 let mut t = non_primary.tertiary_case_quarternary(tertiary_mask);
2502 debug_assert_ne!(non_primary.bits() & 0xc000, 0xc000);
2503 if t == COMMON_WEIGHT16 {
2504 common_tertiaries += 1;
2505 } else if tertiary_mask & 0x8000 == 0 {
2506 handle_common!(tertiaries, t, common_tertiaries, TER_ONLY_COMMON);
2509 if t > COMMON_WEIGHT16 {
2510 t += 0xc000;
2511 }
2512 tertiaries.append_weight_16(t);
2513 } else if !self.options.upper_first() {
2514 handle_common!(tertiaries, t, common_tertiaries, TER_LOWER_FIRST_COMMON);
2517 if t > COMMON_WEIGHT16 {
2518 t += 0x4000;
2519 }
2520 tertiaries.append_weight_16(t);
2521 } else {
2522 if t <= NO_CE_TERTIARY {
2535 } else if non_primary.bits() > 0xffff {
2537 t ^= 0xc000;
2539 if t < (TER_UPPER_FIRST_COMMON[WEIGHT_HIGH] as u16) << 8 {
2540 t -= 0x4000;
2541 }
2542 } else {
2543 debug_assert!((0x8600..=0xbfff).contains(&t));
2545 t += 0x4000;
2546 }
2547 handle_common!(
2548 tertiaries,
2549 t,
2550 common_tertiaries,
2551 TER_UPPER_FIRST_COMMON,
2552 (TER_UPPER_FIRST_COMMON[WEIGHT_LOW] as u16) << 8
2553 );
2554 tertiaries.append_weight_16(t);
2555 }
2556 }
2557
2558 if levels & QUATERNARY_LEVEL_FLAG != 0 {
2559 let q = (non_primary.bits() & 0xffff) as u16;
2560 if q & 0xc0 == 0 && q > NO_CE_QUATERNARY {
2561 common_quaternaries += 1;
2562 } else if q == NO_CE_QUATERNARY
2563 && self.options.alternate_handling() == AlternateHandling::NonIgnorable
2564 && quaternaries.is_empty()
2565 {
2566 quaternaries.append_byte(LEVEL_SEPARATOR_BYTE);
2574 } else {
2575 let q = if q == NO_CE_QUATERNARY {
2576 LEVEL_SEPARATOR_BYTE
2577 } else {
2578 (0xfc + ((q >> 6) & 3)) as u8
2579 };
2580 handle_common!(
2581 quaternaries,
2582 q,
2583 common_quaternaries,
2584 QUAT_COMMON,
2585 QUAT_COMMON[WEIGHT_LOW]
2586 );
2587 quaternaries.append_byte(q);
2588 }
2589 }
2590
2591 if (non_primary.bits() >> 24) as u8 == LEVEL_SEPARATOR_BYTE {
2592 break; }
2594 }
2595
2596 macro_rules! write_level {
2597 ($key:ident, $flag:ident) => {
2598 if levels & $flag != 0 {
2599 sink.write(state, &[LEVEL_SEPARATOR_BYTE])?;
2600 sink.write(state, &$key.buf)?;
2601 }
2602 };
2603 }
2604
2605 write_level!(secondaries, SECONDARY_LEVEL_FLAG);
2606
2607 if levels & CASE_LEVEL_FLAG != 0 {
2608 sink.write(state, &[LEVEL_SEPARATOR_BYTE])?;
2609
2610 let mut b = 0;
2612 if let Some((last, head)) = cases.buf.split_last() {
2613 debug_assert_eq!(*last, 1); for c in head {
2615 debug_assert_eq!(*c & 0xf, 0);
2616 debug_assert_ne!(*c, 0);
2617 if b == 0 {
2618 b = *c;
2619 } else {
2620 sink.write_byte(state, b | (*c >> 4))?;
2621 b = 0;
2622 }
2623 }
2624 } else {
2625 debug_assert!(false);
2626 }
2627 if b != 0 {
2628 sink.write_byte(state, b)?;
2629 }
2630 }
2631
2632 write_level!(tertiaries, TERTIARY_LEVEL_FLAG);
2633 write_level!(quaternaries, QUATERNARY_LEVEL_FLAG);
2634
2635 Ok(())
2636 }
2637}
2638
2639#[derive(Debug, PartialEq, Eq)]
2641pub struct TooSmall {
2642 pub length: usize,
2644}
2645
2646impl TooSmall {
2647 pub fn new(length: usize) -> Self {
2648 Self { length }
2649 }
2650}
2651
2652pub trait CollationKeySink {
2666 type Error;
2668
2669 type State;
2671
2672 type Output;
2674
2675 fn write(&mut self, state: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error>;
2677
2678 fn write_byte(&mut self, state: &mut Self::State, b: u8) -> Result<(), Self::Error> {
2680 self.write(state, &[b])
2681 }
2682
2683 fn finish(&mut self, state: Self::State) -> Result<Self::Output, Self::Error>;
2686}
2687
2688impl CollationKeySink for Vec<u8> {
2689 type Error = Infallible;
2690 type State = ();
2691 type Output = ();
2692
2693 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2694 self.extend_from_slice(buf);
2695 Ok(())
2696 }
2697
2698 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2699 Ok(())
2700 }
2701}
2702
2703impl CollationKeySink for VecDeque<u8> {
2704 type Error = Infallible;
2705 type State = ();
2706 type Output = ();
2707
2708 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2709 self.extend(buf.iter());
2710 Ok(())
2711 }
2712
2713 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2714 Ok(())
2715 }
2716}
2717
2718impl<const N: usize> CollationKeySink for SmallVec<[u8; N]> {
2719 type Error = Infallible;
2720 type State = ();
2721 type Output = ();
2722
2723 fn write(&mut self, _: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2724 self.extend_from_slice(buf);
2725 Ok(())
2726 }
2727
2728 fn finish(&mut self, _: Self::State) -> Result<Self::Output, Self::Error> {
2729 Ok(())
2730 }
2731}
2732
2733impl CollationKeySink for [u8] {
2734 type Error = TooSmall;
2735 type State = usize;
2736 type Output = usize;
2737
2738 fn write(&mut self, offset: &mut Self::State, buf: &[u8]) -> Result<(), Self::Error> {
2739 if *offset + buf.len() <= self.len() {
2740 #[expect(clippy::indexing_slicing)]
2742 self[*offset..*offset + buf.len()].copy_from_slice(buf);
2743 }
2744 *offset += buf.len();
2745 Ok(())
2746 }
2747
2748 fn finish(&mut self, offset: Self::State) -> Result<Self::Output, Self::Error> {
2749 if offset <= self.len() {
2750 Ok(offset)
2751 } else {
2752 Err(TooSmall::new(offset))
2753 }
2754 }
2755}
2756
2757#[derive(Default)]
2758struct SortKeyLevel {
2759 buf: SmallVec<[u8; 40]>,
2760}
2761
2762impl SortKeyLevel {
2763 fn len(&self) -> usize {
2764 self.buf.len()
2765 }
2766
2767 fn is_empty(&self) -> bool {
2768 self.buf.is_empty()
2769 }
2770
2771 fn append_byte(&mut self, x: u8) {
2772 self.buf.push(x);
2773 }
2774
2775 fn append_weight_16(&mut self, w: u16) {
2776 debug_assert_ne!(w, 0);
2777 let b0 = (w >> 8) as u8;
2778 let b1 = w as u8;
2779 self.append_byte(b0);
2780 if b1 != 0 {
2781 self.append_byte(b1);
2782 }
2783 }
2784
2785 fn append_reverse_weight_16(&mut self, w: u16) {
2786 debug_assert_ne!(w, 0);
2787 let b0 = (w >> 8) as u8;
2788 let b1 = w as u8;
2789 if b1 != 0 {
2790 self.append_byte(b1);
2791 }
2792 self.append_byte(b0);
2793 }
2794
2795 fn append_weight_32(&mut self, w: u32) {
2796 debug_assert_ne!(w, 0);
2797 let b0 = (w >> 24) as u8;
2798 let b1 = (w >> 16) as u8;
2799 let b2 = (w >> 8) as u8;
2800 let b3 = w as u8;
2801 self.append_byte(b0);
2802 if b1 != 0 {
2803 self.append_byte(b1);
2804 if b2 != 0 {
2805 self.append_byte(b2);
2806 if b3 != 0 {
2807 self.append_byte(b3);
2808 }
2809 }
2810 }
2811 }
2812}
2813
2814macro_rules! negdivmod {
2820 ($n:ident, $d:ident, $m:ident) => {
2821 $m = $n % $d;
2822 $n /= $d;
2823 if $m < 0 {
2824 $n -= 1;
2825 $m += $d;
2826 }
2827 };
2828}
2829
2830fn write_diff<S>(mut diff: i32, sink: &mut S, state: &mut S::State) -> Result<(), S::Error>
2831where
2832 S: CollationKeySink + ?Sized,
2833{
2834 let mut out = |b| sink.write_byte(state, b);
2835
2836 if diff >= SLOPE_REACH_NEG_1 {
2837 if diff <= SLOPE_REACH_POS_1 {
2838 out((SLOPE_MIDDLE + diff) as _)?;
2839 } else if diff <= SLOPE_REACH_POS_2 {
2840 out((SLOPE_START_POS_2 + (diff / SLOPE_TAIL_COUNT)) as _)?;
2841 out((SLOPE_MIN + diff % SLOPE_TAIL_COUNT) as _)?;
2842 } else if diff <= SLOPE_REACH_POS_3 {
2843 let p2 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2844 diff /= SLOPE_TAIL_COUNT;
2845 let p1 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2846 let p0 = SLOPE_START_POS_3 + (diff / SLOPE_TAIL_COUNT);
2847 out(p0 as _)?;
2848 out(p1 as _)?;
2849 out(p2 as _)?;
2850 } else {
2851 let p3 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2852 diff /= SLOPE_TAIL_COUNT;
2853 let p2 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2854 diff /= SLOPE_TAIL_COUNT;
2855 let p1 = SLOPE_MIN + diff % SLOPE_TAIL_COUNT;
2856 out(SLOPE_MAX as _)?;
2857 out(p1 as _)?;
2858 out(p2 as _)?;
2859 out(p3 as _)?;
2860 }
2861 } else {
2862 let mut m;
2863
2864 if diff >= SLOPE_REACH_NEG_2 {
2865 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2866 out((SLOPE_START_NEG_2 + diff) as _)?;
2867 out((SLOPE_MIN + m) as _)?;
2868 } else if diff >= SLOPE_REACH_NEG_3 {
2869 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2870 let p2 = SLOPE_MIN + m;
2871 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2872 let p1 = SLOPE_MIN + m;
2873 let p0 = SLOPE_START_NEG_3 + diff;
2874 out(p0 as _)?;
2875 out(p1 as _)?;
2876 out(p2 as _)?;
2877 } else {
2878 negdivmod!(diff, SLOPE_TAIL_COUNT, m);
2879 let p3 = SLOPE_MIN + m;
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 _ = diff;
2885 out(SLOPE_MIN as _)?;
2886 out(p1 as _)?;
2887 out(p2 as _)?;
2888 out(p3 as _)?;
2889 }
2890 }
2891
2892 Ok(())
2893}
2894
2895fn write_identical_level<I, S>(iter: I, sink: &mut S, state: &mut S::State) -> Result<(), S::Error>
2896where
2897 I: Iterator<Item = char>,
2898 S: CollationKeySink + ?Sized,
2899{
2900 let mut prev = 0i32;
2901
2902 for c in iter {
2903 if !(0x4e00..=0xa000).contains(&prev) {
2904 prev = (prev & !0x7f) - SLOPE_REACH_NEG_1;
2905 } else {
2906 prev = 0x9fff - SLOPE_REACH_POS_2;
2908 }
2909
2910 if c == MERGE_SEPARATOR {
2911 sink.write_byte(state, MERGE_SEPARATOR_BYTE)?;
2912 prev = 0;
2913 } else {
2914 let c = c as i32;
2915 write_diff(c - prev, sink, state)?;
2916 prev = c;
2917 }
2918 }
2919 Ok(())
2920}
2921
2922#[cfg(test)]
2923mod test {
2924 use super::*;
2925 use icu_locale::locale;
2926
2927 type Key = Vec<u8>;
2928
2929 fn collator_en(strength: Strength) -> CollatorBorrowed<'static> {
2930 let locale = locale!("en").into();
2931 let mut options = CollatorOptions::default();
2932 options.strength = Some(strength);
2933 Collator::try_new(locale, options).unwrap()
2934 }
2935
2936 fn collator_en_case_level(strength: Strength) -> CollatorBorrowed<'static> {
2937 let locale = locale!("en").into();
2938 let mut options = CollatorOptions::default();
2939 options.strength = Some(strength);
2940 options.case_level = Some(crate::options::CaseLevel::On);
2941 Collator::try_new(locale, options).unwrap()
2942 }
2943
2944 fn keys(strength: Strength) -> (Key, Key, Key) {
2945 let collator = collator_en(strength);
2946
2947 let mut k0 = Vec::new();
2948 let Ok(()) = collator.write_sort_key_to("aabc", &mut k0);
2949 let mut k1 = Vec::new();
2950 let Ok(()) = collator.write_sort_key_to("aAbc", &mut k1);
2951 let mut k2 = Vec::new();
2952 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k2);
2953
2954 (k0, k1, k2)
2955 }
2956
2957 #[test]
2958 fn sort_key_primary() {
2959 let (k0, k1, k2) = keys(Strength::Primary);
2960 assert_eq!(k0, k1);
2961 assert_eq!(k1, k2);
2962 }
2963
2964 #[test]
2965 fn sort_key_secondary() {
2966 let (k0, k1, k2) = keys(Strength::Secondary);
2967 assert_eq!(k0, k1);
2968 assert!(k1 < k2);
2969 }
2970
2971 #[test]
2972 fn sort_key_tertiary() {
2973 let (k0, k1, k2) = keys(Strength::Tertiary);
2974 assert!(k0 < k1);
2975 assert!(k1 < k2);
2976 }
2977
2978 fn collator_ja(strength: Strength) -> CollatorBorrowed<'static> {
2979 let locale = locale!("ja").into();
2980 let mut options = CollatorOptions::default();
2981 options.strength = Some(strength);
2982 Collator::try_new(locale, options).unwrap()
2983 }
2984
2985 fn keys_ja_strs(strength: Strength, s0: &str, s1: &str) -> (Key, Key) {
2986 let collator = collator_ja(strength);
2987
2988 let mut k0 = Vec::new();
2989 let Ok(()) = collator.write_sort_key_to(s0, &mut k0);
2990 let mut k1 = Vec::new();
2991 let Ok(()) = collator.write_sort_key_to(s1, &mut k1);
2992
2993 (k0, k1)
2994 }
2995
2996 fn keys_ja(strength: Strength) -> (Key, Key) {
2997 keys_ja_strs(strength, "あ", "ア")
2998 }
2999
3000 #[test]
3001 fn sort_keys_ja_to_quaternary() {
3002 let (k0, k1) = keys_ja(Strength::Primary);
3003 assert_eq!(k0, k1);
3004 let (k0, k1) = keys_ja(Strength::Secondary);
3005 assert_eq!(k0, k1);
3006 let (k0, k1) = keys_ja(Strength::Tertiary);
3007 assert_eq!(k0, k1);
3008 let (k0, k1) = keys_ja(Strength::Quaternary);
3009 assert!(k0 < k1);
3010 }
3011
3012 #[test]
3013 fn sort_keys_ja_identical() {
3014 let (k0, k1) = keys_ja_strs(Strength::Quaternary, "ア", "ア");
3015 assert_eq!(k0, k1);
3016 let (k0, k1) = keys_ja_strs(Strength::Identical, "ア", "ア");
3017 assert!(k0 < k1);
3018 }
3019
3020 #[test]
3021 fn sort_keys_utf16() {
3022 let collator = collator_en(Strength::Identical);
3023
3024 const STR8: &[u8] = b"hello world!";
3025 let mut k8 = Vec::new();
3026 let Ok(()) = collator.write_sort_key_utf8_to(STR8, &mut k8);
3027
3028 const STR16: &[u16] = &[
3029 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
3030 ];
3031 let mut k16 = Vec::new();
3032 let Ok(()) = collator.write_sort_key_utf16_to(STR16, &mut k16);
3033 assert_eq!(k8, k16);
3034 }
3035
3036 #[test]
3037 fn sort_keys_invalid() {
3038 let collator = collator_en(Strength::Identical);
3039
3040 let mut k = Vec::new();
3042 let Ok(()) = collator.write_sort_key_utf8_to(b"\xf0\x90", &mut k);
3043 let mut k = Vec::new();
3044 let Ok(()) = collator.write_sort_key_utf16_to(&[0xdd1e], &mut k);
3045 }
3046
3047 #[test]
3048 fn sort_key_to_vecdeque() {
3049 let collator = collator_en(Strength::Identical);
3050
3051 let mut k0 = Vec::new();
3052 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k0);
3053 let mut k1 = VecDeque::new();
3054 let Ok(()) = collator.write_sort_key_to("áAbc", &mut k1);
3055 assert!(k0.iter().eq(k1.iter()));
3056 }
3057
3058 #[test]
3059 fn sort_key_to_slice() {
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 = [0u8; 100];
3065 let len = collator.write_sort_key_to("áAbc", &mut k1[..]).unwrap();
3066 assert_eq!(len, k0.len());
3067 assert!(k0.iter().eq(k1[..len].iter()));
3068 }
3069
3070 #[test]
3071 fn sort_key_to_slice_no_space() {
3072 let collator = collator_en(Strength::Identical);
3073 let mut k = [0u8; 0];
3074 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3075 assert!(matches!(res, Err(TooSmall { .. })));
3076 }
3077
3078 #[test]
3079 fn sort_key_to_slice_too_long() {
3080 let collator = collator_en(Strength::Identical);
3082 let mut k = [0u8; 5];
3083 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3084 assert!(matches!(res, Err(TooSmall { .. })));
3085 }
3086
3087 #[test]
3088 fn sort_key_to_slice_identical_too_long() {
3089 let collator = collator_en(Strength::Identical);
3091 let mut k = [0u8; 22];
3092 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3093 assert!(matches!(res, Err(TooSmall { .. })));
3094 }
3095
3096 #[test]
3097 fn sort_key_just_right() {
3098 let collator = collator_en(Strength::Identical);
3100 let mut k = [0u8; 0];
3101 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3102 let len = res.unwrap_err().length;
3103
3104 let mut k = vec![0u8; len - 1];
3106 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3107 let len = res.unwrap_err().length;
3108
3109 let mut k = vec![0u8; len];
3111 let res = collator.write_sort_key_to("áAbc", &mut k[..]);
3112 assert_eq!(res, Ok(len));
3113 }
3114
3115 #[test]
3116 fn sort_key_utf16_slice_too_small() {
3117 let collator = collator_en(Strength::Identical);
3118 const STR16: &[u16] = &[0x68, 0x65, 0x6c, 0x6c, 0x6f];
3119 let mut k = [0u8; 4];
3120 let res = collator.write_sort_key_utf16_to(STR16, &mut k[..]);
3121 assert!(matches!(res, Err(TooSmall { .. })));
3122 }
3123
3124 #[test]
3125 fn sort_key_very_long() {
3126 let collator = collator_en(Strength::Secondary);
3127 let mut k = Vec::new();
3128 let Ok(()) = collator.write_sort_key_to(&"a".repeat(300), &mut k);
3129 }
3130
3131 #[test]
3132 fn sort_key_case_level() {
3133 let collator = collator_en_case_level(Strength::Tertiary);
3134 let mut k = Vec::new();
3135 let Ok(()) = collator.write_sort_key_to("aBc", &mut k);
3136 }
3137
3138 #[test]
3139 fn sort_key_case_level_empty() {
3140 let collator = collator_en_case_level(Strength::Tertiary);
3141 let mut k = Vec::new();
3142 let Ok(()) = collator.write_sort_key_to("", &mut k);
3143 }
3144
3145 fn check_sort_key_less(a: &[u16], b: &[u16]) {
3146 let collator = collator_en(Strength::Identical);
3147 let mut ak = Vec::new();
3148 let Ok(()) = collator.write_sort_key_utf16_to(a, &mut ak);
3149 let mut bk = Vec::new();
3150 let Ok(()) = collator.write_sort_key_utf16_to(b, &mut bk);
3151 assert!(ak < bk, "failed: {a:04x?} - {b:04x?}");
3152 }
3153
3154 #[test]
3155 fn sort_key_fffe_bug_6811() {
3156 check_sort_key_less(
3157 &[0xfffe, 0x0001, 0x0002, 0x0003],
3158 &[0x0001, 0xfffe, 0x0002, 0x0003],
3159 );
3160 check_sort_key_less(
3161 &[0x0001, 0xfffe, 0x0002, 0x0003],
3162 &[0x0001, 0x0002, 0xfffe, 0x0003],
3163 );
3164 check_sort_key_less(
3165 &[0x0001, 0x0002, 0xfffe, 0x0003],
3166 &[0x0001, 0x0002, 0x0003, 0xfffe],
3167 );
3168 check_sort_key_less(&[0xfffe, 0x0000, 0x0000], &[0x0000, 0xfffe, 0x0000]);
3169 check_sort_key_less(&[0x0000, 0xfffe, 0x0000], &[0x0000, 0x0000, 0xfffe]);
3170 }
3171}