icu_datetime/provider/
packed_pattern.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! Data structures for packing of datetime patterns.
6
7use super::pattern::{
8    runtime::{Pattern, PatternBorrowed, PatternMetadata},
9    PatternItem,
10};
11use crate::{options::Length, size_test_macro::size_test};
12use alloc::vec::Vec;
13use icu_plurals::{
14    provider::{FourBitMetadata, PluralElementsPackedULE},
15    PluralElements,
16};
17use icu_provider::prelude::*;
18use zerovec::{VarZeroVec, ZeroSlice};
19
20/// A field of [`PackedPatternsBuilder`].
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct LengthPluralElements<T> {
23    /// The "long" length pattern plural elements.
24    pub long: PluralElements<T>,
25    /// The "medium" length pattern plural elements.
26    pub medium: PluralElements<T>,
27    /// The "short" length pattern plural elements.
28    pub short: PluralElements<T>,
29}
30
31/// A builder for a [`PackedPatterns`].
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct PackedPatternsBuilder<'a> {
34    /// Patterns always available.
35    pub standard: LengthPluralElements<Pattern<'a>>,
36    /// Patterns for variant 0. If `None`, falls back to standard.
37    pub variant0: Option<LengthPluralElements<Pattern<'a>>>,
38    /// Patterns for variant 1. If `None`, falls back to standard.
39    pub variant1: Option<LengthPluralElements<Pattern<'a>>>,
40}
41
42size_test!(PackedPatterns, packed_skeleton_data_size, 32);
43
44icu_provider::data_marker!(
45    /// `DatetimePatternsDateBuddhistV1`
46    DatetimePatternsDateBuddhistV1,
47    PackedPatterns<'static>
48);
49icu_provider::data_marker!(
50    /// `DatetimePatternsDateChineseV1`
51    DatetimePatternsDateChineseV1,
52    PackedPatterns<'static>
53);
54icu_provider::data_marker!(
55    /// `DatetimePatternsDateCopticV1`
56    DatetimePatternsDateCopticV1,
57    PackedPatterns<'static>
58);
59icu_provider::data_marker!(
60    /// `DatetimePatternsDateDangiV1`
61    DatetimePatternsDateDangiV1,
62    PackedPatterns<'static>
63);
64icu_provider::data_marker!(
65    /// `DatetimePatternsDateEthiopianV1`
66    DatetimePatternsDateEthiopianV1,
67    PackedPatterns<'static>
68);
69icu_provider::data_marker!(
70    /// `DatetimePatternsDateGregorianV1`
71    DatetimePatternsDateGregorianV1,
72    PackedPatterns<'static>
73);
74icu_provider::data_marker!(
75    /// `DatetimePatternsDateHebrewV1`
76    DatetimePatternsDateHebrewV1,
77    PackedPatterns<'static>
78);
79icu_provider::data_marker!(
80    /// `DatetimePatternsDateIndianV1`
81    DatetimePatternsDateIndianV1,
82    PackedPatterns<'static>
83);
84icu_provider::data_marker!(
85    /// `DatetimePatternsDateHijriV1`
86    DatetimePatternsDateHijriV1,
87    PackedPatterns<'static>
88);
89icu_provider::data_marker!(
90    /// `DatetimePatternsDateJapaneseV1`
91    DatetimePatternsDateJapaneseV1,
92    PackedPatterns<'static>
93);
94icu_provider::data_marker!(
95    /// `DatetimePatternsDateJapanextV1`
96    DatetimePatternsDateJapanextV1,
97    PackedPatterns<'static>
98);
99icu_provider::data_marker!(
100    /// `DatetimePatternsDatePersianV1`
101    DatetimePatternsDatePersianV1,
102    PackedPatterns<'static>
103);
104icu_provider::data_marker!(
105    /// `DatetimePatternsDateRocV1`
106    DatetimePatternsDateRocV1,
107    PackedPatterns<'static>
108);
109icu_provider::data_marker!(
110    /// `DatetimePatternsTimeV1`
111    DatetimePatternsTimeV1,
112    PackedPatterns<'static>
113);
114
115// Main data struct for packed datetime patterns.
116#[doc = packed_skeleton_data_size!()]
117///
118/// ## Variants
119///
120/// This supports a set of "standard" patterns plus up to two "variants".
121/// The variants are currently used by year formatting:
122///
123/// - Standard: Year, which could be partial precision (2-digit Gregorian)
124/// - Variant 0: Full Year, which is always full precision
125/// - Variant 1: Year With Era
126///
127/// And by time formatting:
128///
129/// - Standard: Hour only
130/// - Variant 0: Hour and minute
131/// - Variant 1: Hour, minute, and second
132///
133/// Variants should be used when the pattern could depend on the value being
134/// formatted. For example, with [`YearStyle::Auto`], any of these three
135/// patterns could be selected based on the year value.
136///
137/// ## Representation
138///
139/// Currently, there are at most 9 patterns that need to be stored together,
140/// named according to this table:
141///
142/// |        | Standard | Variant 0 | Variant 1 |
143/// |--------|----------|-----------|-----------|
144/// | Long   | La       | Lb        | Lc        |
145/// | Medium | Ma       | Mb        | Mc        |
146/// | Short  | Sa       | Sb        | Sc        |
147///
148/// The header byte encodes which pattern in the patterns array corresponds to
149/// a particular cell in the table. It contains the following information:
150///
151/// - Bits 0-1: "LMS" value of the standard column
152/// - Bit 2: "Q" value: 1 for directly-indexed variants; 0 for per-cell offsets
153/// - Bits 3-20: Packed offset into patterns table for each variant cell
154/// - Bits 21-31: unused/reserved
155///
156/// The LMS value determines which pattern index is used for the first column:
157///
158/// | LMS Value   | Long Index | Medium Index | Short Index |
159/// |-------------|------------|--------------|-------------|
160/// | 0 (L=M=S)   | 0          | 0            | 0           |
161/// | 1 (L, M=S)  | 0          | 1            | 1           |
162/// | 2 (L=M, S)  | 0          | 0            | 1           |
163/// | 3 (L, M, S) | 0          | 1            | 2           |
164///
165/// If bit 2 is 1 (Q=1), it means there is one pattern per table cell,
166/// with the index offset by the short index `S` from the table above.
167/// However, this requires storing multiple, possibly duplicate, patterns in
168/// the packed structure. The more common case is Q=0 and then to store
169/// per-cell offsets in chunks of 3 bits per cell:
170///
171/// - Chunk = 0: Inherit according to the table below
172/// - Chunk = 1-7: Use pattern index Chunk - 1
173///
174/// This is summarized below:
175///
176/// | Cell in Table | Q=1 Pattern Index | Q=0 Header Bits | Inheritance |
177/// |---------------|-------------------|-----------------|-------------|
178/// | Lb            | S + 1             | 3-5             | La          |
179/// | Mb            | S + 2             | 6-8             | Ma          |
180/// | Sb            | S + 3             | 9-11            | Sa          |
181/// | Lc            | S + 4             | 12-14           | La          |
182/// | Mc            | S + 5             | 15-17           | Ma          |
183/// | Sc            | S + 6             | 18-20           | Sa          |
184///
185/// As a result, if there are no variants, bits 2 and higher will be all zero,
186/// making the header int suitable for varint packing, such as that used by
187/// postcard and other size-optimized serialization formats.
188///
189/// [`YearStyle::Auto`]: crate::options::YearStyle::Auto
190#[derive(Debug, PartialEq, Eq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
191#[cfg_attr(feature = "datagen", derive(databake::Bake))]
192#[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider))]
193pub struct PackedPatterns<'data> {
194    /// An encoding of which standard/variant cell corresponds to which entry
195    /// in the patterns table. See class docs.
196    pub header: u32,
197    /// The list of patterns. Length should be between 1 and 9,
198    /// depending on the header.
199    pub elements: VarZeroVec<'data, PluralElementsPackedULE<ZeroSlice<PatternItem>>>,
200}
201
202icu_provider::data_struct!(
203    PackedPatterns<'_>,
204    #[cfg(feature = "datagen")]
205);
206
207mod constants {
208    /// Value when standard long, medium, and short are all the same
209    pub(super) const LMS: u32 = 0;
210    /// Value when standard medium is the same as short but not long
211    pub(super) const L_MS: u32 = 1;
212    /// Value when standard medium is the same as long but not short
213    pub(super) const LM_S: u32 = 2;
214    /// Bit that indicates that standard medium differs from standard long
215    pub(super) const M_DIFFERS: u32 = 0x1;
216    /// Bit that indicates that standard short differs from standard medium
217    pub(super) const S_DIFFERS: u32 = 0x2;
218    /// Bitmask over all LMS values
219    pub(super) const LMS_MASK: u32 = 0x3;
220    /// Bit that indicates whether there are per-cell chunks
221    pub(super) const Q_BIT: u32 = 0x4;
222    /// A mask applied to individual chunks (the largest possible chunk)
223    pub(super) const CHUNK_MASK: u32 = 0x7;
224}
225
226struct UnpackedPatterns<'a> {
227    pub(super) has_explicit_medium: bool,
228    pub(super) has_explicit_short: bool,
229    pub(super) variant_indices: VariantIndices,
230    pub(super) elements: Vec<PluralElements<Pattern<'a>>>,
231}
232
233#[repr(u8)]
234#[derive(Copy, Clone)]
235enum VariantPatternIndex {
236    Inherit = 0,
237    I0 = 1,
238    I1 = 2,
239    I2 = 3,
240    I3 = 4,
241    I4 = 5,
242    I5 = 6,
243    I6 = 7,
244}
245
246impl VariantPatternIndex {
247    #[cfg(feature = "datagen")]
248    pub(super) fn from_header_with_shift(header: u32, shift: u32) -> Self {
249        match Self::try_from_u32((header >> shift) & constants::CHUNK_MASK) {
250            Some(x) => x,
251            None => {
252                debug_assert!(false, "unreachable");
253                Self::Inherit
254            }
255        }
256    }
257
258    fn try_from_u32(u: u32) -> Option<Self> {
259        match u {
260            0 => Some(Self::Inherit),
261            1 => Some(Self::I0),
262            2 => Some(Self::I1),
263            3 => Some(Self::I2),
264            4 => Some(Self::I3),
265            5 => Some(Self::I4),
266            6 => Some(Self::I5),
267            7 => Some(Self::I6),
268            _ => None,
269        }
270    }
271
272    pub(super) fn try_from_chunks_u32(chunks: [u32; 6]) -> Option<[Self; 6]> {
273        let [c0, c1, c2, c3, c4, c5] = chunks;
274        Some([
275            Self::try_from_u32(c0)?,
276            Self::try_from_u32(c1)?,
277            Self::try_from_u32(c2)?,
278            Self::try_from_u32(c3)?,
279            Self::try_from_u32(c4)?,
280            Self::try_from_u32(c5)?,
281        ])
282    }
283
284    pub(super) fn to_chunks_u32(chunks: [Self; 6]) -> [u32; 6] {
285        let [c0, c1, c2, c3, c4, c5] = chunks;
286        [
287            c0 as u32, c1 as u32, c2 as u32, c3 as u32, c4 as u32, c5 as u32,
288        ]
289    }
290}
291
292enum VariantIndices {
293    OnePatternPerVariant,
294    IndicesPerVariant([VariantPatternIndex; 6]),
295}
296
297impl<'a> UnpackedPatterns<'a> {
298    pub(super) fn build(&self) -> PackedPatterns<'static> {
299        let mut header = 0u32;
300        if self.has_explicit_medium {
301            header |= constants::M_DIFFERS;
302        }
303        if self.has_explicit_short {
304            header |= constants::S_DIFFERS;
305        }
306        match self.variant_indices {
307            VariantIndices::OnePatternPerVariant => {
308                header |= constants::Q_BIT;
309            }
310            VariantIndices::IndicesPerVariant(chunks) => {
311                let mut shift = 3;
312                for chunk_u32 in VariantPatternIndex::to_chunks_u32(chunks).iter() {
313                    debug_assert!(*chunk_u32 <= constants::CHUNK_MASK);
314                    header |= *chunk_u32 << shift;
315                    shift += 3;
316                }
317            }
318        }
319        let elements: Vec<PluralElements<(FourBitMetadata, &ZeroSlice<PatternItem>)>> = self
320            .elements
321            .iter()
322            .map(|plural_elements| {
323                plural_elements.as_ref().map(|pattern| {
324                    (
325                        pattern.metadata.to_four_bit_metadata(),
326                        pattern.items.as_slice(),
327                    )
328                })
329            })
330            .collect();
331        PackedPatterns {
332            header,
333            elements: elements.as_slice().into(),
334        }
335    }
336
337    #[cfg(feature = "datagen")]
338    pub(super) fn from_packed(packed: &'a PackedPatterns<'_>) -> Self {
339        let variant_indices = if (packed.header & constants::Q_BIT) != 0 {
340            VariantIndices::OnePatternPerVariant
341        } else {
342            VariantIndices::IndicesPerVariant([
343                VariantPatternIndex::from_header_with_shift(packed.header, 3),
344                VariantPatternIndex::from_header_with_shift(packed.header, 6),
345                VariantPatternIndex::from_header_with_shift(packed.header, 9),
346                VariantPatternIndex::from_header_with_shift(packed.header, 12),
347                VariantPatternIndex::from_header_with_shift(packed.header, 15),
348                VariantPatternIndex::from_header_with_shift(packed.header, 18),
349            ])
350        };
351        let elements = packed
352            .elements
353            .iter()
354            .map(|plural_elements| {
355                plural_elements.decode().map(|(metadata, items)| {
356                    PatternBorrowed {
357                        metadata: PatternMetadata::from_u8(metadata.get()),
358                        items,
359                    }
360                    .as_pattern()
361                })
362            })
363            .collect();
364        Self {
365            has_explicit_medium: (packed.header & constants::M_DIFFERS) != 0,
366            has_explicit_short: (packed.header & constants::S_DIFFERS) != 0,
367            variant_indices,
368            elements,
369        }
370    }
371}
372
373impl PackedPatternsBuilder<'_> {
374    /// Builds a packed pattern representation from the builder.
375    pub fn build(mut self) -> PackedPatterns<'static> {
376        self.simplify();
377
378        // Initialize the elements vector with the standard patterns.
379        let mut elements = Vec::new();
380        let mut has_explicit_medium = false;
381        let mut has_explicit_short = false;
382        elements.push(self.standard.long.as_ref().map(Pattern::as_ref));
383        let mut s_offset = 0;
384        if self.standard.medium != self.standard.long {
385            elements.push(self.standard.medium.as_ref().map(Pattern::as_ref));
386            has_explicit_medium = true;
387            s_offset += 1;
388        }
389        if self.standard.short != self.standard.medium {
390            elements.push(self.standard.short.as_ref().map(Pattern::as_ref));
391            has_explicit_short = true;
392            s_offset += 1;
393        }
394
395        // Fill in the variant patterns
396        let variant_patterns = [
397            self.variant0.as_ref().map(|v| &v.long),
398            self.variant0.as_ref().map(|v| &v.medium),
399            self.variant0.as_ref().map(|v| &v.short),
400            self.variant1.as_ref().map(|v| &v.long),
401            self.variant1.as_ref().map(|v| &v.medium),
402            self.variant1.as_ref().map(|v| &v.short),
403        ];
404        let fallbacks = [
405            &self.standard.long,
406            &self.standard.medium,
407            &self.standard.short,
408            &self.standard.long,
409            &self.standard.medium,
410            &self.standard.short,
411        ];
412        let mut chunks = [0u32; 6]; // per-cell chunk values
413        for ((pattern, fallback), chunk) in variant_patterns
414            .iter()
415            .zip(fallbacks.iter())
416            .zip(chunks.iter_mut())
417        {
418            if let Some(pattern) = pattern {
419                if pattern != fallback {
420                    *chunk = match elements.iter().position(|p| p == *pattern) {
421                        Some(i) => i as u32 + 1,
422                        None => {
423                            elements.push(pattern.as_ref().map(Pattern::as_ref));
424                            elements.len() as u32
425                        }
426                    }
427                }
428            }
429        }
430
431        // Check to see if we need to switch to Q=1 mode. We need to do this
432        // if any of the calculated chunk values is too big (larger than 7).
433        let variant_indices = if let Some(chunks) = VariantPatternIndex::try_from_chunks_u32(chunks)
434        {
435            // per-cell offsets
436            VariantIndices::IndicesPerVariant(chunks)
437        } else {
438            // one pattern per table cell
439            elements.truncate(s_offset + 1);
440            elements.extend(variant_patterns.into_iter().zip(fallbacks.iter()).map(
441                |(pattern, fallback)| pattern.unwrap_or(fallback).as_ref().map(Pattern::as_ref),
442            ));
443            VariantIndices::OnePatternPerVariant
444        };
445
446        // Now we can build the data representation
447        let unpacked = UnpackedPatterns {
448            has_explicit_medium,
449            has_explicit_short,
450            variant_indices,
451            elements,
452        };
453        unpacked.build()
454    }
455
456    fn simplify(&mut self) {
457        if self.variant0.as_ref() == Some(&self.standard) {
458            self.variant0 = None;
459        }
460        if self.variant1.as_ref() == Some(&self.standard) {
461            self.variant1 = None;
462        }
463    }
464}
465
466/// Which pattern to select. For details, see [`PackedPatterns`].
467pub(crate) enum PackedSkeletonVariant {
468    /// Default-precision year OR hours only
469    Standard,
470    /// Full-precision year OR hours and minutes
471    Variant0,
472    /// Year with era OR hours, minutes, and seconds
473    Variant1,
474}
475
476impl PackedPatterns<'_> {
477    pub(crate) fn get(&self, length: Length, variant: PackedSkeletonVariant) -> PatternBorrowed {
478        use Length::*;
479        use PackedSkeletonVariant::*;
480        let lms = self.header & constants::LMS_MASK;
481        let pattern_index = if matches!(variant, Standard) {
482            // Standard pattern (first column)
483            match (length, lms) {
484                (Long, _) => 0,
485                (Medium, constants::LMS | constants::LM_S) => 0,
486                (Medium, _) => 1,
487                (Short, constants::LMS) => 0,
488                (Short, constants::L_MS | constants::LM_S) => 1,
489                (Short, _) => 2,
490            }
491        } else {
492            let s_offset = match lms {
493                constants::LMS => 0,
494                constants::L_MS | constants::LM_S => 1,
495                _ => 2,
496            };
497            let q = self.header & constants::Q_BIT;
498            if q == 0 {
499                // per-cell offsets
500                let chunk_in_low_bits = match (length, variant) {
501                    (Long, Variant0) => self.header >> 3,
502                    (Medium, Variant0) => self.header >> 6,
503                    (Short, Variant0) => self.header >> 9,
504                    (Long, Variant1) => self.header >> 12,
505                    (Medium, Variant1) => self.header >> 15,
506                    (Short, Variant1) => self.header >> 18,
507                    (_, Standard) => {
508                        debug_assert!(false, "unreachable");
509                        return PatternBorrowed::DEFAULT;
510                    }
511                };
512                let chunk = chunk_in_low_bits & constants::CHUNK_MASK;
513                if chunk == 0 {
514                    // Fall back to standard with the same length
515                    return self.get(length, Standard);
516                }
517                chunk - 1
518            } else {
519                // one pattern per table cell
520                let additional_offset = match (length, variant) {
521                    (Long, Variant0) => 1,
522                    (Medium, Variant0) => 2,
523                    (Short, Variant0) => 3,
524                    (Long, Variant1) => 4,
525                    (Medium, Variant1) => 5,
526                    (Short, Variant1) => 6,
527                    (_, Standard) => {
528                        debug_assert!(false, "unreachable");
529                        return PatternBorrowed::DEFAULT;
530                    }
531                };
532                s_offset + additional_offset
533            }
534        };
535        let Some(plural_elements) = self.elements.get(pattern_index as usize) else {
536            debug_assert!(false, "unreachable");
537            return PatternBorrowed::DEFAULT;
538        };
539        let (metadata, items) = plural_elements.get_default();
540        PatternBorrowed {
541            metadata: PatternMetadata::from_u8(metadata.get()),
542            items,
543        }
544    }
545
546    fn get_as_plural_elements(
547        &self,
548        length: Length,
549        variant: PackedSkeletonVariant,
550    ) -> PluralElements<Pattern> {
551        PluralElements::new(self.get(length, variant).as_pattern())
552    }
553
554    /// Converts this packed data to a builder that can be mutated.
555    pub fn to_builder(&self) -> PackedPatternsBuilder {
556        use Length::*;
557        use PackedSkeletonVariant::*;
558        let mut builder = PackedPatternsBuilder {
559            standard: LengthPluralElements {
560                long: self.get_as_plural_elements(Long, Standard),
561                medium: self.get_as_plural_elements(Medium, Standard),
562                short: self.get_as_plural_elements(Short, Standard),
563            },
564            variant0: Some(LengthPluralElements {
565                long: self.get_as_plural_elements(Long, Variant0),
566                medium: self.get_as_plural_elements(Medium, Variant0),
567                short: self.get_as_plural_elements(Short, Variant0),
568            }),
569            variant1: Some(LengthPluralElements {
570                long: self.get_as_plural_elements(Long, Variant1),
571                medium: self.get_as_plural_elements(Medium, Variant1),
572                short: self.get_as_plural_elements(Short, Variant1),
573            }),
574        };
575        builder.simplify();
576        builder
577    }
578}
579
580#[cfg(feature = "serde")]
581mod _serde {
582    use super::*;
583    use crate::provider::pattern::reference;
584    use zerovec::VarZeroSlice;
585
586    #[cfg_attr(feature = "serde", derive(serde::Deserialize))]
587    #[cfg_attr(feature = "datagen", derive(serde::Serialize))]
588    struct PackedPatternsMachine<'data> {
589        pub header: u32,
590        #[serde(borrow)]
591        pub elements: &'data VarZeroSlice<PluralElementsPackedULE<ZeroSlice<PatternItem>>>,
592    }
593
594    #[cfg_attr(feature = "serde", derive(serde::Deserialize))]
595    #[cfg_attr(feature = "datagen", derive(serde::Serialize))]
596    #[derive(Default)]
597    struct PackedPatternsHuman {
598        #[cfg_attr(
599            feature = "serde",
600            serde(default, skip_serializing_if = "core::ops::Not::not")
601        )]
602        pub(super) has_explicit_medium: bool,
603        #[cfg_attr(
604            feature = "serde",
605            serde(default, skip_serializing_if = "core::ops::Not::not")
606        )]
607        pub(super) has_explicit_short: bool,
608        #[cfg_attr(
609            feature = "serde",
610            serde(default, skip_serializing_if = "core::ops::Not::not")
611        )]
612        pub(super) has_one_pattern_per_variant: bool,
613        #[cfg_attr(
614            feature = "serde",
615            serde(default, skip_serializing_if = "Option::is_none")
616        )]
617        pub(super) variant_pattern_indices: Option<[u32; 6]>,
618        pub(super) elements: Vec<reference::Pattern>,
619    }
620
621    impl<'de, 'data> serde::Deserialize<'de> for PackedPatterns<'data>
622    where
623        'de: 'data,
624    {
625        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
626        where
627            D: serde::Deserializer<'de>,
628        {
629            use serde::de::Error as _;
630            if deserializer.is_human_readable() {
631                let human = <PackedPatternsHuman>::deserialize(deserializer)?;
632                let variant_indices = match (
633                    human.has_one_pattern_per_variant,
634                    human.variant_pattern_indices,
635                ) {
636                    (true, None) => VariantIndices::OnePatternPerVariant,
637                    (false, Some(chunks)) => VariantIndices::IndicesPerVariant(
638                        VariantPatternIndex::try_from_chunks_u32(chunks).ok_or_else(|| {
639                            D::Error::custom("variant pattern index out of range")
640                        })?,
641                    ),
642                    _ => {
643                        return Err(D::Error::custom(
644                            "must have either one pattern per variant or indices",
645                        ))
646                    }
647                };
648                let elements = human
649                    .elements
650                    .iter()
651                    .map(|pattern| PluralElements::new(pattern.to_runtime_pattern()))
652                    .collect();
653                let unpacked = UnpackedPatterns {
654                    has_explicit_medium: human.has_explicit_medium,
655                    has_explicit_short: human.has_explicit_short,
656                    variant_indices,
657                    elements,
658                };
659                Ok(unpacked.build())
660            } else {
661                let machine = <PackedPatternsMachine>::deserialize(deserializer)?;
662                Ok(Self {
663                    header: machine.header,
664                    elements: machine.elements.as_varzerovec(),
665                })
666            }
667        }
668    }
669
670    #[cfg(feature = "datagen")]
671    impl serde::Serialize for PackedPatterns<'_> {
672        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
673        where
674            S: serde::Serializer,
675        {
676            use serde::ser::Error as _;
677            if serializer.is_human_readable() {
678                let unpacked = UnpackedPatterns::from_packed(self);
679                let mut human = PackedPatternsHuman {
680                    has_explicit_medium: unpacked.has_explicit_medium,
681                    has_explicit_short: unpacked.has_explicit_short,
682                    ..Default::default()
683                };
684                match unpacked.variant_indices {
685                    VariantIndices::OnePatternPerVariant => {
686                        human.has_one_pattern_per_variant = true;
687                    }
688                    VariantIndices::IndicesPerVariant(chunks) => {
689                        let chunks = VariantPatternIndex::to_chunks_u32(chunks);
690                        human.variant_pattern_indices = Some(chunks);
691                    }
692                }
693                human.elements = Vec::with_capacity(unpacked.elements.len());
694                for pattern_elements in unpacked.elements.into_iter() {
695                    let pattern = pattern_elements
696                        .try_into_other()
697                        .ok_or_else(|| S::Error::custom("cannot yet serialize plural patterns"))?;
698                    human.elements.push(reference::Pattern::from(&pattern));
699                }
700                human.serialize(serializer)
701            } else {
702                let machine = PackedPatternsMachine {
703                    header: self.header,
704                    elements: &self.elements,
705                };
706                machine.serialize(serializer)
707            }
708        }
709    }
710}
711
712#[cfg(test)]
713mod tests {
714    use super::*;
715    use crate::provider::pattern::reference;
716
717    const PATTERN_STRS: &[&str] = &[
718        "M/d/y",
719        "HH:mm",
720        "MMM d y G",
721        "E",
722        "E MMM d",
723        "dd.MM.yy",
724        "h a",
725        "hh:mm:ss B",
726        "y MMMM",
727    ];
728
729    fn get_patterns() -> Vec<Pattern<'static>> {
730        PATTERN_STRS
731            .iter()
732            .map(|s| {
733                s.parse::<reference::Pattern>()
734                    .unwrap()
735                    .to_runtime_pattern()
736            })
737            .collect::<Vec<_>>()
738    }
739
740    #[test]
741    fn test_basic() {
742        let patterns = get_patterns();
743        let mut it = patterns.iter().cloned();
744        let lms0 = LengthPluralElements {
745            long: PluralElements::new(it.next().unwrap()),
746            medium: PluralElements::new(it.next().unwrap()),
747            short: PluralElements::new(it.next().unwrap()),
748        };
749        let lms1 = LengthPluralElements {
750            long: PluralElements::new(it.next().unwrap()),
751            medium: PluralElements::new(it.next().unwrap()),
752            short: PluralElements::new(it.next().unwrap()),
753        };
754        let lms2 = LengthPluralElements {
755            long: PluralElements::new(it.next().unwrap()),
756            medium: PluralElements::new(it.next().unwrap()),
757            short: PluralElements::new(it.next().unwrap()),
758        };
759        let lms0a = LengthPluralElements {
760            long: PluralElements::new(patterns[0].clone()),
761            medium: PluralElements::new(patterns[0].clone()),
762            short: PluralElements::new(patterns[1].clone()),
763        };
764        let lms1a = LengthPluralElements {
765            long: PluralElements::new(patterns[3].clone()),
766            medium: PluralElements::new(patterns[4].clone()),
767            short: PluralElements::new(patterns[4].clone()),
768        };
769
770        {
771            // Q = 1
772            let builder = PackedPatternsBuilder {
773                standard: lms0.clone(),
774                variant0: Some(lms1.clone()),
775                variant1: Some(lms2.clone()),
776            };
777            let packed = builder.clone().build();
778            assert_eq!(packed.header, 7);
779            assert_eq!(packed.elements.len(), 9);
780            for (pattern_elements, expected) in packed.elements.iter().zip(patterns.iter()) {
781                assert_eq!(pattern_elements.get_default().1, &expected.items);
782            }
783            let recovered_builder = packed.to_builder();
784            assert_eq!(builder, recovered_builder);
785        }
786        {
787            // Q = 0
788            let builder = PackedPatternsBuilder {
789                standard: lms0.clone(),
790                variant0: Some(lms0.clone()),
791                variant1: Some(lms2.clone()),
792            };
793            let packed = builder.clone().build();
794            assert_eq!(packed.header, 0x1AC003);
795            assert_eq!(packed.elements.len(), 6);
796            let recovered_builder = packed.to_builder();
797            assert_ne!(builder, recovered_builder);
798            let mut builder = builder;
799            builder.simplify();
800            assert_eq!(builder, recovered_builder);
801        }
802        {
803            // No variants
804            let builder = PackedPatternsBuilder {
805                standard: lms0.clone(),
806                variant0: None,
807                variant1: None,
808            };
809            let packed = builder.clone().build();
810            assert_eq!(packed.header, 3);
811            assert_eq!(packed.elements.len(), 3);
812            let recovered_builder = packed.to_builder();
813            assert_eq!(builder, recovered_builder);
814        }
815        {
816            // Some duplicate patterns and inheritance
817            let builder = PackedPatternsBuilder {
818                standard: lms0a.clone(),
819                variant0: Some(lms0.clone()),
820                variant1: Some(lms1.clone()),
821            };
822            let packed = builder.clone().build();
823            assert_eq!(packed.header, 0x1AC682);
824            assert_eq!(packed.elements.len(), 6);
825            let recovered_builder = packed.to_builder();
826            assert_eq!(builder, recovered_builder);
827        }
828        {
829            // Q = 1 with 8 patterns (min for Q = 1)
830            let builder = PackedPatternsBuilder {
831                standard: lms0a.clone(),
832                variant0: Some(lms1.clone()),
833                variant1: Some(lms2.clone()),
834            };
835            let packed = builder.clone().build();
836            assert_eq!(packed.header, 6);
837            assert_eq!(packed.elements.len(), 8);
838            let recovered_builder = packed.to_builder();
839            assert_eq!(builder, recovered_builder);
840        }
841        {
842            // Q = 0 with 7 patterns (max for Q = 0)
843            let builder = PackedPatternsBuilder {
844                standard: lms1a.clone(),
845                variant0: Some(lms0a.clone()),
846                variant1: Some(lms2.clone()),
847            };
848            let packed = builder.clone().build();
849            assert_eq!(packed.header, 0x1F58D9);
850            assert_eq!(packed.elements.len(), 7);
851            let recovered_builder = packed.to_builder();
852            assert_eq!(builder, recovered_builder);
853        }
854    }
855
856    #[cfg(feature = "datagen")]
857    #[test]
858    fn test_serde() {
859        let patterns = get_patterns();
860        let lms0a = LengthPluralElements {
861            long: PluralElements::new(patterns[0].clone()),
862            medium: PluralElements::new(patterns[0].clone()),
863            short: PluralElements::new(patterns[1].clone()),
864        };
865        let lms1 = LengthPluralElements {
866            long: PluralElements::new(patterns[3].clone()),
867            medium: PluralElements::new(patterns[4].clone()),
868            short: PluralElements::new(patterns[5].clone()),
869        };
870
871        let builder = PackedPatternsBuilder {
872            standard: lms0a,
873            variant0: Some(lms1),
874            variant1: None,
875        };
876        let packed = builder.clone().build();
877
878        let bincode_bytes = bincode::serialize(&packed).unwrap();
879        assert_eq!(
880            bincode_bytes.as_slice(),
881            &[
882                26, 11, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 5, 0, 16, 0, 26, 0, 30, 0, 46, 0, 0, 128,
883                32, 1, 0, 0, 47, 128, 64, 1, 0, 0, 47, 128, 16, 1, 2, 128, 114, 2, 0, 0, 58, 128,
884                128, 2, 0, 128, 80, 1, 0, 128, 80, 1, 0, 0, 32, 128, 32, 3, 0, 0, 32, 128, 64, 1,
885                0, 128, 64, 2, 0, 0, 46, 128, 32, 2, 0, 0, 46, 128, 16, 2
886            ][..]
887        );
888        let bincode_recovered = bincode::deserialize::<PackedPatterns>(&bincode_bytes).unwrap();
889        assert_eq!(builder, bincode_recovered.to_builder());
890
891        let json_str = serde_json::to_string(&packed).unwrap();
892        assert_eq!(json_str, "{\"has_explicit_short\":true,\"variant_pattern_indices\":[3,4,5,0,0,0],\"elements\":[\"M/d/y\",\"HH:mm\",\"E\",\"E MMM d\",\"dd.MM.yy\"]}");
893        let json_recovered = serde_json::from_str::<PackedPatterns>(&json_str).unwrap();
894        assert_eq!(builder, json_recovered.to_builder());
895    }
896}