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