Skip to content

Commit ba900d3

Browse files
authored
Move PatternsByGreatestDifference::get_*_pattern resolvers to PatternsByGreatestDifferenceULE (#8148)
This is annoying. Normally, our VarULE types are the "internal" types and we implement complex logic on our regular types, because it's easy, and you can always go from a VarULE type to a regular one but the other way around isn't always possible. So having methods on `PatternsByGreatestDifference` is great: they can refer to the "nice" decoded fields, and if we have a `PatternsByGreatestDifferenceULE` (which we will) we can always `ZeroFrom` it to the other type. Unfortunately, there's one downside: because `PatternsByGreatestDifference` _could_ be allocated, we cannot return long lived `'data` references to its patterns. This is why [`PatternsByGreatestDifference::get_*_pattern`](https://unicode-org.github.io/icu4x/rustdoc/icu/datetime/provider/range_patterns/struct.PatternsByGreatestDifference.html#method.get_date_pattern) returns things with lifetime `'a` not `'data`. So we can't actually write code that e.g. returns an appropriately resolved `FormattedDateTimeRange` that contains `'data` patterns. `PatternsByGreatestDifference*` lives inside a VarZeroVec in [`GenericPackedPatterns`](https://unicode-org.github.io/icu4x/rustdoc/icu/datetime/provider/packed_pattern/struct.GenericPackedPatterns.html) anyway, which means ICU4X code will _always_ have this type as `PatternsByGreatestDifferenceULE` (even a runtime mutated VarZeroVec contains encoded data). So there's no API downside of moving these methods over to `PatternsByGreatestDifferenceULE` where they will be actually callable. The main downsides are: - The code is slightly worse since it needs to decode some AsULEs. Not a big deal - The tests are slightly worse because they need to convert to `PatternsByGreatestDifferenceULE`. _shrug_. ## Changelog: N/A <!-- Please fill in according to documents/process/changelog.md -->
1 parent d011dcc commit ba900d3

2 files changed

Lines changed: 146 additions & 65 deletions

File tree

components/datetime/src/provider/pattern/runtime/pattern.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ pub struct Pattern<'data> {
4040
}
4141

4242
/// Fully borrowed version of [`Pattern`].
43-
#[derive(Debug, Copy, Clone)]
44-
pub(crate) struct PatternBorrowed<'data> {
43+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
44+
pub struct PatternBorrowed<'data> {
4545
pub(crate) items: &'data ZeroSlice<PatternItem>,
4646
pub(crate) metadata: PatternMetadata,
4747
}
@@ -150,6 +150,17 @@ impl<'data> PatternBorrowed<'data> {
150150
}
151151
}
152152

153+
impl<'data> zerofrom::ZeroFrom<'data, PatternULE> for PatternBorrowed<'data> {
154+
#[inline]
155+
fn zero_from(ule: &'data PatternULE) -> Self {
156+
use zerovec::ule::AsULE;
157+
Self {
158+
items: &ule.items,
159+
metadata: <PatternMetadata as AsULE>::from_unaligned(ule.metadata),
160+
}
161+
}
162+
}
163+
153164
impl From<Vec<PatternItem>> for Pattern<'_> {
154165
fn from(items: Vec<PatternItem>) -> Self {
155166
Self {

components/datetime/src/provider/range_patterns.rs

Lines changed: 133 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#[cfg(feature = "datagen")]
88
use crate::provider::packed_pattern::GenericUnpackedPatterns;
99
use crate::provider::packed_pattern::{GenericPackedPatterns, PackedPatternsBuilderHelper};
10+
use crate::provider::pattern::runtime::PatternBorrowed;
1011
use crate::provider::pattern::runtime::{Pattern, PatternULE};
1112
use icu_provider::prelude::*;
1213
use zerovec::VarZeroVec;
@@ -128,6 +129,40 @@ pub enum RangePatternInfo<'a> {
128129
FullRange(Pattern<'a>),
129130
}
130131

132+
impl<'a> RangePatternInfo<'a> {
133+
/// Gets the inner pattern.
134+
pub fn pattern(&self) -> &Pattern<'a> {
135+
match self {
136+
Self::Symmetric(p) => p,
137+
Self::FullRange(p) => p,
138+
}
139+
}
140+
}
141+
142+
/// Fully borrowed version of [`RangePatternInfo`].
143+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
144+
pub enum RangePatternInfoBorrowed<'a> {
145+
/// Symmetric pattern: `[sub] [glue] [sub]`, where the sub-pattern is shared.
146+
Symmetric(PatternBorrowed<'a>),
147+
/// Full Range pattern: the entire range pattern is stored.
148+
FullRange(PatternBorrowed<'a>),
149+
}
150+
151+
impl<'a> From<RangePatternInfoBorrowed<'a>> for RangePatternInfo<'a> {
152+
fn from(other: RangePatternInfoBorrowed<'a>) -> Self {
153+
match other {
154+
RangePatternInfoBorrowed::Symmetric(p) => RangePatternInfo::Symmetric(p.as_pattern()),
155+
RangePatternInfoBorrowed::FullRange(p) => RangePatternInfo::FullRange(p.as_pattern()),
156+
}
157+
}
158+
}
159+
160+
impl<'a> zerofrom::ZeroFrom<'a, RangePatternInfoBorrowed<'a>> for RangePatternInfo<'a> {
161+
fn zero_from(other: &'a RangePatternInfoBorrowed<'a>) -> Self {
162+
Self::from(*other)
163+
}
164+
}
165+
131166
/// A bitset encoding which fields are present in the `GreatestDifference` pattern list
132167
/// and their range structure.
133168
///
@@ -235,55 +270,53 @@ fn resolve_fallback(header: GreatestDifferenceHeader, requested: u8, max_value:
235270
None
236271
}
237272

238-
impl<'data> PatternsByGreatestDifference<'data> {
239-
/// Internal helper to retrieve and construct `RangePatternInfo` for a resolved field index.
240-
///
241-
/// GIGO: If the `resolved_field_idx` is `Absent`, it returns a default symmetric pattern
242-
/// to avoid panicking at runtime.
243-
fn get_pattern_internal<'a>(&'a self, resolved_field_idx: u8) -> RangePatternInfo<'a> {
244-
let state = self.header.get_state(resolved_field_idx);
245-
let start_idx = self.header.get_pattern_index(resolved_field_idx);
273+
impl PatternsByGreatestDifferenceULE {
274+
fn get_pattern_internal<'a>(&'a self, resolved_field_idx: u8) -> RangePatternInfoBorrowed<'a> {
275+
use zerovec::ule::AsULE;
276+
let header = <GreatestDifferenceHeader as AsULE>::from_unaligned(self.header);
277+
let state = header.get_state(resolved_field_idx);
278+
let start_idx = header.get_pattern_index(resolved_field_idx);
246279

247280
let get_pat = |offset| {
248281
self.patterns
249282
.get(start_idx + offset)
250-
.map(<Pattern as zerofrom::ZeroFrom<PatternULE>>::zero_from)
251-
.unwrap_or_default()
283+
.map(<PatternBorrowed as zerofrom::ZeroFrom<PatternULE>>::zero_from)
284+
.unwrap_or(PatternBorrowed::DEFAULT)
252285
};
253286

254287
match state {
255-
RangeStructure::Absent => RangePatternInfo::Symmetric(Pattern::default()),
256-
RangeStructure::Symmetric => RangePatternInfo::Symmetric(get_pat(0)),
257-
RangeStructure::FullRange => RangePatternInfo::FullRange(get_pat(0)),
288+
RangeStructure::Absent => RangePatternInfoBorrowed::Symmetric(PatternBorrowed::DEFAULT),
289+
RangeStructure::Symmetric => RangePatternInfoBorrowed::Symmetric(get_pat(0)),
290+
RangeStructure::FullRange => RangePatternInfoBorrowed::FullRange(get_pat(0)),
258291
}
259292
}
260293

261294
/// Gets the pattern info for the given date field, falling back to larger fields if necessary.
262295
pub fn get_date_pattern<'a>(
263296
&'a self,
264297
field: DateGreatestDifferenceField,
265-
) -> Option<RangePatternInfo<'a>> {
266-
let resolved_field_idx = resolve_fallback(
267-
self.header,
268-
field as u8,
269-
DateGreatestDifferenceField::MAX_VALUE,
270-
)?;
298+
) -> Option<RangePatternInfoBorrowed<'a>> {
299+
use zerovec::ule::AsULE;
300+
let header = <GreatestDifferenceHeader as AsULE>::from_unaligned(self.header);
301+
let resolved_field_idx =
302+
resolve_fallback(header, field as u8, DateGreatestDifferenceField::MAX_VALUE)?;
271303
Some(self.get_pattern_internal(resolved_field_idx))
272304
}
273305

274306
/// Gets the pattern info for the given time field, falling back to larger fields if necessary.
275307
pub fn get_time_pattern<'a>(
276308
&'a self,
277309
field: TimeGreatestDifferenceField,
278-
) -> Option<RangePatternInfo<'a>> {
279-
let resolved_field_idx = resolve_fallback(
280-
self.header,
281-
field as u8,
282-
TimeGreatestDifferenceField::MAX_VALUE,
283-
)?;
310+
) -> Option<RangePatternInfoBorrowed<'a>> {
311+
use zerovec::ule::AsULE;
312+
let header = <GreatestDifferenceHeader as AsULE>::from_unaligned(self.header);
313+
let resolved_field_idx =
314+
resolve_fallback(header, field as u8, TimeGreatestDifferenceField::MAX_VALUE)?;
284315
Some(self.get_pattern_internal(resolved_field_idx))
285316
}
317+
}
286318

319+
impl<'data> PatternsByGreatestDifference<'data> {
287320
/// Construct from an iterator of (`bit_index`, `pattern_info`).
288321
///
289322
/// The `bit_index` must be <= 3.
@@ -540,23 +573,39 @@ mod tests {
540573
.is_date_field_present(DateGreatestDifferenceField::Year)
541574
);
542575

576+
let pgd_ule_box = zerovec::ule::encode_varule_to_box(&pgd);
577+
let pgd: &PatternsByGreatestDifferenceULE = &pgd_ule_box;
578+
579+
let info_a = RangePatternInfo::Symmetric(pattern1);
580+
let info_b = RangePatternInfo::Symmetric(pattern2);
581+
543582
// Day difference should return Day pattern.
544583
assert_eq!(
545-
pgd.get_date_pattern(DateGreatestDifferenceField::Day),
546-
Some(RangePatternInfo::Symmetric(pattern1.clone()))
584+
pgd.get_date_pattern(DateGreatestDifferenceField::Day)
585+
.map(RangePatternInfo::from)
586+
.as_ref(),
587+
Some(&info_a)
547588
);
548589
// Month difference should fall back to Year pattern (since Month is absent but Year is present).
549590
assert_eq!(
550-
pgd.get_date_pattern(DateGreatestDifferenceField::Month),
551-
Some(RangePatternInfo::Symmetric(pattern2.clone()))
591+
pgd.get_date_pattern(DateGreatestDifferenceField::Month)
592+
.map(RangePatternInfo::from)
593+
.as_ref(),
594+
Some(&info_b)
552595
);
553596
// Year difference should return Year pattern.
554597
assert_eq!(
555-
pgd.get_date_pattern(DateGreatestDifferenceField::Year),
556-
Some(RangePatternInfo::Symmetric(pattern2.clone()))
598+
pgd.get_date_pattern(DateGreatestDifferenceField::Year)
599+
.map(RangePatternInfo::from)
600+
.as_ref(),
601+
Some(&info_b)
557602
);
558603
// Era difference should return None (since Era is absent and no larger field is present).
559-
assert_eq!(pgd.get_date_pattern(DateGreatestDifferenceField::Era), None);
604+
assert_eq!(
605+
pgd.get_date_pattern(DateGreatestDifferenceField::Era)
606+
.as_ref(),
607+
None
608+
);
560609
}
561610

562611
#[test]
@@ -571,8 +620,8 @@ mod tests {
571620
.unwrap()
572621
.to_runtime_pattern();
573622

574-
let info_d = RangePatternInfo::Symmetric(pattern_d.clone());
575-
let info_y = RangePatternInfo::Symmetric(pattern_y.clone());
623+
let info_d = RangePatternInfo::Symmetric(pattern_d);
624+
let info_y = RangePatternInfo::Symmetric(pattern_y);
576625

577626
// Valid date patterns
578627
let pgd = PatternsByGreatestDifference::try_from_date_patterns(
@@ -589,20 +638,22 @@ mod tests {
589638
assert_eq!(
590639
pgd.patterns
591640
.get(0)
592-
.map(<Pattern as zerofrom::ZeroFrom<PatternULE>>::zero_from),
593-
Some(pattern_d.clone())
641+
.map(<Pattern as zerofrom::ZeroFrom<PatternULE>>::zero_from)
642+
.as_ref(),
643+
Some(info_d.pattern())
594644
);
595645
assert_eq!(
596646
pgd.patterns
597647
.get(1)
598-
.map(<Pattern as zerofrom::ZeroFrom<PatternULE>>::zero_from),
599-
Some(pattern_y.clone())
648+
.map(<Pattern as zerofrom::ZeroFrom<PatternULE>>::zero_from)
649+
.as_ref(),
650+
Some(info_y.pattern())
600651
);
601652

602653
// Unsorted input in BTreeMap::from is automatically sorted
603654
let pgd2 = PatternsByGreatestDifference::try_from_date_patterns(
604655
alloc::collections::BTreeMap::from([
605-
(DateGreatestDifferenceField::Year, info_y.clone()),
656+
(DateGreatestDifferenceField::Year, info_y),
606657
(DateGreatestDifferenceField::Day, info_d.clone()),
607658
]),
608659
)
@@ -611,8 +662,9 @@ mod tests {
611662
assert_eq!(
612663
pgd2.patterns
613664
.get(0)
614-
.map(<Pattern as zerofrom::ZeroFrom<PatternULE>>::zero_from),
615-
Some(pattern_d.clone())
665+
.map(<Pattern as zerofrom::ZeroFrom<PatternULE>>::zero_from)
666+
.as_ref(),
667+
Some(info_d.pattern())
616668
);
617669

618670
// Empty
@@ -708,8 +760,8 @@ mod try_from_patterns_tests {
708760
let pat_b = Pattern::from_str("m").unwrap();
709761

710762
let info_a = RangePatternInfo::Symmetric(pat_a.clone());
711-
let info_b = RangePatternInfo::Symmetric(pat_b.clone());
712-
let info_c = RangePatternInfo::FullRange(pat_a.clone());
763+
let info_b = RangePatternInfo::Symmetric(pat_b);
764+
let info_c = RangePatternInfo::FullRange(pat_a);
713765

714766
// 1. All different -> no dedup
715767
let input = vec![
@@ -726,20 +778,26 @@ mod try_from_patterns_tests {
726778
// Header value: 0b00100101 = 0x25
727779
assert_eq!(pgd.header.0, 0x25);
728780
assert_eq!(pgd.patterns.len(), 3); // 1 (Symmetric) + 1 (Symmetric) + 1 (FullRange) = 3
781+
782+
let pgd_ule_box = zerovec::ule::encode_varule_to_box(&pgd);
783+
let pgd: &PatternsByGreatestDifferenceULE = &pgd_ule_box;
729784
assert_eq!(
730785
pgd.get_date_pattern(DateGreatestDifferenceField::Day)
731-
.unwrap(),
732-
info_a
786+
.map(RangePatternInfo::from)
787+
.as_ref(),
788+
Some(&info_a)
733789
);
734790
assert_eq!(
735791
pgd.get_date_pattern(DateGreatestDifferenceField::Month)
736-
.unwrap(),
737-
info_b
792+
.map(RangePatternInfo::from)
793+
.as_ref(),
794+
Some(&info_b)
738795
);
739796
assert_eq!(
740797
pgd.get_date_pattern(DateGreatestDifferenceField::Year)
741-
.unwrap(),
742-
info_c
798+
.map(RangePatternInfo::from)
799+
.as_ref(),
800+
Some(&info_c)
743801
);
744802

745803
// 2. All identical -> dedup to 1 (at the largest field, Year)
@@ -756,21 +814,27 @@ mod try_from_patterns_tests {
756814
// Header value: 0b00010000 = 0x10
757815
assert_eq!(pgd.header.0, 0x10);
758816
assert_eq!(pgd.patterns.len(), 1);
817+
818+
let pgd_ule_box = zerovec::ule::encode_varule_to_box(&pgd);
819+
let pgd: &PatternsByGreatestDifferenceULE = &pgd_ule_box;
759820
// Day and Month should fallback to Year (info_a)
760821
assert_eq!(
761822
pgd.get_date_pattern(DateGreatestDifferenceField::Day)
762-
.unwrap(),
763-
info_a
823+
.map(RangePatternInfo::from)
824+
.as_ref(),
825+
Some(&info_a)
764826
);
765827
assert_eq!(
766828
pgd.get_date_pattern(DateGreatestDifferenceField::Month)
767-
.unwrap(),
768-
info_a
829+
.map(RangePatternInfo::from)
830+
.as_ref(),
831+
Some(&info_a)
769832
);
770833
assert_eq!(
771834
pgd.get_date_pattern(DateGreatestDifferenceField::Year)
772-
.unwrap(),
773-
info_a
835+
.map(RangePatternInfo::from)
836+
.as_ref(),
837+
Some(&info_a)
774838
);
775839

776840
// 3. Day and Month identical, Year different -> dedup Day to Month
@@ -787,21 +851,27 @@ mod try_from_patterns_tests {
787851
// Header value: 0b00010100 = 0x14
788852
assert_eq!(pgd.header.0, 0x14);
789853
assert_eq!(pgd.patterns.len(), 2);
854+
855+
let pgd_ule_box = zerovec::ule::encode_varule_to_box(&pgd);
856+
let pgd: &PatternsByGreatestDifferenceULE = &pgd_ule_box;
790857
// Day should fallback to Month (info_a)
791858
assert_eq!(
792859
pgd.get_date_pattern(DateGreatestDifferenceField::Day)
793-
.unwrap(),
794-
info_a
860+
.map(RangePatternInfo::from)
861+
.as_ref(),
862+
Some(&info_a)
795863
);
796864
assert_eq!(
797865
pgd.get_date_pattern(DateGreatestDifferenceField::Month)
798-
.unwrap(),
799-
info_a
866+
.map(RangePatternInfo::from)
867+
.as_ref(),
868+
Some(&info_a)
800869
);
801870
assert_eq!(
802871
pgd.get_date_pattern(DateGreatestDifferenceField::Year)
803-
.unwrap(),
804-
info_b
872+
.map(RangePatternInfo::from)
873+
.as_ref(),
874+
Some(&info_b)
805875
);
806876

807877
// 4. Day and Year identical, Month different -> no dedup (since Month is in between and different)
@@ -826,11 +896,11 @@ mod try_from_patterns_tests {
826896
assert_eq!(pgd.patterns.len(), 1);
827897

828898
// 6. Duplicate keys -> error
829-
let input = vec![(0, info_a.clone()), (0, info_b.clone())];
899+
let input = vec![(0, info_a.clone()), (0, info_b)];
830900
assert!(PatternsByGreatestDifference::try_from_patterns(input).is_err());
831901

832902
// 7. Out of bound keys -> error
833-
let input = vec![(4, info_a.clone())];
903+
let input = vec![(4, info_a)];
834904
assert!(PatternsByGreatestDifference::try_from_patterns(input).is_err());
835905
}
836906
}

0 commit comments

Comments
 (0)