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

zbraniecki / icu4x / 11904027177

19 Nov 2024 12:33AM UTC coverage: 75.477% (+0.3%) from 75.174%
11904027177

push

github

web-flow
Move DateTimePattern into pattern module (#5834)

#1317

Also removes `NeoNeverMarker` and fixes #5689

258 of 319 new or added lines in 6 files covered. (80.88%)

6967 existing lines in 278 files now uncovered.

54522 of 72237 relevant lines covered (75.48%)

655305.49 hits per line

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

95.12
/components/collections/src/codepointinvlist/cpinvlist.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
#[cfg(feature = "serde")]
6
use alloc::format;
7
#[cfg(feature = "serde")]
8
use alloc::string::String;
9
use alloc::vec::Vec;
10
use core::{char, ops::RangeBounds, ops::RangeInclusive};
11
use potential_utf::PotentialCodePoint;
12
use yoke::Yokeable;
13
use zerofrom::ZeroFrom;
14
use zerovec::{ule::AsULE, zerovec, ZeroVec};
15

16
use super::InvalidSetError;
17
use crate::codepointinvlist::utils::{deconstruct_range, is_valid_zv};
18

19
/// Represents the end code point of the Basic Multilingual Plane range, starting from code point 0, inclusive
20
const BMP_MAX: u32 = 0xFFFF;
21

22
/// Represents the inversion list for a set of all code points in the Basic Multilingual Plane.
23
const BMP_INV_LIST_VEC: ZeroVec<PotentialCodePoint> = zerovec!(PotentialCodePoint; PotentialCodePoint::to_unaligned; [PotentialCodePoint::from_u24(0x0), PotentialCodePoint::from_u24(BMP_MAX + 1)]);
24

25
/// Represents the inversion list for all of the code points in the Unicode range.
26
const ALL_VEC: ZeroVec<PotentialCodePoint> = zerovec!(PotentialCodePoint; PotentialCodePoint::to_unaligned; [PotentialCodePoint::from_u24(0x0), PotentialCodePoint::from_u24((char::MAX as u32) + 1)]);
27

28
/// A membership wrapper for [`CodePointInversionList`].
29
///
30
/// Provides exposure to membership functions and constructors from serialized `CodePointSet`s (sets of code points)
31
/// and predefined ranges.
32
#[zerovec::make_varule(CodePointInversionListULE)]
495,595✔
33
#[zerovec::skip_derive(Ord)]
34
#[zerovec::derive(Debug)]
35
#[derive(Debug, Eq, PartialEq, Clone, Yokeable, ZeroFrom)]
25,243✔
36
pub struct CodePointInversionList<'data> {
37
    // If we wanted to use an array to keep the memory on the stack, there is an unsafe nightly feature
38
    // https://doc.rust-lang.org/nightly/core/array/trait.FixedSizeArray.html
39
    // Allows for traits of fixed size arrays
40

41
    // Implements an [inversion list.](https://en.wikipedia.org/wiki/Inversion_list)
42
    inv_list: ZeroVec<'data, PotentialCodePoint>,
11,515✔
43
    size: u32,
11,515✔
44
}
45

46
#[cfg(feature = "serde")]
47
impl<'de: 'a, 'a> serde::Deserialize<'de> for CodePointInversionList<'a> {
48
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170✔
49
    where
50
        D: serde::Deserializer<'de>,
51
    {
52
        use serde::de::Error;
53

54
        let parsed_inv_list = if deserializer.is_human_readable() {
338✔
55
            let parsed_strings = Vec::<alloc::borrow::Cow<'de, str>>::deserialize(deserializer)?;
4✔
56
            let mut inv_list = ZeroVec::new_owned(Vec::with_capacity(parsed_strings.len() * 2));
2✔
57
            for range in parsed_strings {
8✔
58
                fn internal(range: &str) -> Option<(u32, u32)> {
3✔
59
                    let (start, range) = UnicodeCodePoint::parse(range)?;
3✔
60
                    if range.is_empty() {
3✔
UNCOV
61
                        return Some((start.0, start.0));
×
62
                    }
63
                    let (hyphen, range) = UnicodeCodePoint::parse(range)?;
3✔
64
                    if hyphen.0 != '-' as u32 {
3✔
UNCOV
65
                        return None;
×
66
                    }
67
                    let (end, range) = UnicodeCodePoint::parse(range)?;
6✔
68
                    range.is_empty().then_some((start.0, end.0))
3✔
69
                }
3✔
70
                let (start, end) = internal(&range).ok_or_else(|| Error::custom(format!(
3✔
71
                    "Cannot deserialize invalid inversion list for CodePointInversionList: {range:?}"
UNCOV
72
                )))?;
×
73
                inv_list.with_mut(|v| {
6✔
74
                    v.push(PotentialCodePoint::from_u24(start).to_unaligned());
3✔
75
                    v.push(PotentialCodePoint::from_u24(end + 1).to_unaligned());
3✔
76
                });
3✔
77
            }
5✔
78
            inv_list
2✔
79
        } else {
4✔
80
            ZeroVec::<PotentialCodePoint>::deserialize(deserializer)?
166✔
81
        };
82
        CodePointInversionList::try_from_inversion_list(parsed_inv_list).map_err(|e| {
168✔
UNCOV
83
            Error::custom(format!(
×
84
                "Cannot deserialize invalid inversion list for CodePointInversionList: {e:?}"
85
            ))
UNCOV
86
        })
×
87
    }
176✔
88
}
89

90
#[cfg(feature = "databake")]
91
impl databake::Bake for CodePointInversionList<'_> {
92
    fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
1✔
93
        env.insert("icu_collections");
1✔
94
        let inv_list = self.inv_list.bake(env);
1✔
95
        let size = self.size.bake(env);
1✔
96
        // Safe because our parts are safe.
97
        databake::quote! { unsafe {
1✔
98
            #[allow(unused_unsafe)]
99
            icu_collections::codepointinvlist::CodePointInversionList::from_parts_unchecked(#inv_list, #size)
100
        }}
101
    }
1✔
102
}
103

104
#[cfg(feature = "databake")]
105
impl databake::BakeSize for CodePointInversionList<'_> {
106
    fn borrows_size(&self) -> usize {
×
107
        self.inv_list.borrows_size()
×
UNCOV
108
    }
×
109
}
110

111
#[cfg(feature = "serde")]
UNCOV
112
#[derive(Debug, Copy, Clone)]
×
UNCOV
113
struct UnicodeCodePoint(u32);
×
114

115
#[cfg(feature = "serde")]
116
impl UnicodeCodePoint {
117
    fn from_u32(cp: u32) -> Result<Self, String> {
6✔
118
        if cp <= char::MAX as u32 {
6✔
119
            Ok(Self(cp))
6✔
120
        } else {
UNCOV
121
            Err(format!("Not a Unicode code point {}", cp))
×
122
        }
123
    }
6✔
124

125
    fn parse(value: &str) -> Option<(Self, &str)> {
9✔
126
        Some(if let Some(hex) = value.strip_prefix("U+") {
18✔
127
            let (escape, remainder) = (hex.get(..4)?, hex.get(4..)?);
2✔
128
            (Self(u32::from_str_radix(escape, 16).ok()?), remainder)
2✔
129
        } else {
130
            let c = value.chars().next()?;
7✔
131
            (Self(c as u32), value.get(c.len_utf8()..)?)
16✔
132
        })
133
    }
9✔
134
}
135

136
#[cfg(feature = "serde")]
137
impl core::fmt::Display for UnicodeCodePoint {
138
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6✔
139
        match self.0 {
140
            s @ 0xD800..=0xDFFF => write!(f, "U+{s:X}"),
6✔
141
            // SAFETY: c <= char::MAX by construction, and not a surrogate
142
            c => write!(f, "{}", unsafe { char::from_u32_unchecked(c) }),
4✔
143
        }
144
    }
6✔
145
}
146

147
#[cfg(feature = "serde")]
148
impl serde::Serialize for CodePointInversionList<'_> {
149
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
174✔
150
    where
151
        S: serde::Serializer,
152
    {
153
        if serializer.is_human_readable() {
176✔
154
            use serde::ser::Error;
155
            use serde::ser::SerializeSeq;
156
            let mut seq = serializer.serialize_seq(Some(self.inv_list.len() / 2))?;
2✔
157
            for range in self.iter_ranges() {
8✔
158
                let start = UnicodeCodePoint::from_u32(*range.start()).map_err(S::Error::custom)?;
3✔
159
                if range.start() == range.end() {
3✔
UNCOV
160
                    seq.serialize_element(&format!("{start}"))?;
×
161
                } else {
162
                    let end = UnicodeCodePoint::from_u32(*range.end()).map_err(S::Error::custom)?;
3✔
163
                    seq.serialize_element(&format!("{start}-{end}",))?;
177✔
164
                }
165
            }
166
            seq.end()
2✔
167
        } else {
2✔
168
            // Note: serde(flatten) currently does not promote a struct field of type Vec
169
            // to replace the struct when serializing. The error message from the default
170
            // serialization is: "can only flatten structs and maps (got a sequence)".
171
            self.inv_list.serialize(serializer)
166✔
172
        }
173
    }
168✔
174
}
175

176
impl<'data> CodePointInversionList<'data> {
177
    /// Returns a new [`CodePointInversionList`] from an [inversion list](https://en.wikipedia.org/wiki/Inversion_list)
178
    /// represented as a [`ZeroVec`]`<`[`PotentialCodePoint`]`>` of code points.
179
    ///
180
    /// The inversion list must be of even length, sorted ascending non-overlapping,
181
    /// and within the bounds of `0x0 -> 0x10FFFF` inclusive, and end points being exclusive.
182
    ///
183
    /// # Examples
184
    ///
185
    /// ```
186
    /// use icu::collections::codepointinvlist::CodePointInversionList;
187
    /// use icu::collections::codepointinvlist::InvalidSetError;
188
    /// use zerovec::ZeroVec;
189
    /// use potential_utf::PotentialCodePoint;
190
    ///
191
    /// let valid = [0x0, 0x10000];
192
    /// let inv_list: ZeroVec<PotentialCodePoint> = valid.into_iter().map(PotentialCodePoint::from_u24).collect();
193
    /// let result = CodePointInversionList::try_from_inversion_list(inv_list);
194
    /// assert!(matches!(result, CodePointInversionList));
195
    ///
196
    /// let invalid = vec![0x0, 0x80, 0x3];
197
    /// let inv_list: ZeroVec<PotentialCodePoint> = invalid.iter().copied().map(PotentialCodePoint::from_u24).collect();
198
    /// let result = CodePointInversionList::try_from_inversion_list(inv_list);
199
    /// assert!(matches!(
200
    ///     result,
201
    ///     Err(InvalidSetError(_))
202
    /// ));
203
    /// if let Err(InvalidSetError(actual)) = result {
204
    ///     assert_eq!(&invalid, &actual.into_iter().map(u32::from).collect::<Vec<_>>());
205
    /// }
206
    /// ```
207
    pub fn try_from_inversion_list(
4,641✔
208
        inv_list: ZeroVec<'data, PotentialCodePoint>,
209
    ) -> Result<Self, InvalidSetError> {
210
        #[allow(clippy::indexing_slicing)] // chunks
211
        if is_valid_zv(&inv_list) {
9,282✔
212
            let size = inv_list
4,639✔
213
                .as_ule_slice()
214
                .chunks(2)
215
                .map(|end_points| {
2,252,552✔
216
                    u32::from(<PotentialCodePoint as AsULE>::from_unaligned(end_points[1]))
2,252,552✔
217
                        - u32::from(<PotentialCodePoint as AsULE>::from_unaligned(end_points[0]))
2,252,552✔
218
                })
2,252,552✔
219
                .sum::<u32>();
220
            Ok(Self { inv_list, size })
4,639✔
221
        } else {
222
            Err(InvalidSetError(inv_list.to_vec()))
2✔
223
        }
224
    }
4,641✔
225

226
    #[doc(hidden)] // databake internal
227
    pub const unsafe fn from_parts_unchecked(
1✔
228
        inv_list: ZeroVec<'data, PotentialCodePoint>,
1✔
229
        size: u32,
230
    ) -> Self {
231
        Self { inv_list, size }
1✔
232
    }
1✔
233

234
    /// Returns a new, fully-owned [`CodePointInversionList`] by cloning an [inversion list](https://en.wikipedia.org/wiki/Inversion_list)
235
    /// represented as a slice of [`PotentialCodePoint`] code points.
236
    ///
237
    /// The inversion list must be of even length, sorted ascending non-overlapping,
238
    /// and within the bounds of `0x0 -> 0x10FFFF` inclusive, and end points being exclusive.
239
    ///
240
    /// # Examples
241
    ///
242
    /// ```
243
    /// use icu::collections::codepointinvlist::CodePointInversionList;
244
    ///
245
    /// let bmp_list = &[0x0, 0x10000];
246
    /// let smp_list = &[0x10000, 0x20000];
247
    /// let sip_list = &[0x20000, 0x30000];
248
    ///
249
    /// let lists: Vec<CodePointInversionList> =
250
    ///     [&bmp_list[..], smp_list, sip_list]
251
    ///         .into_iter()
252
    ///         .map(|l| {
253
    ///             CodePointInversionList::try_from_u32_inversion_list_slice(l)
254
    ///                 .unwrap()
255
    ///         })
256
    ///         .collect();
257
    ///
258
    /// let bmp = &lists[0];
259
    /// assert!(bmp.contains32(0xFFFF));
260
    /// assert!(!bmp.contains32(0x10000));
261
    ///
262
    /// assert!(!lists.iter().any(|set| set.contains32(0x40000)));
263
    /// ```
264
    pub fn try_from_u32_inversion_list_slice(inv_list: &[u32]) -> Result<Self, InvalidSetError> {
102✔
265
        let inv_list_zv: ZeroVec<PotentialCodePoint> = inv_list
102✔
266
            .iter()
267
            .copied()
268
            .map(PotentialCodePoint::from_u24)
269
            .collect();
270
        CodePointInversionList::try_from_inversion_list(inv_list_zv)
102✔
271
    }
102✔
272

273
    /// Attempts to convert this list into a fully-owned one. No-op if already fully owned
UNCOV
274
    pub fn into_owned(self) -> CodePointInversionList<'static> {
×
UNCOV
275
        CodePointInversionList {
×
UNCOV
276
            inv_list: self.inv_list.into_owned(),
×
UNCOV
277
            size: self.size,
×
278
        }
UNCOV
279
    }
×
280

281
    /// Returns an owned inversion list representing the current [`CodePointInversionList`]
282
    pub fn get_inversion_list_vec(&self) -> Vec<u32> {
18✔
283
        self.as_inversion_list().iter().map(u32::from).collect()
18✔
284
    }
18✔
285

286
    /// Returns [`CodePointInversionList`] spanning entire Unicode range
287
    ///
288
    /// The range spans from `0x0 -> 0x10FFFF` inclusive.
289
    ///  
290
    /// # Examples
291
    ///
292
    /// ```
293
    /// use icu::collections::codepointinvlist::CodePointInversionList;
294
    ///
295
    /// let expected = [0x0, (char::MAX as u32) + 1];
296
    /// assert_eq!(
297
    ///     CodePointInversionList::all().get_inversion_list_vec(),
298
    ///     expected
299
    /// );
300
    /// assert_eq!(
301
    ///     CodePointInversionList::all().size(),
302
    ///     (expected[1] - expected[0]) as usize
303
    /// );
304
    /// ```
305
    pub fn all() -> Self {
10,792✔
306
        Self {
10,792✔
307
            inv_list: ALL_VEC,
308
            size: (char::MAX as u32) + 1,
309
        }
310
    }
10,792✔
311

312
    /// Returns [`CodePointInversionList`] spanning BMP range
313
    ///
314
    /// The range spans from `0x0 -> 0xFFFF` inclusive.
315
    ///
316
    /// # Examples
317
    ///
318
    /// ```
319
    /// use icu::collections::codepointinvlist::CodePointInversionList;
320
    ///
321
    /// const BMP_MAX: u32 = 0xFFFF;
322
    ///
323
    /// let expected = [0x0, BMP_MAX + 1];
324
    /// assert_eq!(
325
    ///     CodePointInversionList::bmp().get_inversion_list_vec(),
326
    ///     expected
327
    /// );
328
    /// assert_eq!(
329
    ///     CodePointInversionList::bmp().size(),
330
    ///     (expected[1] - expected[0]) as usize
331
    /// );
332
    /// ```
333
    pub fn bmp() -> Self {
3✔
334
        Self {
3✔
335
            inv_list: BMP_INV_LIST_VEC,
336
            size: BMP_MAX + 1,
337
        }
338
    }
3✔
339

340
    /// Returns the inversion list as a slice
341
    ///
342
    /// Public only to the crate, not exposed to public
343
    pub(crate) fn as_inversion_list(&self) -> &ZeroVec<PotentialCodePoint> {
379✔
344
        &self.inv_list
345
    }
379✔
346

347
    /// Yields an [`Iterator`] going through the character set in the [`CodePointInversionList`]
348
    ///
349
    /// # Examples
350
    ///
351
    /// ```
352
    /// use icu::collections::codepointinvlist::CodePointInversionList;
353
    /// let example_list = [0x41, 0x44, 0x45, 0x46];
354
    /// let example =
355
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
356
    ///         .unwrap();
357
    /// let mut ex_iter_chars = example.iter_chars();
358
    /// assert_eq!(Some('A'), ex_iter_chars.next());
359
    /// assert_eq!(Some('B'), ex_iter_chars.next());
360
    /// assert_eq!(Some('C'), ex_iter_chars.next());
361
    /// assert_eq!(Some('E'), ex_iter_chars.next());
362
    /// assert_eq!(None, ex_iter_chars.next());
363
    /// ```
364
    pub fn iter_chars(&self) -> impl Iterator<Item = char> + '_ {
15✔
365
        #[allow(clippy::indexing_slicing)] // chunks
366
        self.inv_list
15✔
367
            .as_ule_slice()
368
            .chunks(2)
369
            .flat_map(|pair| {
6✔
370
                u32::from(PotentialCodePoint::from_unaligned(pair[0]))
6✔
371
                    ..u32::from(PotentialCodePoint::from_unaligned(pair[1]))
6✔
372
            })
6✔
373
            .filter_map(char::from_u32)
374
    }
15✔
375

376
    /// Yields an [`Iterator`] returning the ranges of the code points that are
377
    /// included in the [`CodePointInversionList`]
378
    ///
379
    /// Ranges are returned as [`RangeInclusive`], which is inclusive of its
380
    /// `end` bound value. An end-inclusive behavior matches the ICU4C/J
381
    /// behavior of ranges, ex: `CodePointInversionList::contains(UChar32 start, UChar32 end)`.
382
    ///
383
    /// # Example
384
    ///
385
    /// ```
386
    /// use icu::collections::codepointinvlist::CodePointInversionList;
387
    /// let example_list = [0x41, 0x44, 0x45, 0x46];
388
    /// let example =
389
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
390
    ///         .unwrap();
391
    /// let mut example_iter_ranges = example.iter_ranges();
392
    /// assert_eq!(Some(0x41..=0x43), example_iter_ranges.next());
393
    /// assert_eq!(Some(0x45..=0x45), example_iter_ranges.next());
394
    /// assert_eq!(None, example_iter_ranges.next());
395
    /// ```
396
    pub fn iter_ranges(&self) -> impl ExactSizeIterator<Item = RangeInclusive<u32>> + '_ {
248✔
397
        #[allow(clippy::indexing_slicing)] // chunks
398
        self.inv_list.as_ule_slice().chunks(2).map(|pair| {
22,128✔
399
            let range_start = u32::from(PotentialCodePoint::from_unaligned(pair[0]));
21,880✔
400
            let range_limit = u32::from(PotentialCodePoint::from_unaligned(pair[1]));
21,880✔
401
            range_start..=(range_limit - 1)
21,880✔
402
        })
21,880✔
403
    }
248✔
404

405
    /// Yields an [`Iterator`] returning the ranges of the code points that are
406
    /// *not* included in the [`CodePointInversionList`]
407
    ///
408
    /// Ranges are returned as [`RangeInclusive`], which is inclusive of its
409
    /// `end` bound value. An end-inclusive behavior matches the ICU4C/J
410
    /// behavior of ranges, ex: `CodePointInversionList::contains(UChar32 start, UChar32 end)`.
411
    ///
412
    /// # Example
413
    ///
414
    /// ```
415
    /// use icu::collections::codepointinvlist::CodePointInversionList;
416
    /// let example_list = [0x41, 0x44, 0x45, 0x46];
417
    /// let example =
418
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
419
    ///         .unwrap();
420
    /// let mut example_iter_ranges = example.iter_ranges_complemented();
421
    /// assert_eq!(Some(0..=0x40), example_iter_ranges.next());
422
    /// assert_eq!(Some(0x44..=0x44), example_iter_ranges.next());
423
    /// assert_eq!(Some(0x46..=char::MAX as u32), example_iter_ranges.next());
424
    /// assert_eq!(None, example_iter_ranges.next());
425
    /// ```
426
    pub fn iter_ranges_complemented(&self) -> impl Iterator<Item = RangeInclusive<u32>> + '_ {
66✔
427
        let inv_ule = self.inv_list.as_ule_slice();
66✔
428
        let middle = inv_ule.get(1..inv_ule.len() - 1).unwrap_or(&[]);
66✔
429
        let beginning = if let Some(first) = self.inv_list.first() {
66✔
430
            let first = u32::from(first);
66✔
431
            if first == 0 {
66✔
432
                None
5✔
433
            } else {
434
                Some(0..=first - 1)
61✔
435
            }
436
        } else {
UNCOV
437
            None
×
438
        };
439
        let end = if let Some(last) = self.inv_list.last() {
66✔
440
            let last = u32::from(last);
66✔
441
            if last == char::MAX as u32 {
66✔
UNCOV
442
                None
×
443
            } else {
444
                Some(last..=char::MAX as u32)
66✔
445
            }
446
        } else {
UNCOV
447
            None
×
448
        };
449
        #[allow(clippy::indexing_slicing)] // chunks
450
        let chunks = middle.chunks(2).map(|pair| {
17,578✔
451
            let range_start = u32::from(PotentialCodePoint::from_unaligned(pair[0]));
17,512✔
452
            let range_limit = u32::from(PotentialCodePoint::from_unaligned(pair[1]));
17,512✔
453
            range_start..=(range_limit - 1)
17,512✔
454
        });
17,512✔
455
        beginning.into_iter().chain(chunks).chain(end)
66✔
456
    }
66✔
457

458
    /// Returns the number of ranges contained in this [`CodePointInversionList`]
459
    pub fn get_range_count(&self) -> usize {
1✔
460
        self.inv_list.len() / 2
1✔
461
    }
1✔
462

463
    /// Returns a specific range contained in this [`CodePointInversionList`] by index.
464
    /// Intended for use in FFI.
465
    pub fn get_nth_range(&self, idx: usize) -> Option<RangeInclusive<u32>> {
4✔
466
        let start_idx = idx * 2;
4✔
467
        let end_idx = start_idx + 1;
4✔
468
        let start = u32::from(self.inv_list.get(start_idx)?);
4✔
469
        let end = u32::from(self.inv_list.get(end_idx)?);
4✔
470
        Some(start..=(end - 1))
3✔
471
    }
4✔
472

473
    /// Returns the number of elements of the [`CodePointInversionList`]
474
    pub fn size(&self) -> usize {
307✔
475
        if self.is_empty() {
307✔
476
            return 0;
57✔
477
        }
478
        self.size as usize
250✔
479
    }
307✔
480

481
    /// Returns whether or not the [`CodePointInversionList`] is empty
482
    pub fn is_empty(&self) -> bool {
311✔
483
        self.inv_list.is_empty()
311✔
484
    }
311✔
485

486
    /// Wrapper for contains
487
    ///
488
    /// Returns an [`Option`] as to whether or not it is possible for the query to be contained.
489
    /// The value in the [`Option`] is the start index of the range that contains the query.
490
    fn contains_query(&self, query: u32) -> Option<usize> {
3,229,675✔
491
        let query = PotentialCodePoint::try_from(query).ok()?;
3,229,675✔
492
        match self.inv_list.binary_search(&query) {
3,229,675✔
493
            Ok(pos) => {
18,705✔
494
                if pos % 2 == 0 {
18,705✔
495
                    Some(pos)
13,608✔
496
                } else {
497
                    None
5,097✔
498
                }
499
            }
500
            Err(pos) => {
3,210,970✔
501
                if pos % 2 != 0 && pos < self.inv_list.len() {
3,210,970✔
502
                    Some(pos - 1)
56,400✔
503
                } else {
504
                    None
3,154,570✔
505
                }
506
            }
507
        }
508
    }
3,229,675✔
509

510
    /// Checks to see the query is in the [`CodePointInversionList`]
511
    ///
512
    /// Runs a binary search in `O(log(n))` where `n` is the number of start and end points
513
    /// in the set using [`core`] implementation
514
    ///
515
    /// # Examples
516
    ///
517
    /// ```
518
    /// use icu::collections::codepointinvlist::CodePointInversionList;
519
    /// let example_list = [0x41, 0x43, 0x44, 0x45];
520
    /// let example =
521
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
522
    ///         .unwrap();
523
    /// assert!(example.contains('A'));
524
    /// assert!(!example.contains('C'));
525
    /// ```
526
    pub fn contains(&self, query: char) -> bool {
760,713✔
527
        self.contains_query(query as u32).is_some()
760,713✔
528
    }
760,713✔
529

530
    /// Checks to see the unsigned int is in the [`CodePointInversionList::all()`](CodePointInversionList::all())
531
    ///
532
    /// Note: Even though [`u32`] and [`prim@char`] in Rust are non-negative 4-byte
533
    /// values, there is an important difference. A [`u32`] can take values up to
534
    /// a very large integer value, while a [`prim@char`] in Rust is defined to be in
535
    /// the range from 0 to the maximum valid Unicode Scalar Value.
536
    ///
537
    /// Runs a binary search in `O(log(n))` where `n` is the number of start and end points
538
    /// in the set using [`core`] implementation
539
    ///
540
    /// # Examples
541
    ///
542
    /// ```
543
    /// use icu::collections::codepointinvlist::CodePointInversionList;
544
    /// let example_list = [0x41, 0x43, 0x44, 0x45];
545
    /// let example =
546
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
547
    ///         .unwrap();
548
    /// assert!(example.contains32(0x41));
549
    /// assert!(!example.contains32(0x43));
550
    /// ```
551
    pub fn contains32(&self, query: u32) -> bool {
2,468,930✔
552
        self.contains_query(query).is_some()
2,468,930✔
553
    }
2,468,930✔
554

555
    /// Checks to see if the range is in the [`CodePointInversionList`]
556
    ///
557
    /// Runs a binary search in `O(log(n))` where `n` is the number of start and end points
558
    /// in the set using [`Vec`] implementation. Only runs the search once on the `start`
559
    /// parameter, while the `end` parameter is checked in a single `O(1)` step.
560
    ///
561
    /// # Examples
562
    ///
563
    /// ```
564
    /// use icu::collections::codepointinvlist::CodePointInversionList;
565
    /// let example_list = [0x41, 0x43, 0x44, 0x45];
566
    /// let example =
567
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
568
    ///         .unwrap();
569
    /// assert!(example.contains_range('A'..'C'));
570
    /// assert!(example.contains_range('A'..='B'));
571
    /// assert!(!example.contains_range('A'..='C'));
572
    /// ```
573
    ///
574
    /// Surrogate points (`0xD800 -> 0xDFFF`) will return [`false`] if the Range contains them but the
575
    /// [`CodePointInversionList`] does not.
576
    ///
577
    /// Note: when comparing to ICU4C/J, keep in mind that `Range`s in Rust are
578
    /// constructed inclusive of start boundary and exclusive of end boundary.
579
    /// The ICU4C/J `CodePointInversionList::contains(UChar32 start, UChar32 end)` method
580
    /// differs by including the end boundary.
581
    ///
582
    /// # Examples
583
    ///
584
    /// ```
585
    /// use icu::collections::codepointinvlist::CodePointInversionList;
586
    /// use std::char;
587
    /// let check =
588
    ///     char::from_u32(0xD7FE).unwrap()..char::from_u32(0xE001).unwrap();
589
    /// let example_list = [0xD7FE, 0xD7FF, 0xE000, 0xE001];
590
    /// let example =
591
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
592
    ///         .unwrap();
593
    /// assert!(!example.contains_range(check));
594
    /// ```
595
    pub fn contains_range(&self, range: impl RangeBounds<char>) -> bool {
9✔
596
        let (from, till) = deconstruct_range(range);
9✔
597
        if from >= till {
9✔
598
            return false;
2✔
599
        }
600
        match self.contains_query(from) {
7✔
601
            Some(pos) => {
4✔
602
                if let Some(x) = self.inv_list.get(pos + 1) {
4✔
603
                    (till) <= x.into()
4✔
604
                } else {
UNCOV
605
                    debug_assert!(
×
606
                        false,
607
                        "Inversion list query should not return out of bounds index"
608
                    );
609
                    false
610
                }
611
            }
612
            None => false,
3✔
613
        }
614
    }
9✔
615

616
    /// Check if the calling [`CodePointInversionList`] contains all the characters of the given [`CodePointInversionList`]
617
    ///
618
    /// # Examples
619
    ///
620
    /// ```
621
    /// use icu::collections::codepointinvlist::CodePointInversionList;
622
    /// let example_list = [0x41, 0x46, 0x55, 0x5B]; // A - E, U - Z
623
    /// let example =
624
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
625
    ///         .unwrap();
626
    /// let a_to_d =
627
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&[0x41, 0x45])
628
    ///         .unwrap();
629
    /// let f_to_t =
630
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&[0x46, 0x55])
631
    ///         .unwrap();
632
    /// let r_to_x =
633
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&[0x52, 0x58])
634
    ///         .unwrap();
635
    /// assert!(example.contains_set(&a_to_d)); // contains all
636
    /// assert!(!example.contains_set(&f_to_t)); // contains none
637
    /// assert!(!example.contains_set(&r_to_x)); // contains some
638
    /// ```
639
    pub fn contains_set(&self, set: &Self) -> bool {
5✔
640
        if set.size() > self.size() {
5✔
641
            return false;
2✔
642
        }
643

644
        let mut set_ranges = set.iter_ranges();
3✔
645
        let mut check_elem = set_ranges.next();
3✔
646

647
        let ranges = self.iter_ranges();
3✔
648
        for range in ranges {
10✔
649
            match check_elem {
8✔
650
                Some(ref check_range) => {
7✔
651
                    if check_range.start() >= range.start()
12✔
652
                        && check_range.end() <= &(range.end() + 1)
6✔
653
                    {
654
                        check_elem = set_ranges.next();
5✔
655
                    }
656
                }
657
                _ => break,
658
            }
659
        }
660
        check_elem.is_none()
3✔
661
    }
5✔
662

663
    /// Returns the end of the initial substring where the characters are either contained/not contained
664
    /// in the set.
665
    ///
666
    /// # Examples
667
    ///
668
    /// ```
669
    /// use icu::collections::codepointinvlist::CodePointInversionList;
670
    /// let example_list = [0x41, 0x44]; // {A, B, C}
671
    /// let example =
672
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
673
    ///         .unwrap();
674
    /// assert_eq!(example.span("CABXYZ", true), 3);
675
    /// assert_eq!(example.span("XYZC", false), 3);
676
    /// assert_eq!(example.span("XYZ", true), 0);
677
    /// assert_eq!(example.span("ABC", false), 0);
678
    /// ```
679
    pub fn span(&self, span_str: &str, contained: bool) -> usize {
8✔
680
        span_str
8✔
681
            .chars()
682
            .take_while(|&x| self.contains(x) == contained)
22✔
683
            .count()
684
    }
8✔
685

686
    /// Returns the start of the trailing substring (starting from end of string) where the characters are
687
    /// either contained/not contained in the set. Returns the length of the string if no valid return.
688
    ///
689
    /// # Examples
690
    ///
691
    /// ```
692
    /// use icu::collections::codepointinvlist::CodePointInversionList;
693
    /// let example_list = [0x41, 0x44]; // {A, B, C}
694
    /// let example =
695
    ///     CodePointInversionList::try_from_u32_inversion_list_slice(&example_list)
696
    ///         .unwrap();
697
    /// assert_eq!(example.span_back("XYZCAB", true), 3);
698
    /// assert_eq!(example.span_back("ABCXYZ", true), 6);
699
    /// assert_eq!(example.span_back("CABXYZ", false), 3);
700
    /// ```
701
    pub fn span_back(&self, span_str: &str, contained: bool) -> usize {
7✔
702
        span_str.len()
14✔
703
            - span_str
7✔
704
                .chars()
705
                .rev()
706
                .take_while(|&x| self.contains(x) == contained)
20✔
707
                .count()
708
    }
7✔
709
}
710

711
#[cfg(test)]
712
mod tests {
713
    use super::{CodePointInversionList, InvalidSetError};
714
    use std::{char, vec::Vec};
715
    use zerovec::ZeroVec;
716

717
    #[test]
718
    fn test_codepointinversionlist_try_from_vec() {
2✔
719
        let ex = vec![0x2, 0x3, 0x4, 0x5];
1✔
720
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
721
        assert_eq!(ex, check.get_inversion_list_vec());
2✔
722
        assert_eq!(2, check.size());
1✔
723
    }
2✔
724

725
    #[test]
726
    fn test_codepointinversionlist_try_from_vec_error() {
2✔
727
        let check = vec![0x1, 0x1, 0x2, 0x3, 0x4];
1✔
728
        let set = CodePointInversionList::try_from_u32_inversion_list_slice(&check);
1✔
729
        assert!(matches!(set, Err(InvalidSetError(_))));
1✔
730
        if let Err(InvalidSetError(actual)) = set {
1✔
731
            assert_eq!(
2✔
732
                &check,
1✔
733
                &actual.into_iter().map(u32::from).collect::<Vec<_>>()
1✔
734
            );
735
        }
736
    }
2✔
737

738
    // CodePointInversionList membership functions
739
    #[test]
740
    fn test_codepointinversionlist_contains_query() {
2✔
741
        let ex = vec![0x41, 0x46, 0x4B, 0x55];
1✔
742
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
743
        assert!(check.contains_query(0x40).is_none());
1✔
744
        assert_eq!(check.contains_query(0x41).unwrap(), 0);
1✔
745
        assert_eq!(check.contains_query(0x44).unwrap(), 0);
1✔
746
        assert!(check.contains_query(0x46).is_none());
1✔
747
        assert_eq!(check.contains_query(0x4C).unwrap(), 2);
1✔
748
        assert!(check.contains_query(0x56).is_none());
1✔
749
    }
2✔
750

751
    #[test]
752
    fn test_codepointinversionlist_contains() {
2✔
753
        let ex = vec![0x2, 0x5, 0xA, 0xF];
1✔
754
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
755
        assert!(check.contains(0x2 as char));
1✔
756
        assert!(check.contains(0x4 as char));
1✔
757
        assert!(check.contains(0xA as char));
1✔
758
        assert!(check.contains(0xE as char));
1✔
759
    }
2✔
760

761
    #[test]
762
    fn test_codepointinversionlist_contains_false() {
2✔
763
        let ex = vec![0x2, 0x5, 0xA, 0xF];
1✔
764
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
765
        assert!(!check.contains(0x1 as char));
1✔
766
        assert!(!check.contains(0x5 as char));
1✔
767
        assert!(!check.contains(0x9 as char));
1✔
768
        assert!(!check.contains(0xF as char));
1✔
769
        assert!(!check.contains(0x10 as char));
1✔
770
    }
2✔
771

772
    #[test]
773
    fn test_codepointinversionlist_contains_range() {
2✔
774
        let ex = vec![0x41, 0x46, 0x4B, 0x55];
1✔
775
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
776
        assert!(check.contains_range('A'..='E')); // 65 - 69
1✔
777
        assert!(check.contains_range('C'..'D')); // 67 - 67
1✔
778
        assert!(check.contains_range('L'..'P')); // 76 - 80
1✔
779
        assert!(!check.contains_range('L'..='U')); // 76 - 85
1✔
780
    }
2✔
781

782
    #[test]
783
    fn test_codepointinversionlist_contains_range_false() {
2✔
784
        let ex = vec![0x41, 0x46, 0x4B, 0x55];
1✔
785
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
786
        assert!(!check.contains_range('!'..'A')); // 33 - 65
1✔
787
        assert!(!check.contains_range('F'..'K')); // 70 - 74
1✔
788
        assert!(!check.contains_range('U'..)); // 85 - ..
1✔
789
    }
2✔
790

791
    #[test]
792
    fn test_codepointinversionlist_contains_range_invalid() {
2✔
793
        let check = CodePointInversionList::all();
1✔
794
        assert!(!check.contains_range('A'..'!')); // 65 - 33
1✔
795
        assert!(!check.contains_range('A'..'A')); // 65 - 65
1✔
796
    }
2✔
797

798
    #[test]
799
    fn test_codepointinversionlist_contains_set_u() {
2✔
800
        let ex = vec![0xA, 0x14, 0x28, 0x32, 0x46, 0x50, 0x64, 0x6E];
1✔
801
        let u = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
802
        let inside = vec![0xF, 0x14, 0x2C, 0x31, 0x46, 0x50, 0x64, 0x6D];
1✔
803
        let s = CodePointInversionList::try_from_u32_inversion_list_slice(&inside).unwrap();
1✔
804
        assert!(u.contains_set(&s));
1✔
805
    }
2✔
806

807
    #[test]
808
    fn test_codepointinversionlist_contains_set_u_false() {
2✔
809
        let ex = vec![0xA, 0x14, 0x28, 0x32, 0x46, 0x50, 0x64, 0x78];
1✔
810
        let u = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
811
        let outside = vec![0x0, 0xA, 0x16, 0x2C, 0x32, 0x46, 0x4F, 0x51, 0x6D, 0x6F];
1✔
812
        let s = CodePointInversionList::try_from_u32_inversion_list_slice(&outside).unwrap();
1✔
813
        assert!(!u.contains_set(&s));
1✔
814
    }
2✔
815

816
    #[test]
817
    fn test_codepointinversionlist_size() {
2✔
818
        let ex = vec![0x2, 0x5, 0xA, 0xF];
1✔
819
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
820
        assert_eq!(8, check.size());
1✔
821
        let check = CodePointInversionList::all();
1✔
822
        let expected = (char::MAX as u32) + 1;
1✔
823
        assert_eq!(expected as usize, check.size());
1✔
824
        let inv_list_vec = vec![];
1✔
825
        let check = CodePointInversionList {
1✔
826
            inv_list: ZeroVec::from_slice_or_alloc(&inv_list_vec),
1✔
827
            size: 0,
828
        };
829
        assert_eq!(check.size(), 0);
1✔
830
    }
2✔
831

832
    #[test]
833
    fn test_codepointinversionlist_is_empty() {
2✔
834
        let inv_list_vec = vec![];
1✔
835
        let check = CodePointInversionList {
1✔
836
            inv_list: ZeroVec::from_slice_or_alloc(&inv_list_vec),
1✔
837
            size: 0,
838
        };
839
        assert!(check.is_empty());
1✔
840
    }
2✔
841

842
    #[test]
843
    fn test_codepointinversionlist_is_not_empty() {
2✔
844
        let check = CodePointInversionList::all();
1✔
845
        assert!(!check.is_empty());
1✔
846
    }
2✔
847

848
    #[test]
849
    fn test_codepointinversionlist_iter_chars() {
2✔
850
        let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
1✔
851
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
852
        let mut iter = check.iter_chars();
1✔
853
        assert_eq!(Some('A'), iter.next());
1✔
854
        assert_eq!(Some('B'), iter.next());
1✔
855
        assert_eq!(Some('C'), iter.next());
1✔
856
        assert_eq!(Some('E'), iter.next());
1✔
857
        assert_eq!(None, iter.next());
1✔
858
    }
2✔
859

860
    #[test]
861
    fn test_codepointinversionlist_iter_ranges() {
2✔
862
        let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
1✔
863
        let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
864
        let mut ranges = set.iter_ranges();
1✔
865
        assert_eq!(Some(0x41..=0x43), ranges.next());
1✔
866
        assert_eq!(Some(0x45..=0x45), ranges.next());
1✔
867
        assert_eq!(Some(0xD800..=0xD800), ranges.next());
1✔
868
        assert_eq!(None, ranges.next());
1✔
869
    }
2✔
870

871
    #[test]
872
    fn test_codepointinversionlist_iter_ranges_exactsizeiter_trait() {
2✔
873
        let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
1✔
874
        let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
875
        let ranges = set.iter_ranges();
1✔
876
        assert_eq!(3, ranges.len());
1✔
877
    }
2✔
878

879
    #[test]
880
    fn test_codepointinversionlist_range_count() {
2✔
881
        let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
1✔
882
        let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
883
        assert_eq!(3, set.get_range_count());
1✔
884
    }
2✔
885

886
    #[test]
887
    fn test_codepointinversionlist_get_nth_range() {
2✔
888
        let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
1✔
889
        let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
890
        assert_eq!(Some(0x41..=0x43), set.get_nth_range(0));
1✔
891
        assert_eq!(Some(0x45..=0x45), set.get_nth_range(1));
1✔
892
        assert_eq!(Some(0xD800..=0xD800), set.get_nth_range(2));
1✔
893
        assert_eq!(None, set.get_nth_range(3));
1✔
894
    }
2✔
895

896
    // Range<char> cannot represent the upper bound (non-inclusive) for
897
    // char::MAX, whereas Range<u32> can.
898
    #[test]
899
    fn test_codepointinversionlist_iter_ranges_with_max_code_point() {
2✔
900
        let ex = vec![0x80, (char::MAX as u32) + 1];
1✔
901
        let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
902
        let mut ranges = set.iter_ranges();
1✔
903
        assert_eq!(Some(0x80..=(char::MAX as u32)), ranges.next());
1✔
904
        assert_eq!(None, ranges.next());
1✔
905
    }
2✔
906

907
    #[test]
908
    fn test_codepointinversionlist_span_contains() {
2✔
909
        let ex = vec![0x41, 0x44, 0x46, 0x4B]; // A - D, F - K
1✔
910
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
911
        assert_eq!(check.span("ABCDE", true), 3);
1✔
912
        assert_eq!(check.span("E", true), 0);
1✔
913
    }
2✔
914

915
    #[test]
916
    fn test_codepointinversionlist_span_does_not_contain() {
2✔
917
        let ex = vec![0x41, 0x44, 0x46, 0x4B]; // A - D, F - K
1✔
918
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
919
        assert_eq!(check.span("DEF", false), 2);
1✔
920
        assert_eq!(check.span("KLMA", false), 3);
1✔
921
    }
2✔
922

923
    #[test]
924
    fn test_codepointinversionlist_span_back_contains() {
2✔
925
        let ex = vec![0x41, 0x44, 0x46, 0x4B]; // A - D, F - K
1✔
926
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
927
        assert_eq!(check.span_back("XYZABFH", true), 3);
1✔
928
        assert_eq!(check.span_back("ABCXYZ", true), 6);
1✔
929
    }
2✔
930

931
    #[test]
932
    fn test_codepointinversionlist_span_back_does_not_contain() {
2✔
933
        let ex = vec![0x41, 0x44, 0x46, 0x4B]; // A - D, F - K
1✔
934
        let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1✔
935
        assert_eq!(check.span_back("ABCXYZ", false), 3);
1✔
936
        assert_eq!(check.span_back("XYZABC", false), 6);
1✔
937
    }
2✔
938

939
    #[test]
940
    fn test_uniset_to_inv_list() {
2✔
941
        let inv_list = [
1✔
942
            0x9, 0xE, 0x20, 0x21, 0x85, 0x86, 0xA0, 0xA1, 0x1626, 0x1627, 0x2000, 0x2003, 0x2028,
943
            0x202A, 0x202F, 0x2030, 0x205F, 0x2060, 0x3000, 0x3001,
944
        ];
945
        let s: CodePointInversionList =
946
            CodePointInversionList::try_from_u32_inversion_list_slice(&inv_list).unwrap();
1✔
947
        let round_trip_inv_list = s.get_inversion_list_vec();
1✔
948
        assert_eq!(
2✔
949
            round_trip_inv_list
1✔
950
                .into_iter()
951
                .map(u32::from)
952
                .collect::<ZeroVec<u32>>(),
953
            inv_list
954
        );
955
    }
2✔
956

957
    #[test]
958
    fn test_serde_serialize() {
2✔
959
        let inv_list = [0x41, 0x46, 0x4B, 0x55];
1✔
960
        let uniset = CodePointInversionList::try_from_u32_inversion_list_slice(&inv_list).unwrap();
1✔
961
        let json_str = serde_json::to_string(&uniset).unwrap();
1✔
962
        assert_eq!(json_str, r#"["A-E","K-T"]"#);
1✔
963
    }
2✔
964

965
    #[test]
966
    fn test_serde_serialize_surrogates() {
2✔
967
        let inv_list = [0xDFAB, 0xDFFF];
1✔
968
        let uniset = CodePointInversionList::try_from_u32_inversion_list_slice(&inv_list).unwrap();
1✔
969
        let json_str = serde_json::to_string(&uniset).unwrap();
1✔
970
        assert_eq!(json_str, r#"["U+DFAB-U+DFFE"]"#);
1✔
971
    }
2✔
972

973
    #[test]
974
    fn test_serde_deserialize() {
2✔
975
        let inv_list_str = r#"["A-E","K-T"]"#;
1✔
976
        let exp_inv_list = [0x41, 0x46, 0x4B, 0x55];
1✔
977
        let exp_uniset =
978
            CodePointInversionList::try_from_u32_inversion_list_slice(&exp_inv_list).unwrap();
1✔
979
        let act_uniset: CodePointInversionList = serde_json::from_str(inv_list_str).unwrap();
1✔
980
        assert_eq!(act_uniset, exp_uniset);
1✔
981
    }
2✔
982

983
    #[test]
984
    fn test_serde_deserialize_surrogates() {
2✔
985
        let inv_list_str = r#"["U+DFAB-U+DFFE"]"#;
1✔
986
        let exp_inv_list = [0xDFAB, 0xDFFF];
1✔
987
        let exp_uniset =
988
            CodePointInversionList::try_from_u32_inversion_list_slice(&exp_inv_list).unwrap();
1✔
989
        let act_uniset: CodePointInversionList = serde_json::from_str(inv_list_str).unwrap();
1✔
990
        assert_eq!(act_uniset, exp_uniset);
1✔
991
    }
2✔
992

993
    #[test]
994
    fn test_serde_deserialize_invalid() {
2✔
995
        assert!(serde_json::from_str::<CodePointInversionList>("[65,70,98775,85]").is_err());
1✔
996
        assert!(serde_json::from_str::<CodePointInversionList>("[65,70,U+FFFFFFFFFF,85]").is_err());
1✔
997
    }
2✔
998

999
    #[test]
1000
    fn test_serde_with_postcard_roundtrip() -> Result<(), postcard::Error> {
2✔
1001
        let set = CodePointInversionList::bmp();
1✔
1002
        let set_serialized: Vec<u8> = postcard::to_allocvec(&set).unwrap();
1✔
1003
        let set_deserialized: CodePointInversionList =
1004
            postcard::from_bytes::<CodePointInversionList>(&set_serialized)?;
1✔
1005

1006
        assert_eq!(&set, &set_deserialized);
1✔
1007
        assert!(!set_deserialized.inv_list.is_owned());
1✔
1008

1009
        Ok(())
1✔
1010
    }
2✔
1011

1012
    #[test]
1013
    fn databake() {
2✔
1014
        databake::test_bake!(
4✔
1015
            CodePointInversionList<'static>,
1016
            const,
1017
            unsafe {
1018
                #[allow(unused_unsafe)]
1019
                crate::codepointinvlist::CodePointInversionList::from_parts_unchecked(
1✔
1020
                    unsafe {
1021
                        zerovec::ZeroVec::from_bytes_unchecked(
1✔
1022
                            b"0\0\0\0:\0\0\0A\0\0\0G\0\0\0a\0\0\0g\0\0\0",
1023
                        )
1024
                    },
1025
                    22u32,
1026
                )
1027
            },
1028
            icu_collections,
1029
            [zerovec],
1030
        );
1031
    }
2✔
1032
}
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