• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zbraniecki / icu4x / 6815798908

09 Nov 2023 05:17PM UTC coverage: 72.607% (-2.4%) from 75.01%
6815798908

push

github

web-flow
Implement `Any/BufferProvider` for some smart pointers (#4255)

Allows storing them as a `Box<dyn Any/BufferProvider>` without using a
wrapper type that implements the trait.

44281 of 60987 relevant lines covered (72.61%)

201375.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.9
/components/collections/src/codepointtrie/cptrie.rs
1
// This file is part of ICU4X. For terms of use, please see the file
3,998✔
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
use crate::codepointtrie::error::Error;
6
use crate::codepointtrie::impl_const::*;
7

8
use crate::codepointinvlist::CodePointInversionList;
9
use core::char::CharTryFromError;
10
use core::convert::Infallible;
11
use core::convert::TryFrom;
12
use core::fmt::Display;
13
use core::iter::FromIterator;
14
use core::num::TryFromIntError;
15
use core::ops::RangeInclusive;
16
use yoke::Yokeable;
17
use zerofrom::ZeroFrom;
18
use zerovec::ZeroVec;
19
use zerovec::ZeroVecError;
20

21
/// The type of trie represents whether the trie has an optimization that
22
/// would make it smaller or faster.
23
///
24
/// Regarding performance, a trie being a small or fast type affects the number of array lookups
25
/// needed for code points in the range `[0x1000, 0x10000)`. In this range, `Small` tries use 4 array lookups,
26
/// while `Fast` tries use 2 array lookups.
27
/// Code points before the interval (in `[0, 0x1000)`) will always use 2 array lookups.
28
/// Code points after the interval (in `[0x10000, 0x10FFFF]`) will always use 4 array lookups.
29
///
30
/// Regarding size, `Fast` type tries are larger than `Small` type tries because the minimum size of
31
/// the index array is larger. The minimum size is the "fast max" limit, which is the limit of the range
32
/// of code points with 2 array lookups.
33
///
34
/// See the document [Unicode Properties and Code Point Tries in ICU4X](https://github.com/unicode-org/icu4x/blob/main/docs/design/properties_code_point_trie.md).
35
///
36
/// Also see [`UCPTrieType`](https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/ucptrie_8h.html) in ICU4C.
37
#[derive(Clone, Copy, PartialEq, Debug, Eq)]
70,716,266✔
38
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
5✔
39
#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = icu_collections::codepointtrie))]
1✔
40
pub enum TrieType {
1✔
41
    /// Represents the "fast" type code point tries for the
42
    /// [`TrieType`] trait. The "fast max" limit is set to `0xffff`.
43
    Fast = 0,
44
    /// Represents the "small" type code point tries for the
45
    /// [`TrieType`] trait. The "fast max" limit is set to `0x0fff`.
46
    Small = 1,
47
}
48

49
// TrieValue trait
50

51
// AsULE is AsUnalignedLittleEndian, i.e. "allowed in a zerovec"
52

53
/// A trait representing the values stored in the data array of a [`CodePointTrie`].
54
/// This trait is used as a type parameter in constructing a `CodePointTrie`.
55
pub trait TrieValue: Copy + Eq + PartialEq + zerovec::ule::AsULE + 'static {
56
    /// Last-resort fallback value to return if we cannot read data from the trie.
57
    ///
58
    /// In most cases, the error value is read from the last element of the `data` array,
59
    /// this value is used for empty codepointtrie arrays
60

61
    /// Error type when converting from a u32 to this TrieValue.
62
    type TryFromU32Error: Display;
63
    /// A parsing function that is primarily motivated by deserialization contexts.
64
    /// When the serialization type width is smaller than 32 bits, then it is expected
65
    /// that the call site will widen the value to a `u32` first.
66
    fn try_from_u32(i: u32) -> Result<Self, Self::TryFromU32Error>;
67

68
    /// A method for converting back to a u32 that can roundtrip through
69
    /// [`Self::try_from_u32()`]. The default implementation of this trait
70
    /// method panics in debug mode and returns 0 in release mode.
71
    ///
72
    /// This method is allowed to have GIGO behavior when fed a value that has
73
    /// no corresponding u32 (since such values cannot be stored in the trie)
74
    fn to_u32(self) -> u32 {
×
75
        debug_assert!(
×
76
            false,
77
            "TrieValue::to_u32() not implemented for {}",
78
            ::core::any::type_name::<Self>()
×
79
        );
80
        0
81
    }
82
}
83

84
macro_rules! impl_primitive_trie_value {
85
    ($primitive:ty, $error:ty) => {
86
        impl TrieValue for $primitive {
87
            type TryFromU32Error = $error;
88
            fn try_from_u32(i: u32) -> Result<Self, Self::TryFromU32Error> {
21,560✔
89
                Self::try_from(i)
21,560✔
90
            }
21,560✔
91

92
            fn to_u32(self) -> u32 {
×
93
                // bitcast when the same size, zero-extend/sign-extend
94
                // when not the same size
95
                self as u32
×
96
            }
×
97
        }
98
    };
99
}
100

101
impl_primitive_trie_value!(u8, TryFromIntError);
102
impl_primitive_trie_value!(u16, TryFromIntError);
103
impl_primitive_trie_value!(u32, Infallible);
104
impl_primitive_trie_value!(i8, TryFromIntError);
105
impl_primitive_trie_value!(i16, TryFromIntError);
106
impl_primitive_trie_value!(i32, TryFromIntError);
107
impl_primitive_trie_value!(char, CharTryFromError);
108

109
/// Helper function used by [`get_range`]. Converts occurrences of trie's null
110
/// value into the provided null_value.
111
///
112
/// Note: the ICU version of this helper function uses a ValueFilter function
113
/// to apply a transform on a non-null value. But currently, this implementation
114
/// stops short of that functionality, and instead leaves the non-null trie value
115
/// untouched. This is equivalent to having a ValueFilter function that is the
116
/// identity function.
117
fn maybe_filter_value<T: TrieValue>(value: T, trie_null_value: T, null_value: T) -> T {
1,703,007✔
118
    if value == trie_null_value {
1,703,007✔
119
        null_value
322,981✔
120
    } else {
121
        value
1,380,026✔
122
    }
123
}
1,703,007✔
124

125
/// This struct represents a de-serialized CodePointTrie that was exported from
126
/// ICU binary data.
127
///
128
/// For more information:
129
/// - [ICU Site design doc](http://site.icu-project.org/design/struct/utrie)
130
/// - [ICU User Guide section on Properties lookup](https://unicode-org.github.io/icu/userguide/strings/properties.html#lookup)
131
// serde impls in crate::serde
132
#[derive(Debug, Eq, PartialEq, Yokeable, ZeroFrom)]
×
133
pub struct CodePointTrie<'trie, T: TrieValue> {
134
    pub(crate) header: CodePointTrieHeader,
×
135
    pub(crate) index: ZeroVec<'trie, u16>,
×
136
    pub(crate) data: ZeroVec<'trie, T>,
×
137
    // serde impl skips this field
138
    #[zerofrom(clone)] // TrieValue is Copy, this allows us to avoid
139
    // a T: ZeroFrom bound
140
    pub(crate) error_value: T,
×
141
}
142

143
/// This struct contains the fixed-length header fields of a [`CodePointTrie`].
144
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
3✔
145
#[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = icu_collections::codepointtrie))]
1✔
146
#[derive(Copy, Clone, Debug, Eq, PartialEq, Yokeable, ZeroFrom)]
×
147
pub struct CodePointTrieHeader {
148
    /// The code point of the start of the last range of the trie. A
149
    /// range is defined as a partition of the code point space such that the
150
    /// value in this trie associated with all code points of the same range is
151
    /// the same.
152
    ///
153
    /// For the property value data for many Unicode properties,
154
    /// often times, `high_start` is `U+10000` or lower. In such cases, not
155
    /// reserving space in the `index` array for duplicate values is a large
156
    /// savings. The "highValue" associated with the `high_start` range is
157
    /// stored at the second-to-last position of the `data` array.
158
    /// (See `impl_const::HIGH_VALUE_NEG_DATA_OFFSET`.)
159
    pub high_start: u32,
×
160
    /// A version of the `high_start` value that is right-shifted 12 spaces,
161
    /// but is rounded up to a multiple `0x1000` for easy testing from UTF-8
162
    /// lead bytes.
163
    pub shifted12_high_start: u16,
×
164
    /// Offset for the null block in the "index-3" table of the `index` array.
165
    /// Set to an impossibly high value (e.g., `0xffff`) if there is no
166
    /// dedicated index-3 null block.
167
    pub index3_null_offset: u16,
×
168
    /// Internal data null block offset, not shifted.
169
    /// Set to an impossibly high value (e.g., `0xfffff`) if there is no
170
    /// dedicated data null block.
171
    pub data_null_offset: u32,
×
172
    /// The value stored in the trie that represents a null value being
173
    /// associated to a code point.
174
    pub null_value: u32,
×
175
    /// The enum value representing the type of trie, where trie type is as it
176
    /// is defined in ICU (ex: Fast, Small).
177
    pub trie_type: TrieType,
×
178
}
179

180
impl TryFrom<u8> for TrieType {
181
    type Error = crate::codepointtrie::error::Error;
182

183
    fn try_from(trie_type_int: u8) -> Result<TrieType, crate::codepointtrie::error::Error> {
163✔
184
        match trie_type_int {
163✔
185
            0 => Ok(TrieType::Fast),
1✔
186
            1 => Ok(TrieType::Small),
162✔
187
            _ => Err(crate::codepointtrie::error::Error::FromDeserialized {
×
188
                reason: "Cannot parse value for trie_type",
189
            }),
×
190
        }
191
    }
163✔
192
}
193

194
impl<'trie, T: TrieValue> CodePointTrie<'trie, T> {
195
    #[doc(hidden)] // databake internal
196
    pub const fn from_parts(
1✔
197
        header: CodePointTrieHeader,
198
        index: ZeroVec<'trie, u16>,
199
        data: ZeroVec<'trie, T>,
200
        error_value: T,
201
    ) -> Self {
202
        Self {
1✔
203
            header,
1✔
204
            index,
1✔
205
            data,
1✔
206
            error_value,
207
        }
208
    }
1✔
209

210
    /// Returns a new [`CodePointTrie`] backed by borrowed data for the `index`
211
    /// array and `data` array, whose data values have width `W`.
212
    pub fn try_new(
176✔
213
        header: CodePointTrieHeader,
214
        index: ZeroVec<'trie, u16>,
215
        data: ZeroVec<'trie, T>,
216
    ) -> Result<CodePointTrie<'trie, T>, Error> {
217
        // Validation invariants are not needed here when constructing a new
218
        // `CodePointTrie` because:
219
        //
220
        // - Rust includes the size of a slice (or Vec or similar), which allows it
221
        //   to prevent lookups at out-of-bounds indices, whereas in C++, it is the
222
        //   programmer's responsibility to keep track of length info.
223
        // - For lookups into collections, Rust guarantees that a fallback value will
224
        //   be returned in the case of `.get()` encountering a lookup error, via
225
        //   the `Option` type.
226
        // - The `ZeroVec` serializer stores the length of the array along with the
227
        //   ZeroVec data, meaning that a deserializer would also see that length info.
228

229
        let error_value = data.last().ok_or(Error::EmptyDataVector)?;
176✔
230
        let trie: CodePointTrie<'trie, T> = CodePointTrie {
176✔
231
            header,
176✔
232
            index,
176✔
233
            data,
176✔
234
            error_value,
×
235
        };
236
        Ok(trie)
176✔
237
    }
176✔
238

239
    /// Returns the position in the data array containing the trie's stored
240
    /// error value.
241
    #[inline(always)] // `always` based on normalizer benchmarking
242
    fn trie_error_val_index(&self) -> u32 {
1✔
243
        self.data.len() as u32 - ERROR_VALUE_NEG_DATA_OFFSET
1✔
244
    }
1✔
245

246
    fn internal_small_index(&self, code_point: u32) -> u32 {
69,412,599✔
247
        let mut index1_pos: u32 = code_point >> SHIFT_1;
69,412,599✔
248
        if self.header.trie_type == TrieType::Fast {
138,825,198✔
249
            debug_assert!(
×
250
                FAST_TYPE_FAST_INDEXING_MAX < code_point && code_point < self.header.high_start
×
251
            );
252
            index1_pos = index1_pos + BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH;
×
253
        } else {
254
            assert!(code_point < self.header.high_start && self.header.high_start > SMALL_LIMIT);
69,412,599✔
255
            index1_pos += SMALL_INDEX_LENGTH;
69,412,599✔
256
        }
257
        let index1_val = if let Some(index1_val) = self.index.get(index1_pos as usize) {
69,412,599✔
258
            index1_val
69,429,112✔
259
        } else {
260
            return self.trie_error_val_index();
16,513✔
261
        };
262
        let index3_block_idx: u32 = (index1_val as u32) + ((code_point >> SHIFT_2) & INDEX_2_MASK);
69,429,112✔
263
        let mut index3_block: u32 =
264
            if let Some(index3_block) = self.index.get(index3_block_idx as usize) {
69,429,112✔
265
                index3_block as u32
69,429,112✔
266
            } else {
267
                return self.trie_error_val_index();
×
268
            };
269
        let mut index3_pos: u32 = (code_point >> SHIFT_3) & INDEX_3_MASK;
69,429,112✔
270
        let mut data_block: u32;
271
        if index3_block & 0x8000 == 0 {
138,858,224✔
272
            // 16-bit indexes
273
            data_block =
69,429,112✔
274
                if let Some(data_block) = self.index.get((index3_block + index3_pos) as usize) {
69,429,112✔
275
                    data_block as u32
69,429,112✔
276
                } else {
277
                    return self.trie_error_val_index();
×
278
                };
279
        } else {
280
            // 18-bit indexes stored in groups of 9 entries per 8 indexes.
281
            index3_block = (index3_block & 0x7fff) + (index3_pos & !7) + (index3_pos >> 3);
×
282
            index3_pos &= 7;
×
283
            data_block = if let Some(data_block) = self.index.get(index3_block as usize) {
×
284
                data_block as u32
×
285
            } else {
286
                return self.trie_error_val_index();
×
287
            };
288
            data_block = (data_block << (2 + (2 * index3_pos))) & 0x30000;
×
289
            index3_block += 1;
×
290
            data_block =
×
291
                if let Some(index3_val) = self.index.get((index3_block + index3_pos) as usize) {
×
292
                    data_block | (index3_val as u32)
×
293
                } else {
294
                    return self.trie_error_val_index();
×
295
                };
296
        }
297
        // Returns data_pos == data_block (offset) +
298
        //     portion of code_point bit field for last (4th) lookup
299
        data_block + (code_point & SMALL_DATA_MASK)
69,429,112✔
300
    }
69,445,625✔
301

302
    /// Returns the position in the `data` array for the given code point,
303
    /// where this code point is at or above the fast limit associated for the
304
    /// `trie_type`. We will refer to that limit as "`fastMax`" here.
305
    ///
306
    /// A lookup of the value in the code point trie for a code point in the
307
    /// code point space range [`fastMax`, `high_start`) will be a 4-step
308
    /// lookup: 3 lookups in the `index` array and one lookup in the `data`
309
    /// array. Lookups for code points in the range [`high_start`,
310
    /// `CODE_POINT_MAX`] are short-circuited to be a single lookup, see
311
    /// [CodePointTrieHeader::high_start].
312
    fn small_index(&self, code_point: u32) -> u32 {
72,674,130✔
313
        if code_point >= self.header.high_start {
72,674,130✔
314
            self.data.len() as u32 - HIGH_VALUE_NEG_DATA_OFFSET
3,260,449✔
315
        } else {
316
            self.internal_small_index(code_point) // helper fn
69,413,681✔
317
        }
318
    }
72,674,130✔
319

320
    /// Returns the position in the `data` array for the given code point,
321
    /// where this code point is below the fast limit associated for the
322
    /// `trie type`. We will refer to that limit as "`fastMax`" here.
323
    ///
324
    /// A lookup of the value in the code point trie for a code point in the
325
    /// code point space range [0, `fastMax`) will be a 2-step lookup: 1
326
    /// lookup in the `index` array and one lookup in the `data` array. By
327
    /// design, for trie type `T`, there is an element allocated in the `index`
328
    /// array for each block of code points in [0, `fastMax`), which in
329
    /// turn guarantees that those code points are represented and only need 1
330
    /// lookup.
331
    #[inline(always)] // `always` based on normalizer benchmarking
332
    fn fast_index(&self, code_point: u32) -> u32 {
2,979,843✔
333
        let index_array_pos: u32 = code_point >> FAST_TYPE_SHIFT;
2,979,843✔
334
        let index_array_val: u16 =
335
            if let Some(index_array_val) = self.index.get(index_array_pos as usize) {
2,979,843✔
336
                index_array_val
2,979,843✔
337
            } else {
338
                return self.trie_error_val_index();
×
339
            };
340
        let fast_index_val: u32 = index_array_val as u32 + (code_point & FAST_TYPE_DATA_MASK);
2,979,843✔
341
        fast_index_val
2,979,843✔
342
    }
2,979,843✔
343

344
    /// Returns the value that is associated with `code_point` in this [`CodePointTrie`].
345
    ///
346
    /// # Examples
347
    ///
348
    /// ```
349
    /// use icu_collections::codepointtrie::planes;
350
    /// let trie = planes::get_planes_trie();
351
    ///
352
    /// assert_eq!(0, trie.get32(0x41)); // 'A' as u32
353
    /// assert_eq!(0, trie.get32(0x13E0)); // 'Ꮰ' as u32
354
    /// assert_eq!(1, trie.get32(0x10044)); // '𐁄' as u32
355
    /// ```
356
    #[inline(always)] // `always` based on normalizer benchmarking
357
    pub fn get32(&self, code_point: u32) -> T {
75,610,714✔
358
        // If we cannot read from the data array, then return the sentinel value
359
        // self.error_value() for the instance type for T: TrieValue.
360
        self.get32_ule(code_point)
151,221,428✔
361
            .map(|t| T::from_unaligned(*t))
75,636,281✔
362
            .unwrap_or(self.error_value)
75,610,714✔
363
    }
75,610,714✔
364

365
    /// Returns the value that is associated with `char` in this [`CodePointTrie`].
366
    ///
367
    /// # Examples
368
    ///
369
    /// ```
370
    /// use icu_collections::codepointtrie::planes;
371
    /// let trie = planes::get_planes_trie();
372
    ///
373
    /// assert_eq!(0, trie.get('A')); // 'A' as u32
374
    /// assert_eq!(0, trie.get('Ꮰ')); // 'Ꮰ' as u32
375
    /// assert_eq!(1, trie.get('𐁄')); // '𐁄' as u32
376
    /// ```
377
    #[inline(always)]
378
    pub fn get(&self, c: char) -> T {
1,924,958✔
379
        self.get32(u32::from(c))
1,924,958✔
380
    }
1,924,958✔
381

382
    /// Returns a reference to the ULE of the value that is associated with `code_point` in this [`CodePointTrie`].
383
    ///
384
    /// # Examples
385
    ///
386
    /// ```
387
    /// use icu_collections::codepointtrie::planes;
388
    /// let trie = planes::get_planes_trie();
389
    ///
390
    /// assert_eq!(Some(&0), trie.get32_ule(0x41)); // 'A' as u32
391
    /// assert_eq!(Some(&0), trie.get32_ule(0x13E0)); // 'Ꮰ' as u32
392
    /// assert_eq!(Some(&1), trie.get32_ule(0x10044)); // '𐁄' as u32
393
    /// ```
394
    #[inline(always)] // `always` based on normalizer benchmarking
395
    pub fn get32_ule(&self, code_point: u32) -> Option<&T::ULE> {
75,642,560✔
396
        // All code points up to the fast max limit are represented
397
        // individually in the `index` array to hold their `data` array position, and
398
        // thus only need 2 lookups for a [CodePointTrie::get()](`crate::codepointtrie::CodePointTrie::get`).
399
        // Code points above the "fast max" limit require 4 lookups.
400
        let fast_max = match self.header.trie_type {
75,642,560✔
401
            TrieType::Fast => FAST_TYPE_FAST_INDEXING_MAX,
12✔
402
            TrieType::Small => SMALL_TYPE_FAST_INDEXING_MAX,
75,642,548✔
403
        };
404
        let data_pos: u32 = if code_point <= fast_max {
75,642,560✔
405
            Self::fast_index(self, code_point)
2,970,425✔
406
        } else if code_point <= CODE_POINT_MAX {
72,672,135✔
407
            Self::small_index(self, code_point)
72,672,134✔
408
        } else {
409
            self.trie_error_val_index()
1✔
410
        };
411
        // Returns the trie value (or trie's error value).
412
        self.data.as_ule_slice().get(data_pos as usize)
75,642,560✔
413
    }
75,642,560✔
414

415
    /// Converts the CodePointTrie into one that returns another type of the same size.
416
    ///
417
    /// Borrowed data remains borrowed, and owned data remains owned.
418
    ///
419
    /// If the old and new types are not the same size, use
420
    /// [`CodePointTrie::try_alloc_map_value`].
421
    ///
422
    /// # Panics
423
    ///
424
    /// Panics if `T` and `P` are different sizes.
425
    ///
426
    /// More specifically, panics if [ZeroVec::try_into_converted()] panics when converting
427
    /// `ZeroVec<T>` into `ZeroVec<P>`, which happens if `T::ULE` and `P::ULE` differ in size.
428
    ///
429
    /// # Examples
430
    ///
431
    /// ```no_run
432
    /// use icu_collections::codepointtrie::planes;
433
    /// use icu_collections::codepointtrie::CodePointTrie;
434
    ///
435
    /// let planes_trie_u8: CodePointTrie<u8> = planes::get_planes_trie();
436
    /// let planes_trie_i8: CodePointTrie<i8> =
437
    ///     planes_trie_u8.try_into_converted().expect("infallible");
438
    ///
439
    /// assert_eq!(planes_trie_i8.get32(0x30000), 3);
440
    /// ```
441
    pub fn try_into_converted<P>(self) -> Result<CodePointTrie<'trie, P>, ZeroVecError>
×
442
    where
443
        P: TrieValue,
444
    {
445
        let converted_data = self.data.try_into_converted()?;
×
446
        let error_ule = self.error_value.to_unaligned();
×
447
        let slice = &[error_ule];
×
448
        let error_vec = ZeroVec::<T>::new_borrowed(slice);
×
449
        let error_converted = error_vec.try_into_converted::<P>()?;
×
450
        #[allow(clippy::expect_used)] // we know this cannot fail
451
        Ok(CodePointTrie {
×
452
            header: self.header,
×
453
            index: self.index,
×
454
            data: converted_data,
×
455
            error_value: error_converted
×
456
                .get(0)
457
                .expect("vector known to have one element"),
458
        })
×
459
    }
×
460

461
    /// Maps the CodePointTrie into one that returns a different type.
462
    ///
463
    /// This function returns owned data.
464
    ///
465
    /// If the old and new types are the same size, use the more efficient
466
    /// [`CodePointTrie::try_into_converted`].
467
    ///
468
    /// # Examples
469
    ///
470
    /// ```
471
    /// use core::convert::Infallible;
472
    /// use icu_collections::codepointtrie::planes;
473
    /// use icu_collections::codepointtrie::CodePointTrie;
474
    ///
475
    /// let planes_trie_u8: CodePointTrie<u8> = planes::get_planes_trie();
476
    /// let planes_trie_u16: CodePointTrie<u16> = planes_trie_u8
477
    ///     .try_alloc_map_value(TryFrom::try_from)
478
    ///     .expect("infallible");
479
    ///
480
    /// assert_eq!(planes_trie_u16.get32(0x30000), 3);
481
    /// ```
482
    pub fn try_alloc_map_value<P, E>(
1✔
483
        &self,
484
        mut f: impl FnMut(T) -> Result<P, E>,
485
    ) -> Result<CodePointTrie<'trie, P>, E>
486
    where
487
        P: TrieValue,
488
    {
489
        let error_converted = f(self.error_value)?;
1✔
490
        let converted_data = self.data.iter().map(f).collect::<Result<ZeroVec<P>, E>>()?;
1✔
491
        Ok(CodePointTrie {
1✔
492
            header: self.header,
1✔
493
            index: self.index.clone(),
1✔
494
            data: converted_data,
1✔
495
            error_value: error_converted,
1✔
496
        })
497
    }
1✔
498

499
    /// Returns a [`CodePointMapRange`] struct which represents a range of code
500
    /// points associated with the same trie value. The returned range will be
501
    /// the longest stretch of consecutive code points starting at `start` that
502
    /// share this value.
503
    ///
504
    /// This method is designed to use the internal details of
505
    /// the structure of [`CodePointTrie`] to be optimally efficient. This will
506
    /// outperform a naive approach that just uses [`CodePointTrie::get()`].
507
    ///
508
    /// This method provides lower-level functionality that can be used in the
509
    /// implementation of other methods that are more convenient to the user.
510
    /// To obtain an optimal partition of the code point space for
511
    /// this trie resulting in the fewest number of ranges, see
512
    /// [`CodePointTrie::iter_ranges()`].
513
    ///
514
    /// # Examples
515
    ///
516
    /// ```
517
    /// use icu_collections::codepointtrie::planes;
518
    ///
519
    /// let trie = planes::get_planes_trie();
520
    ///
521
    /// const CODE_POINT_MAX: u32 = 0x10ffff;
522
    /// let start = 0x1_0000;
523
    /// let exp_end = 0x1_ffff;
524
    ///
525
    /// let start_val = trie.get32(start);
526
    /// assert_eq!(trie.get32(exp_end), start_val);
527
    /// assert_ne!(trie.get32(exp_end + 1), start_val);
528
    ///
529
    /// use core::ops::RangeInclusive;
530
    /// use icu_collections::codepointtrie::CodePointMapRange;
531
    ///
532
    /// let cpm_range: CodePointMapRange<u8> = trie.get_range(start).unwrap();
533
    ///
534
    /// assert_eq!(cpm_range.range.start(), &start);
535
    /// assert_eq!(cpm_range.range.end(), &exp_end);
536
    /// assert_eq!(cpm_range.value, start_val);
537
    ///
538
    /// // `start` can be any code point, whether or not it lies on the boundary
539
    /// // of a maximally large range that still contains `start`
540
    ///
541
    /// let submaximal_1_start = start + 0x1234;
542
    /// let submaximal_1 = trie.get_range(submaximal_1_start).unwrap();
543
    /// assert_eq!(submaximal_1.range.start(), &0x1_1234);
544
    /// assert_eq!(submaximal_1.range.end(), &0x1_ffff);
545
    /// assert_eq!(submaximal_1.value, start_val);
546
    ///
547
    /// let submaximal_2_start = start + 0xffff;
548
    /// let submaximal_2 = trie.get_range(submaximal_2_start).unwrap();
549
    /// assert_eq!(submaximal_2.range.start(), &0x1_ffff);
550
    /// assert_eq!(submaximal_2.range.end(), &0x1_ffff);
551
    /// assert_eq!(submaximal_2.value, start_val);
552
    /// ```
553
    pub fn get_range(&self, start: u32) -> Option<CodePointMapRange<T>> {
856,144✔
554
        // Exit early if the start code point is out of range, or if it is
555
        // in the last range of code points in high_start..=CODE_POINT_MAX
556
        // (start- and end-inclusive) that all share the same trie value.
557
        if CODE_POINT_MAX < start {
856,144✔
558
            return None;
234✔
559
        }
560
        if start >= self.header.high_start {
855,910✔
561
            let di: usize = self.data.len() - (HIGH_VALUE_NEG_DATA_OFFSET as usize);
2✔
562
            let value: T = self.data.get(di)?;
2✔
563
            return Some(CodePointMapRange {
2✔
564
                range: RangeInclusive::new(start, CODE_POINT_MAX),
2✔
565
                value,
566
            });
567
        }
568

569
        let null_value: T = T::try_from_u32(self.header.null_value).ok()?;
855,908✔
570

571
        let mut prev_i3_block: u32 = u32::MAX; // using u32::MAX (instead of -1 as an i32 in ICU)
855,908✔
572
        let mut prev_block: u32 = u32::MAX; // using u32::MAX (instead of -1 as an i32 in ICU)
855,908✔
573
        let mut c: u32 = start;
855,908✔
574
        let mut trie_value: T = self.error_value();
855,908✔
575
        let mut value: T = self.error_value();
855,908✔
576
        let mut have_value: bool = false;
855,908✔
577

578
        loop {
855,908✔
579
            let i3_block: u32;
580
            let mut i3: u32;
581
            let i3_block_length: u32;
582
            let data_block_length: u32;
583

584
            // Initialize values before beginning the iteration in the subsequent
585
            // `loop` block. In particular, use the "i3*" local variables
586
            // (representing the `index` array position's offset + increment
587
            // for a 3rd-level trie lookup) to help initialize the data block
588
            // variable `block` in the loop for the `data` array.
589
            //
590
            // When a lookup code point is <= the trie's *_FAST_INDEXING_MAX that
591
            // corresponds to its `trie_type`, the lookup only takes 2 steps
592
            // (once into the `index`, once into the `data` array); otherwise,
593
            // takes 4 steps (3 iterative lookups into the `index`, once more
594
            // into the `data` array). So for convenience's sake, when we have the
595
            // 2-stage lookup, reuse the "i3*" variable names for the first lookup.
596
            if c <= 0xffff
2,865,646✔
597
                && (self.header.trie_type == TrieType::Fast || c <= SMALL_TYPE_FAST_INDEXING_MAX)
634,887✔
598
            {
599
                i3_block = 0;
246,074✔
600
                i3 = c >> FAST_TYPE_SHIFT;
246,074✔
601
                i3_block_length = if self.header.trie_type == TrieType::Fast {
246,074✔
602
                    BMP_INDEX_LENGTH
×
603
                } else {
604
                    SMALL_INDEX_LENGTH
246,074✔
605
                };
606
                data_block_length = FAST_TYPE_DATA_BLOCK_LENGTH;
246,074✔
607
            } else {
608
                // Use the multi-stage index.
609
                let mut i1: u32 = c >> SHIFT_1;
1,102,643✔
610
                if self.header.trie_type == TrieType::Fast {
2,205,286✔
611
                    debug_assert!(0xffff < c && c < self.header.high_start);
×
612
                    i1 = i1 + BMP_INDEX_LENGTH - OMITTED_BMP_INDEX_1_LENGTH;
×
613
                } else {
614
                    debug_assert!(
1,102,643✔
615
                        c < self.header.high_start && self.header.high_start > SMALL_LIMIT
1,102,643✔
616
                    );
617
                    i1 += SMALL_INDEX_LENGTH;
1,102,643✔
618
                }
619
                let i2: u16 = self.index.get(i1 as usize)?;
1,102,643✔
620
                let i3_block_idx: u32 = (i2 as u32) + ((c >> SHIFT_2) & INDEX_2_MASK);
1,102,643✔
621
                i3_block = if let Some(i3b) = self.index.get(i3_block_idx as usize) {
1,102,643✔
622
                    i3b as u32
1,102,643✔
623
                } else {
624
                    return None;
×
625
                };
626
                if i3_block == prev_i3_block && (c - start) >= CP_PER_INDEX_2_ENTRY {
1,102,643✔
627
                    // The index-3 block is the same as the previous one, and filled with value.
628
                    debug_assert!((c & (CP_PER_INDEX_2_ENTRY - 1)) == 0);
463,380✔
629
                    c += CP_PER_INDEX_2_ENTRY;
463,380✔
630

631
                    if c >= self.header.high_start {
463,380✔
632
                        break;
633
                    } else {
634
                        continue;
635
                    }
636
                }
637
                prev_i3_block = i3_block;
639,263✔
638
                if i3_block == self.header.index3_null_offset as u32 {
639,263✔
639
                    // This is the index-3 null block.
640
                    // All of the `data` array blocks pointed to by the values
641
                    // in this block of the `index` 3rd-stage subarray will
642
                    // contain this trie's null_value. So if we are in the middle
643
                    // of a range, end it and return early, otherwise start a new
644
                    // range of null values.
645
                    if have_value {
3,302✔
646
                        if null_value != value {
3,288✔
647
                            return Some(CodePointMapRange {
7✔
648
                                range: RangeInclusive::new(start, c - 1),
7✔
649
                                value,
7✔
650
                            });
651
                        }
652
                    } else {
653
                        trie_value = T::try_from_u32(self.header.null_value).ok()?;
7✔
654
                        value = null_value;
7✔
655
                        have_value = true;
7✔
656
                    }
657
                    prev_block = self.header.data_null_offset;
3,288✔
658
                    c = (c + CP_PER_INDEX_2_ENTRY) & !(CP_PER_INDEX_2_ENTRY - 1);
3,288✔
659

660
                    if c >= self.header.high_start {
3,288✔
661
                        break;
662
                    } else {
663
                        continue;
664
                    }
665
                }
666
                i3 = (c >> SHIFT_3) & INDEX_3_MASK;
635,968✔
667
                i3_block_length = INDEX_3_BLOCK_LENGTH;
635,968✔
668
                data_block_length = SMALL_DATA_BLOCK_LENGTH;
635,968✔
669
            }
670

671
            // Enumerate data blocks for one index-3 block.
672
            loop {
673
                let mut block: u32;
674
                if (i3_block & 0x8000) == 0 {
3,413,338✔
675
                    block = if let Some(b) = self.index.get((i3_block + i3) as usize) {
1,706,669✔
676
                        b as u32
1,706,669✔
677
                    } else {
678
                        return None;
×
679
                    };
680
                } else {
681
                    // 18-bit indexes stored in groups of 9 entries per 8 indexes.
682
                    let mut group: u32 = (i3_block & 0x7fff) + (i3 & !7) + (i3 >> 3);
×
683
                    let gi: u32 = i3 & 7;
×
684
                    let gi_val: u32 = if let Some(giv) = self.index.get(group as usize) {
×
685
                        giv.into()
×
686
                    } else {
687
                        return None;
×
688
                    };
689
                    block = (gi_val << (2 + (2 * gi))) & 0x30000;
×
690
                    group += 1;
×
691
                    let ggi_val: u32 = if let Some(ggiv) = self.index.get((group + gi) as usize) {
×
692
                        ggiv as u32
×
693
                    } else {
694
                        return None;
×
695
                    };
696
                    block |= ggi_val;
×
697
                }
698

699
                // If our previous and current return values of the 3rd-stage `index`
700
                // lookup yield the same `data` block offset, and if we already know that
701
                // the entire `data` block / subarray starting at that offset stores
702
                // `value` and nothing else, then we can extend our range by the length
703
                // of a data block and continue.
704
                // Otherwise, we have to iterate over the values stored in the
705
                // new data block to see if they differ from `value`.
706
                if block == prev_block && (c - start) >= data_block_length {
2,292,370✔
707
                    // The block is the same as the previous one, and filled with value.
708
                    debug_assert!((c & (data_block_length - 1)) == 0);
585,701✔
709
                    c += data_block_length;
585,701✔
710
                } else {
711
                    let data_mask: u32 = data_block_length - 1;
1,120,968✔
712
                    prev_block = block;
1,120,968✔
713
                    if block == self.header.data_null_offset {
1,140,252✔
714
                        // This is the data null block.
715
                        // If we are in the middle of a range, end it and
716
                        // return early, otherwise start a new range of null
717
                        // values.
718
                        if have_value {
26,799✔
719
                            if null_value != value {
19,283✔
720
                                return Some(CodePointMapRange {
3,757✔
721
                                    range: RangeInclusive::new(start, c - 1),
3,757✔
722
                                    value,
3,757✔
723
                                });
724
                            }
725
                        } else {
726
                            trie_value = T::try_from_u32(self.header.null_value).ok()?;
3,758✔
727
                            value = null_value;
3,758✔
728
                            have_value = true;
3,758✔
729
                        }
730
                        c = (c + data_block_length) & !data_mask;
19,284✔
731
                    } else {
732
                        let mut di: u32 = block + (c & data_mask);
1,097,927✔
733
                        let mut trie_value_2: T = self.data.get(di as usize)?;
1,097,927✔
734
                        if have_value {
1,949,826✔
735
                            if trie_value_2 != trie_value {
246,028✔
736
                                if maybe_filter_value(
120,613✔
737
                                    trie_value_2,
120,613✔
738
                                    T::try_from_u32(self.header.null_value).ok()?,
120,613✔
739
                                    null_value,
120,613✔
740
                                ) != value
741
                                {
742
                                    return Some(CodePointMapRange {
120,613✔
743
                                        range: RangeInclusive::new(start, c - 1),
120,613✔
744
                                        value,
120,613✔
745
                                    });
746
                                }
747
                                // `trie_value` stores the previous value that was retrieved
748
                                // from the trie.
749
                                // `value` stores the value associated for the range (return
750
                                // value) that we are currently building, which is computed
751
                                // as a transformation by applying maybe_filter_value()
752
                                // to the trie value.
753
                                // The current trie value `trie_value_2` within this data block
754
                                // differs here from the previous value in `trie_value`.
755
                                // But both map to `value` after applying `maybe_filter_value`.
756
                                // It is not clear whether the previous or the current trie value
757
                                // (or neither) is more likely to match potential subsequent trie
758
                                // values that would extend the range by mapping to `value`.
759
                                // On the assumption of locality -- often times consecutive
760
                                // characters map to the same trie values -- remembering the new
761
                                // one might make it faster to extend this range further
762
                                // (by increasing the chance that the next `trie_value_2 !=
763
                                // trie_value` test will be false).
764
                                trie_value = trie_value_2; // may or may not help
×
765
                            }
766
                        } else {
767
                            trie_value = trie_value_2;
851,899✔
768
                            value = maybe_filter_value(
851,899✔
769
                                trie_value_2,
851,899✔
770
                                T::try_from_u32(self.header.null_value).ok()?,
851,899✔
771
                                null_value,
851,899✔
772
                            );
773
                            have_value = true;
851,899✔
774
                        }
775

776
                        c += 1;
977,314✔
777
                        while (c & data_mask) != 0 {
4,633,505✔
778
                            di += 1;
4,387,487✔
779
                            trie_value_2 = self.data.get(di as usize)?;
4,387,487✔
780
                            if trie_value_2 != trie_value {
4,387,487✔
781
                                if maybe_filter_value(
731,296✔
782
                                    trie_value_2,
731,296✔
783
                                    T::try_from_u32(self.header.null_value).ok()?,
731,296✔
784
                                    null_value,
731,296✔
785
                                ) != value
786
                                {
787
                                    return Some(CodePointMapRange {
731,296✔
788
                                        range: RangeInclusive::new(start, c - 1),
731,296✔
789
                                        value,
731,296✔
790
                                    });
791
                                }
792
                                // `trie_value` stores the previous value that was retrieved
793
                                // from the trie.
794
                                // `value` stores the value associated for the range (return
795
                                // value) that we are currently building, which is computed
796
                                // as a transformation by applying maybe_filter_value()
797
                                // to the trie value.
798
                                // The current trie value `trie_value_2` within this data block
799
                                // differs here from the previous value in `trie_value`.
800
                                // But both map to `value` after applying `maybe_filter_value`.
801
                                // It is not clear whether the previous or the current trie value
802
                                // (or neither) is more likely to match potential subsequent trie
803
                                // values that would extend the range by mapping to `value`.
804
                                // On the assumption of locality -- often times consecutive
805
                                // characters map to the same trie values -- remembering the new
806
                                // one might make it faster to extend this range further
807
                                // (by increasing the chance that the next `trie_value_2 !=
808
                                // trie_value` test will be false).
809
                                trie_value = trie_value_2; // may or may not help
×
810
                            }
811

812
                            c += 1;
3,656,191✔
813
                        }
814
                    }
815
                }
816

817
                i3 += 1;
851,003✔
818
                if i3 >= i3_block_length {
851,003✔
819
                    break;
820
                }
821
            }
822

823
            if c >= self.header.high_start {
26,376✔
824
                break;
825
            }
826
        }
827

828
        debug_assert!(have_value);
235✔
829

830
        // Now that c >= high_start, compare `value` to `high_value` to see
831
        // if we can merge our current range with the high_value range
832
        // high_start..=CODE_POINT_MAX (start- and end-inclusive), otherwise
833
        // stop at high_start - 1.
834
        let di: u32 = self.data.len() as u32 - HIGH_VALUE_NEG_DATA_OFFSET;
235✔
835
        let high_value: T = self.data.get(di as usize)?;
235✔
836
        if maybe_filter_value(
235✔
837
            high_value,
838
            T::try_from_u32(self.header.null_value).ok()?,
235✔
839
            null_value,
235✔
840
        ) != value
841
        {
842
            c -= 1;
2✔
843
        } else {
844
            c = CODE_POINT_MAX;
233✔
845
        }
846
        Some(CodePointMapRange {
235✔
847
            range: RangeInclusive::new(start, c),
235✔
848
            value,
235✔
849
        })
850
    }
856,144✔
851

852
    /// Yields an [`Iterator`] returning ranges of consecutive code points that
853
    /// share the same value in the [`CodePointTrie`], as given by
854
    /// [`CodePointTrie::get_range()`].
855
    ///
856
    /// # Examples
857
    ///
858
    /// ```
859
    /// use core::ops::RangeInclusive;
860
    /// use icu::collections::codepointtrie::planes;
861
    /// use icu_collections::codepointtrie::CodePointMapRange;
862
    ///
863
    /// let planes_trie = planes::get_planes_trie();
864
    ///
865
    /// let mut ranges = planes_trie.iter_ranges();
866
    ///
867
    /// for plane in 0..=16 {
868
    ///     let exp_start = plane * 0x1_0000;
869
    ///     let exp_end = exp_start + 0xffff;
870
    ///     assert_eq!(
871
    ///         ranges.next(),
872
    ///         Some(CodePointMapRange {
873
    ///             range: RangeInclusive::new(exp_start, exp_end),
874
    ///             value: plane as u8
875
    ///         })
876
    ///     );
877
    /// }
878
    ///
879
    /// // Hitting the end of the iterator returns `None`, as will subsequent
880
    /// // calls to .next().
881
    /// assert_eq!(ranges.next(), None);
882
    /// assert_eq!(ranges.next(), None);
883
    /// ```
884
    pub fn iter_ranges(&self) -> CodePointMapRangeIterator<T> {
237✔
885
        let init_range = Some(CodePointMapRange {
237✔
886
            range: RangeInclusive::new(u32::MAX, u32::MAX),
237✔
887
            value: self.error_value(),
237✔
888
        });
889
        CodePointMapRangeIterator::<T> {
237✔
890
            cpt: self,
891
            cpm_range: init_range,
237✔
892
        }
893
    }
237✔
894

895
    /// Yields an [`Iterator`] returning the ranges of the code points whose values
896
    /// match `value` in the [`CodePointTrie`].
897
    ///
898
    /// # Examples
899
    ///
900
    /// ```
901
    /// use icu_collections::codepointtrie::planes;
902
    ///
903
    /// let trie = planes::get_planes_trie();
904
    ///
905
    /// let plane_val = 2;
906
    /// let mut sip_range_iter = trie.get_ranges_for_value(plane_val as u8);
907
    ///
908
    /// let start = plane_val * 0x1_0000;
909
    /// let end = start + 0xffff;
910
    ///
911
    /// let sip_range = sip_range_iter.next()
912
    ///     .expect("Plane 2 (SIP) should exist in planes data");
913
    /// assert_eq!(start..=end, sip_range);
914
    ///
915
    /// assert!(sip_range_iter.next().is_none());
916
    pub fn get_ranges_for_value(&self, value: T) -> impl Iterator<Item = RangeInclusive<u32>> + '_ {
46✔
917
        self.iter_ranges()
92✔
918
            .filter(move |cpm_range| cpm_range.value == value)
151,017✔
919
            .map(|cpm_range| cpm_range.range)
4,647✔
920
    }
46✔
921

922
    /// Yields an [`Iterator`] returning the ranges of the code points after passing
923
    /// the value through a mapping function.
924
    ///
925
    /// This is preferable to calling `.get_ranges().map()` since it will coalesce
926
    /// adjacent ranges into one.
927
    ///
928
    /// # Examples
929
    ///
930
    /// ```
931
    /// use icu_collections::codepointtrie::planes;
932
    ///
933
    /// let trie = planes::get_planes_trie();
934
    ///
935
    /// let plane_val = 2;
936
    /// let mut sip_range_iter = trie.iter_ranges_mapped(|value| value != plane_val as u8).filter(|range| range.value);
937
    ///
938
    /// let end = plane_val * 0x1_0000 - 1;
939
    ///
940
    /// let sip_range = sip_range_iter.next()
941
    ///     .expect("Complemented planes data should have at least one entry");
942
    /// assert_eq!(0..=end, sip_range.range);
943
    pub fn iter_ranges_mapped<'a, U: Eq + 'a>(
21✔
944
        &'a self,
945
        mut map: impl FnMut(T) -> U + Copy + 'a,
946
    ) -> impl Iterator<Item = CodePointMapRange<U>> + 'a {
947
        crate::iterator_utils::RangeListIteratorCoalescer::new(self.iter_ranges().map(
42✔
948
            move |range| CodePointMapRange {
78,805✔
949
                range: range.range,
39,392✔
950
                value: map(range.value),
39,392✔
951
            },
39,392✔
952
        ))
953
    }
21✔
954

955
    /// Returns a [`CodePointInversionList`] for the code points that have the given
956
    /// [`TrieValue`] in the trie.
957
    ///
958
    /// # Examples
959
    ///
960
    /// ```
961
    /// use icu_collections::codepointtrie::planes;
962
    ///
963
    /// let trie = planes::get_planes_trie();
964
    ///
965
    /// let plane_val = 2;
966
    /// let sip = trie.get_set_for_value(plane_val as u8);
967
    ///
968
    /// let start = plane_val * 0x1_0000;
969
    /// let end = start + 0xffff;
970
    ///
971
    /// assert!(!sip.contains32(start - 1));
972
    /// assert!(sip.contains32(start));
973
    /// assert!(sip.contains32(end));
974
    /// assert!(!sip.contains32(end + 1));
975
    /// ```
976
    pub fn get_set_for_value(&self, value: T) -> CodePointInversionList<'static> {
46✔
977
        let value_ranges = self.get_ranges_for_value(value);
46✔
978
        CodePointInversionList::from_iter(value_ranges)
46✔
979
    }
46✔
980

981
    /// Returns the value used as an error value for this trie
982
    #[inline]
983
    pub fn error_value(&self) -> T {
1,711,116✔
984
        self.error_value
1,711,116✔
985
    }
1,711,116✔
986
}
987

988
#[cfg(feature = "databake")]
989
impl<'trie, T: TrieValue + databake::Bake> databake::Bake for CodePointTrie<'trie, T> {
990
    fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
1✔
991
        let header = self.header.bake(env);
1✔
992
        let index = self.index.bake(env);
1✔
993
        let data = self.data.bake(env);
1✔
994
        let error_value = self.error_value.bake(env);
1✔
995
        databake::quote! { icu_collections::codepointtrie::CodePointTrie::from_parts(#header, #index, #data, #error_value) }
1✔
996
    }
1✔
997
}
998

999
impl<'trie, T: TrieValue + Into<u32>> CodePointTrie<'trie, T> {
1000
    /// Returns the value that is associated with `code_point` for this [`CodePointTrie`]
1001
    /// as a `u32`.
1002
    ///
1003
    /// # Examples
1004
    ///
1005
    /// ```
1006
    /// use icu_collections::codepointtrie::planes;
1007
    /// let trie = planes::get_planes_trie();
1008
    ///
1009
    /// let cp = '𑖎' as u32;
1010
    /// assert_eq!(cp, 0x1158E);
1011
    ///
1012
    /// let plane_num: u8 = trie.get32(cp);
1013
    /// assert_eq!(trie.get32_u32(cp), plane_num as u32);
1014
    /// ```
1015
    // Note: This API method maintains consistency with the corresponding
1016
    // original ICU APIs.
1017
    pub fn get32_u32(&self, code_point: u32) -> u32 {
4,472✔
1018
        self.get32(code_point).into()
4,472✔
1019
    }
4,472✔
1020
}
1021

1022
impl<'trie, T: TrieValue> Clone for CodePointTrie<'trie, T>
1023
where
1024
    <T as zerovec::ule::AsULE>::ULE: Clone,
1025
{
1026
    fn clone(&self) -> Self {
×
1027
        CodePointTrie {
×
1028
            header: self.header,
×
1029
            index: self.index.clone(),
×
1030
            data: self.data.clone(),
×
1031
            error_value: self.error_value,
×
1032
        }
×
1033
    }
×
1034
}
1035

1036
/// Represents a range of consecutive code points sharing the same value in a
1037
/// code point map. The start and end of the interval is represented as a
1038
/// `RangeInclusive<u32>`, and the value is represented as `T`.
1039
#[derive(PartialEq, Eq, Debug, Clone)]
1,711,128✔
1040
pub struct CodePointMapRange<T> {
1041
    /// Range of code points from start to end (inclusive).
1042
    pub range: RangeInclusive<u32>,
855,564✔
1043
    /// Trie value associated with this range.
1044
    pub value: T,
855,564✔
1045
}
1046

1047
/// A custom [`Iterator`] type specifically for a code point trie that returns
1048
/// [`CodePointMapRange`]s.
1049
pub struct CodePointMapRangeIterator<'a, T: TrieValue> {
1050
    cpt: &'a CodePointTrie<'a, T>,
1051
    // Initialize `range` to Some(CodePointMapRange{ start: u32::MAX, end: u32::MAX, value: 0}).
1052
    // When `range` is Some(...) and has a start value different from u32::MAX, then we have
1053
    // returned at least one code point range due to a call to `next()`.
1054
    // When `range` == `None`, it means that we have hit the end of iteration. It would occur
1055
    // after a call to `next()` returns a None <=> we attempted to call `get_range()`
1056
    // with a start code point that is > CODE_POINT_MAX.
1057
    cpm_range: Option<CodePointMapRange<T>>,
1058
}
1059

1060
impl<'a, T: TrieValue> Iterator for CodePointMapRangeIterator<'a, T> {
1061
    type Item = CodePointMapRange<T>;
1062

1063
    fn next(&mut self) -> Option<Self::Item> {
855,554✔
1064
        self.cpm_range = match &self.cpm_range {
1,711,108✔
1065
            Some(cpmr) => {
855,534✔
1066
                if *cpmr.range.start() == u32::MAX {
855,534✔
1067
                    self.cpt.get_range(0)
237✔
1068
                } else {
1069
                    self.cpt.get_range(cpmr.range.end() + 1)
855,297✔
1070
                }
1071
            }
1072
            None => None,
20✔
1073
        };
1074
        // Note: Clone is cheap. We can't Copy because RangeInclusive does not impl Copy.
1075
        self.cpm_range.clone()
855,554✔
1076
    }
855,554✔
1077
}
1078

1079
#[cfg(test)]
1080
mod tests {
1081
    use super::*;
1082
    use crate::codepointtrie::planes;
1083
    use alloc::vec::Vec;
1084

1085
    #[test]
1086
    #[cfg(feature = "serde")]
1087
    fn test_serde_with_postcard_roundtrip() -> Result<(), postcard::Error> {
2✔
1088
        let trie = crate::codepointtrie::planes::get_planes_trie();
1✔
1089
        let trie_serialized: Vec<u8> = postcard::to_allocvec(&trie).unwrap();
1✔
1090

1091
        // Assert an expected (golden data) version of the serialized trie.
1092
        const EXP_TRIE_SERIALIZED: &[u8] = &[
1093
            128, 128, 64, 128, 2, 2, 0, 0, 1, 160, 18, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1094
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1095
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1096
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1097
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136,
1098
            2, 144, 2, 144, 2, 144, 2, 176, 2, 176, 2, 176, 2, 176, 2, 208, 2, 208, 2, 208, 2, 208,
1099
            2, 240, 2, 240, 2, 240, 2, 240, 2, 16, 3, 16, 3, 16, 3, 16, 3, 48, 3, 48, 3, 48, 3, 48,
1100
            3, 80, 3, 80, 3, 80, 3, 80, 3, 112, 3, 112, 3, 112, 3, 112, 3, 144, 3, 144, 3, 144, 3,
1101
            144, 3, 176, 3, 176, 3, 176, 3, 176, 3, 208, 3, 208, 3, 208, 3, 208, 3, 240, 3, 240, 3,
1102
            240, 3, 240, 3, 16, 4, 16, 4, 16, 4, 16, 4, 48, 4, 48, 4, 48, 4, 48, 4, 80, 4, 80, 4,
1103
            80, 4, 80, 4, 112, 4, 112, 4, 112, 4, 112, 4, 0, 0, 16, 0, 32, 0, 48, 0, 64, 0, 80, 0,
1104
            96, 0, 112, 0, 0, 0, 16, 0, 32, 0, 48, 0, 0, 0, 16, 0, 32, 0, 48, 0, 0, 0, 16, 0, 32,
1105
            0, 48, 0, 0, 0, 16, 0, 32, 0, 48, 0, 0, 0, 16, 0, 32, 0, 48, 0, 0, 0, 16, 0, 32, 0, 48,
1106
            0, 0, 0, 16, 0, 32, 0, 48, 0, 0, 0, 16, 0, 32, 0, 48, 0, 128, 0, 128, 0, 128, 0, 128,
1107
            0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128,
1108
            0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128,
1109
            0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144,
1110
            0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144,
1111
            0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 144,
1112
            0, 144, 0, 144, 0, 144, 0, 144, 0, 144, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160,
1113
            0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160,
1114
            0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160, 0, 160,
1115
            0, 160, 0, 160, 0, 160, 0, 160, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176,
1116
            0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176,
1117
            0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176, 0, 176,
1118
            0, 176, 0, 176, 0, 176, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192,
1119
            0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192,
1120
            0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192, 0, 192,
1121
            0, 192, 0, 192, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208,
1122
            0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208,
1123
            0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208, 0, 208,
1124
            0, 208, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224,
1125
            0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224,
1126
            0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224, 0, 224,
1127
            0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240,
1128
            0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240,
1129
            0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 240, 0, 0,
1130
            1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
1131
            0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
1132
            1, 0, 1, 0, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1,
1133
            16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16,
1134
            1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 32, 1, 32, 1, 32, 1,
1135
            32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32,
1136
            1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1,
1137
            32, 1, 32, 1, 32, 1, 32, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48,
1138
            1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1,
1139
            48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 64, 1, 64,
1140
            1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1,
1141
            64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64,
1142
            1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1,
1143
            80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80,
1144
            1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1,
1145
            96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96,
1146
            1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1,
1147
            96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 128, 0, 136, 0, 136, 0, 136, 0, 136,
1148
            0, 136, 0, 136, 0, 136, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
1149
            2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
1150
            0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0,
1151
            168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0,
1152
            168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 168, 0,
1153
            168, 0, 168, 0, 168, 0, 168, 0, 168, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0,
1154
            200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0,
1155
            200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0, 200, 0,
1156
            200, 0, 200, 0, 200, 0, 200, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0,
1157
            232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0,
1158
            232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0, 232, 0,
1159
            232, 0, 232, 0, 232, 0, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8,
1160
            1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1,
1161
            8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40,
1162
            1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1,
1163
            40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40,
1164
            1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1,
1165
            72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72,
1166
            1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 104, 1, 104, 1, 104, 1, 104, 1,
1167
            104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1,
1168
            104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1,
1169
            104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1,
1170
            136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1,
1171
            136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1,
1172
            136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1,
1173
            168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1,
1174
            168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 1,
1175
            168, 1, 168, 1, 168, 1, 168, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1,
1176
            200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1,
1177
            200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1, 200, 1,
1178
            200, 1, 200, 1, 200, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1,
1179
            232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1,
1180
            232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1, 232, 1,
1181
            232, 1, 232, 1, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2,
1182
            8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 8,
1183
            2, 8, 2, 8, 2, 8, 2, 8, 2, 8, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40,
1184
            2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2,
1185
            40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 40, 2, 72,
1186
            2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2,
1187
            72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72,
1188
            2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 72, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2,
1189
            104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2,
1190
            104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 104, 2,
1191
            104, 2, 104, 2, 104, 2, 104, 2, 104, 2, 244, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1192
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1193
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1194
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1195
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1196
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1197
            2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
1198
            4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6,
1199
            6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
1200
            8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10,
1201
            10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11,
1202
            11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
1203
            12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14,
1204
            14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15,
1205
            15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 0,
1206
        ];
1207
        assert_eq!(trie_serialized, EXP_TRIE_SERIALIZED);
1✔
1208

1209
        let trie_deserialized = postcard::from_bytes::<CodePointTrie<u8>>(&trie_serialized)?;
1✔
1210

1211
        assert_eq!(&trie.index, &trie_deserialized.index);
1✔
1212
        assert_eq!(&trie.data, &trie_deserialized.data);
1✔
1213

1214
        assert!(!trie_deserialized.index.is_owned());
1✔
1215
        assert!(!trie_deserialized.data.is_owned());
1✔
1216

1217
        Ok(())
1✔
1218
    }
2✔
1219

1220
    #[test]
1221
    fn test_get_range() {
2✔
1222
        let planes_trie = planes::get_planes_trie();
1✔
1223

1224
        let first_range: Option<CodePointMapRange<u8>> = planes_trie.get_range(0x0);
1✔
1225
        assert_eq!(
1✔
1226
            first_range,
1227
            Some(CodePointMapRange {
1228
                range: RangeInclusive::new(0x0, 0xffff),
1229
                value: 0
1230
            })
1231
        );
1232

1233
        let second_range: Option<CodePointMapRange<u8>> = planes_trie.get_range(0x1_0000);
1✔
1234
        assert_eq!(
1✔
1235
            second_range,
1236
            Some(CodePointMapRange {
1237
                range: RangeInclusive::new(0x10000, 0x1ffff),
1238
                value: 1
1239
            })
1240
        );
1241

1242
        let penultimate_range: Option<CodePointMapRange<u8>> = planes_trie.get_range(0xf_0000);
1✔
1243
        assert_eq!(
1✔
1244
            penultimate_range,
1245
            Some(CodePointMapRange {
1246
                range: RangeInclusive::new(0xf_0000, 0xf_ffff),
1247
                value: 15
1248
            })
1249
        );
1250

1251
        let last_range: Option<CodePointMapRange<u8>> = planes_trie.get_range(0x10_0000);
1✔
1252
        assert_eq!(
1✔
1253
            last_range,
1254
            Some(CodePointMapRange {
1255
                range: RangeInclusive::new(0x10_0000, 0x10_ffff),
1256
                value: 16
1257
            })
1258
        );
1259
    }
2✔
1260

1261
    #[test]
1262
    fn databake() {
2✔
1263
        databake::test_bake!(
2✔
1264
            CodePointTrie<'static, u32>,
1265
            const: crate::codepointtrie::CodePointTrie::from_parts(
1✔
1266
                crate::codepointtrie::CodePointTrieHeader {
1✔
1267
                    high_start: 1u32,
1268
                    shifted12_high_start: 2u16,
1269
                    index3_null_offset: 3u16,
1270
                    data_null_offset: 4u32,
1271
                    null_value: 5u32,
1272
                    trie_type: crate::codepointtrie::TrieType::Small,
1273
                },
1274
                zerovec::ZeroVec::new(),
1✔
1275
                zerovec::ZeroVec::new(),
1✔
1276
                0u32,
1277
            ),
1✔
1278
            icu_collections,
1279
            [zerovec],
1280
        );
1281
    }
2✔
1282
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc