Skip to main content

icu_provider/baked/
zerotrie.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 stored as as [`ZeroTrieSimpleAscii`]
6
7/// This is a valid separator as `DataLocale` will never produce it.
8///
9/// Mostly for internal use
10pub const ID_SEPARATOR: u8 = 0x1E;
11
12pub use crate::DynamicDataMarker;
13use crate::{
14    prelude::{zerofrom::ZeroFrom, *},
15    ule::MaybeAsVarULE,
16};
17#[cfg(feature = "alloc")]
18use alloc::string::String;
19pub use zerotrie::ZeroTrieSimpleAscii;
20use zerovec::{vecs::Index32, VarZeroSlice};
21
22fn get_index(
23    trie: ZeroTrieSimpleAscii<&'static [u8]>,
24    id: DataIdentifierBorrowed,
25    attributes_prefix_match: bool,
26) -> Option<usize> {
27    use writeable::Writeable;
28    let mut cursor = trie.cursor();
29    let _is_ascii = id.locale.write_to(&mut cursor);
30    if !id.marker_attributes.is_empty() {
31        cursor.step(ID_SEPARATOR);
32        id.marker_attributes.write_to(&mut cursor).ok()?;
33        loop {
34            if let Some(v) = cursor.take_value() {
35                break Some(v);
36            }
37            if !attributes_prefix_match || cursor.probe(0).is_none() {
38                break None;
39            }
40        }
41    } else {
42        cursor.take_value()
43    }
44}
45
46#[cfg(feature = "alloc")]
47#[expect(clippy::type_complexity)]
48fn iter(
49    trie: &'static ZeroTrieSimpleAscii<&'static [u8]>,
50) -> core::iter::FilterMap<
51    zerotrie::ZeroTrieStringIterator<'static>,
52    fn((String, usize)) -> Option<DataIdentifierCow<'static>>,
53> {
54    use alloc::borrow::ToOwned;
55    trie.iter().filter_map(move |(s, _)| {
56        if let Some((locale, attrs)) = s.split_once(ID_SEPARATOR as char) {
57            Some(DataIdentifierCow::from_owned(
58                DataMarkerAttributes::try_from_str(attrs).ok()?.to_owned(),
59                locale.parse().ok()?,
60            ))
61        } else {
62            s.parse().ok().map(DataIdentifierCow::from_locale)
63        }
64    })
65}
66
67/// Regular baked data: a trie for lookups and a slice of values
68#[derive(#[automatically_derived]
impl<M: ::core::fmt::Debug + DataMarker> ::core::fmt::Debug for Data<M> where
    M::DataStruct: ::core::fmt::Debug {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "Data", "trie",
            &self.trie, "values", &&self.values)
    }
}Debug)]
69pub struct Data<M: DataMarker> {
70    // Unsafe invariant: actual values contained MUST be valid indices into `values`
71    trie: ZeroTrieSimpleAscii<&'static [u8]>,
72    values: &'static [M::DataStruct],
73}
74
75impl<M: DataMarker> Data<M> {
76    /// Construct from a trie and values
77    ///
78    /// # Safety
79    /// The actual values contained in the trie must be valid indices into `values`
80    pub const unsafe fn from_trie_and_values_unchecked(
81        trie: ZeroTrieSimpleAscii<&'static [u8]>,
82        values: &'static [M::DataStruct],
83    ) -> Self {
84        Self { trie, values }
85    }
86}
87
88impl<M: DataMarker> super::private::Sealed for Data<M> {}
89impl<M: DataMarker> super::DataStore<M> for Data<M> {
90    fn get(
91        &self,
92        id: DataIdentifierBorrowed,
93        attributes_prefix_match: bool,
94    ) -> Option<DataPayload<M>> {
95        get_index(self.trie, id, attributes_prefix_match)
96            // Safety: Allowed since `i` came from the trie and the field safety invariant
97            .map(|i| unsafe { self.values.get_unchecked(i) })
98            .map(DataPayload::from_static_ref)
99    }
100
101    #[cfg(feature = "alloc")]
102    type IterReturn = core::iter::FilterMap<
103        zerotrie::ZeroTrieStringIterator<'static>,
104        fn((String, usize)) -> Option<DataIdentifierCow<'static>>,
105    >;
106    #[cfg(feature = "alloc")]
107    fn iter(&'static self) -> Self::IterReturn {
108        iter(&self.trie)
109    }
110}
111
112/// Regular baked data: a trie for lookups and a slice of values
113#[derive(#[automatically_derived]
impl<M: ::core::fmt::Debug + DataMarker> ::core::fmt::Debug for DataRef<M>
    where M::DataStruct: ::core::fmt::Debug {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "DataRef",
            "trie", &self.trie, "values", &&self.values)
    }
}Debug)]
114pub struct DataRef<M: DataMarker> {
115    // Unsafe invariant: actual values contained MUST be valid indices into `values`
116    trie: ZeroTrieSimpleAscii<&'static [u8]>,
117    values: &'static [&'static M::DataStruct],
118}
119
120impl<M: DataMarker> DataRef<M> {
121    /// Construct from a trie and references to values
122    ///
123    /// # Safety
124    /// The actual values contained in the trie must be valid indices into `values`
125    pub const unsafe fn from_trie_and_refs_unchecked(
126        trie: ZeroTrieSimpleAscii<&'static [u8]>,
127        values: &'static [&'static M::DataStruct],
128    ) -> Self {
129        Self { trie, values }
130    }
131}
132
133impl<M: DataMarker> super::private::Sealed for DataRef<M> {}
134impl<M: DataMarker> super::DataStore<M> for DataRef<M> {
135    fn get(
136        &self,
137        id: DataIdentifierBorrowed,
138        attributes_prefix_match: bool,
139    ) -> Option<DataPayload<M>> {
140        get_index(self.trie, id, attributes_prefix_match)
141            // Safety: Allowed since `i` came from the trie and the field safety invariant
142            .map(|i| unsafe { self.values.get_unchecked(i) })
143            .copied()
144            .map(DataPayload::from_static_ref)
145    }
146
147    #[cfg(feature = "alloc")]
148    type IterReturn = core::iter::FilterMap<
149        zerotrie::ZeroTrieStringIterator<'static>,
150        fn((String, usize)) -> Option<DataIdentifierCow<'static>>,
151    >;
152    #[cfg(feature = "alloc")]
153    fn iter(&'static self) -> Self::IterReturn {
154        iter(&self.trie)
155    }
156}
157
158/// Optimized data stored as a single [`VarZeroSlice`] to reduce token count
159#[allow(missing_debug_implementations)] // Debug on this will not be too useful
160pub struct DataForVarULEs<M: DataMarker>
161where
162    M::DataStruct: MaybeAsVarULE,
163    M::DataStruct: ZeroFrom<'static, <M::DataStruct as MaybeAsVarULE>::EncodedStruct>,
164{
165    // Unsafe invariant: actual values contained MUST be valid indices into `values`
166    trie: ZeroTrieSimpleAscii<&'static [u8]>,
167    values: &'static VarZeroSlice<<M::DataStruct as MaybeAsVarULE>::EncodedStruct, Index32>,
168}
169
170impl<M: DataMarker> super::private::Sealed for DataForVarULEs<M>
171where
172    M::DataStruct: MaybeAsVarULE,
173    M::DataStruct: ZeroFrom<'static, <M::DataStruct as MaybeAsVarULE>::EncodedStruct>,
174{
175}
176
177impl<M: DataMarker> DataForVarULEs<M>
178where
179    M::DataStruct: MaybeAsVarULE,
180    M::DataStruct: ZeroFrom<'static, <M::DataStruct as MaybeAsVarULE>::EncodedStruct>,
181{
182    /// Construct from a trie and values
183    ///
184    /// # Safety
185    /// The actual values contained in the trie must be valid indices into `values`
186    pub const unsafe fn from_trie_and_values_unchecked(
187        trie: ZeroTrieSimpleAscii<&'static [u8]>,
188        values: &'static VarZeroSlice<<M::DataStruct as MaybeAsVarULE>::EncodedStruct, Index32>,
189    ) -> Self {
190        Self { trie, values }
191    }
192}
193
194impl<M: DataMarker> super::DataStore<M> for DataForVarULEs<M>
195where
196    M::DataStruct: MaybeAsVarULE,
197    M::DataStruct: ZeroFrom<'static, <M::DataStruct as MaybeAsVarULE>::EncodedStruct>,
198{
199    fn get(
200        &self,
201        id: DataIdentifierBorrowed,
202        attributes_prefix_match: bool,
203    ) -> Option<DataPayload<M>> {
204        get_index(self.trie, id, attributes_prefix_match)
205            // Safety: Allowed since `i` came from the trie and the field safety invariant
206            .map(|i| unsafe { self.values.get_unchecked(i) })
207            .map(M::DataStruct::zero_from)
208            .map(DataPayload::from_owned)
209    }
210
211    #[cfg(feature = "alloc")]
212    type IterReturn = core::iter::FilterMap<
213        zerotrie::ZeroTrieStringIterator<'static>,
214        fn((String, usize)) -> Option<DataIdentifierCow<'static>>,
215    >;
216    #[cfg(feature = "alloc")]
217    fn iter(&'static self) -> Self::IterReturn {
218        iter(&self.trie)
219    }
220}