1#[cfg(feature = "serde")]
6use alloc::format;
7#[cfg(feature = "serde")]
8use alloc::string::String;
9#[cfg(feature = "alloc")]
10use alloc::vec::Vec;
11use core::{char, ops::RangeBounds, ops::RangeInclusive};
12use potential_utf::PotentialCodePoint;
13use yoke::Yokeable;
14use zerofrom::ZeroFrom;
15use zerovec::{ZeroVec, ule::AsULE, zerovec};
16
17use super::InvalidSetError;
18use crate::codepointinvlist::utils::{deconstruct_range, is_valid_zv};
19
20const SENTINEL: u32 = char::MAX as u32 + 1;
22
23#[repr(C, packed)]
#[doc =
"[`VarULE`](zerovec::ule::VarULE) type for [`CodePointInversionList`]. See [`CodePointInversionList`] for documentation."]
#[allow(missing_docs)]
pub struct CodePointInversionListULE {
size: <u32 as zerovec::ule::AsULE>::ULE,
inv_list: zerovec::ZeroSlice<PotentialCodePoint>,
}
unsafe impl<'data> zerovec::ule::EncodeAsVarULE<CodePointInversionListULE> for
CodePointInversionList<'data> {
fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R)
-> R {
{
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("other two methods implemented")));
}
}
fn encode_var_ule_len(&self) -> usize {
::core::mem::size_of::<<u32 as zerovec::ule::AsULE>::ULE>() +
<zerovec::ZeroSlice<PotentialCodePoint> as
zerovec::ule::EncodeAsVarULE<zerovec::ZeroSlice<PotentialCodePoint>>>::encode_var_ule_len(&*self.inv_list)
}
fn encode_var_ule_write(&self, mut dst: &mut [u8]) {
if true {
{
match (&self.encode_var_ule_len(), &dst.len()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};
const ZERO: usize = 0;
;
const SIZE_0: usize =
::core::mem::size_of::<<u32 as zerovec::ule::AsULE>::ULE>();
const OFFSET_0: usize = ZERO + SIZE_0;
#[expect(clippy :: indexing_slicing)]
let out = &mut dst[ZERO..ZERO + SIZE_0];
let unaligned = zerovec::ule::AsULE::to_unaligned(self.size);
let unaligned_slice = &[unaligned];
let src =
<<u32 as zerovec::ule::AsULE>::ULE as
zerovec::ule::ULE>::slice_as_bytes(unaligned_slice);
out.copy_from_slice(src);
;
#[expect(clippy :: indexing_slicing)]
let out = &mut dst[OFFSET_0..];
<zerovec::ZeroSlice<PotentialCodePoint> as
zerovec::ule::EncodeAsVarULE<zerovec::ZeroSlice<PotentialCodePoint>>>::encode_var_ule_write(&*self.inv_list,
out)
}
}
unsafe impl<'data> zerovec::ule::EncodeAsVarULE<CodePointInversionListULE> for
&'_ CodePointInversionList<'data> {
fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R)
-> R {
{
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("other two methods implemented")));
}
}
fn encode_var_ule_len(&self) -> usize { (**self).encode_var_ule_len() }
fn encode_var_ule_write(&self, mut dst: &mut [u8]) {
(**self).encode_var_ule_write(dst)
}
}
impl<'data>
zerovec::__zerovec_internal_reexport::ZeroFrom<'data,
CodePointInversionListULE> for CodePointInversionList<'data> {
fn zero_from(other: &'data CodePointInversionListULE) -> Self {
Self {
size: <u32 as zerovec::ule::AsULE>::from_unaligned(other.size),
inv_list: <ZeroVec<'data, PotentialCodePoint> as
zerovec::__zerovec_internal_reexport::ZeroFrom<'data,
zerovec::ZeroSlice<PotentialCodePoint>>>::zero_from(&other.inv_list),
}
}
}
impl<'data> From<&'data CodePointInversionListULE> for
CodePointInversionList<'data> {
fn from(other: &'data CodePointInversionListULE) -> Self {
<Self as
zerovec::__zerovec_internal_reexport::ZeroFrom<'data,
CodePointInversionListULE>>::zero_from(other)
}
}
const __IMPL_VarULE_FOR_CodePointInversionListULE_ULE_SIZE: usize =
0 + ::core::mem::size_of::<<u32 as zerovec::ule::AsULE>::ULE>();
unsafe impl zerovec::ule::VarULE for CodePointInversionListULE {
#[inline]
fn validate_bytes(bytes_one: &[u8])
-> Result<(), zerovec::ule::UleError> {
if true {
{
match (&OFFSET_0,
&__IMPL_VarULE_FOR_CodePointInversionListULE_ULE_SIZE) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};
let Some(last_field_bytes) =
bytes_one.get(OFFSET_0..) else {
return Err(zerovec::ule::UleError::parse::<Self>());
};
const ZERO: usize = 0;
;
const SIZE_0: usize =
::core::mem::size_of::<<u32 as zerovec::ule::AsULE>::ULE>();
const OFFSET_0: usize = ZERO + SIZE_0;
if let Some(bytes) = bytes_one.get(ZERO..ZERO + SIZE_0) {
<<u32 as zerovec::ule::AsULE>::ULE as
zerovec::ule::ULE>::validate_bytes(bytes)?;
} else { return Err(zerovec::ule::UleError::parse::<Self>()); };
<zerovec::ZeroSlice<PotentialCodePoint> as
zerovec::ule::VarULE>::validate_bytes(last_field_bytes)?;
Ok(())
}
#[inline]
#[allow(clippy :: transmute_undefined_repr, reason =
"This is the only way to do this until ptr metadata stabilizes")]
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
#[repr(C)]
struct FatPtr {
ptr: usize,
metadata: usize,
}
const {
if !(::core::mem::size_of::<FatPtr>() ==
::core::mem::size_of::<&zerovec::ZeroSlice<PotentialCodePoint>>())
{
::core::panicking::panic("assertion failed: ::core::mem::size_of::<FatPtr>() ==\n ::core::mem::size_of::<&zerovec::ZeroSlice<PotentialCodePoint>>()")
};
if !(::core::mem::align_of::<FatPtr>() ==
::core::mem::align_of::<&zerovec::ZeroSlice<PotentialCodePoint>>())
{
::core::panicking::panic("assertion failed: ::core::mem::align_of::<FatPtr>() ==\n ::core::mem::align_of::<&zerovec::ZeroSlice<PotentialCodePoint>>()")
};
}
let unsized_bytes =
bytes.get_unchecked(__IMPL_VarULE_FOR_CodePointInversionListULE_ULE_SIZE..);
let unsized_ref =
<zerovec::ZeroSlice<PotentialCodePoint> as
zerovec::ule::VarULE>::from_bytes_unchecked(unsized_bytes);
let FatPtr { metadata, .. } =
::core::mem::transmute::<&zerovec::ZeroSlice<PotentialCodePoint>,
FatPtr>(unsized_ref);
let entire_struct_as_slice: *const [u8] =
::core::ptr::slice_from_raw_parts(bytes.as_ptr(), metadata);
&*(entire_struct_as_slice as *const Self)
}
}
impl core::cmp::PartialEq for CodePointInversionListULE {
fn eq(&self, other: &Self) -> bool {
<Self as zerovec::ule::VarULE>::as_bytes(&self) ==
<Self as zerovec::ule::VarULE>::as_bytes(&other)
}
}
impl core::cmp::Eq for CodePointInversionListULE {}
impl core::fmt::Debug for CodePointInversionListULE {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let this =
<CodePointInversionList as
zerovec::__zerovec_internal_reexport::ZeroFrom<CodePointInversionListULE>>::zero_from(self);
<CodePointInversionList as core::fmt::Debug>::fmt(&this, f)
}
}#[zerovec::make_varule(CodePointInversionListULE)]
28#[zerovec::skip_derive(Ord)]
29#[zerovec::derive(Debug)]
30#[derive(#[automatically_derived]impl<'data> ::core::fmt::Debug for
CodePointInversionList<'data> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"CodePointInversionList inv_list size", &self.inv_list,
&&self.size)
}
}Debug, #[automatically_derived]impl<'data> ::core::cmp::Eq for
CodePointInversionList<'data> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _:
::core::cmp::AssertParamIsEq<ZeroVec<'data,
PotentialCodePoint>>;
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}Eq, #[automatically_derived]impl<'data> ::core::marker::StructuralPartialEq for
CodePointInversionList<'data> {
}
#[automatically_derived]impl<'data> ::core::cmp::PartialEq for
CodePointInversionList<'data> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.size == other.size && self.inv_list == other.inv_list
}
}PartialEq, #[automatically_derived]impl<'data> ::core::clone::Clone for
CodePointInversionList<'data> {
#[inline]
fn clone(&self) -> Self {
CodePointInversionList {
inv_list: ::core::clone::Clone::clone(&self.inv_list),
size: ::core::clone::Clone::clone(&self.size),
}
}
}Clone, unsafe impl<'yoke> yoke::Yokeable<'yoke> for CodePointInversionList<'static>
where {
type Output = CodePointInversionList<'yoke>;
#[inline]
fn transform(&'yoke self) -> &'yoke Self::Output {
if false { let _: *const &'yoke Self::Output = &raw const self; }
self
}
#[inline]
fn transform_owned(self) -> Self::Output { self }
#[inline]
unsafe fn make(from: Self::Output) -> Self {
::core::mem::transmute::<Self::Output, Self>(from)
}
#[inline]
fn transform_mut<F>(&'yoke mut self, f: F) where F: 'static +
for<'_yoke> FnOnce(&'_yoke mut Self::Output) {
let y = unsafe { &mut *(self as *mut Self as *mut Self::Output) };
f(y)
}
}Yokeable, impl<'zf, 'zf_inner>
zerofrom::ZeroFrom<'zf, CodePointInversionList<'zf_inner>> for
CodePointInversionList<'zf> where {
fn zero_from(this: &'zf CodePointInversionList<'zf_inner>) -> Self {
match *this {
CodePointInversionList {
inv_list: ref __binding_0, size: ref __binding_1 } => {
CodePointInversionList {
inv_list: <ZeroVec<'zf, PotentialCodePoint> as
zerofrom::ZeroFrom<'zf,
ZeroVec<'zf_inner,
PotentialCodePoint>>>::zero_from(__binding_0),
size: *__binding_1,
}
}
}
}
}ZeroFrom, #[automatically_derived]impl<'data> ::core::hash::Hash for
CodePointInversionList<'data> {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.inv_list, state);
::core::hash::Hash::hash(&self.size, state)
}
}Hash)]
31#[cfg_attr(not(feature = "alloc"), zerovec::skip_derive(ZeroMapKV, ToOwned))]
32pub struct CodePointInversionList<'data> {
33 inv_list: ZeroVec<'data, PotentialCodePoint>,
39 size: u32,
40}
41
42#[cfg(feature = "serde")]
43impl<'de: 'a, 'a> serde::Deserialize<'de> for CodePointInversionList<'a> {
44 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
45 where
46 D: serde::Deserializer<'de>,
47 {
48 use serde::de::Error;
49
50 let parsed_inv_list = if deserializer.is_human_readable() {
51 let parsed_strings = Vec::<alloc::borrow::Cow<'de, str>>::deserialize(deserializer)?;
52 let mut inv_list = ZeroVec::new_owned(Vec::with_capacity(parsed_strings.len() * 2));
53 for range in parsed_strings {
54 fn internal(range: &str) -> Option<(u32, u32)> {
55 let (start, range) = UnicodeCodePoint::parse(range)?;
56 if range.is_empty() {
57 return Some((start.0, start.0));
58 }
59 let (hyphen, range) = UnicodeCodePoint::parse(range)?;
60 if hyphen.0 != '-' as u32 {
61 return None;
62 }
63 let (end, range) = UnicodeCodePoint::parse(range)?;
64 range.is_empty().then_some((start.0, end.0))
65 }
66 let (start, end) = internal(&range).ok_or_else(|| Error::custom(format!(
67 "Cannot deserialize invalid inversion list for CodePointInversionList: {range:?}"
68 )))?;
69 inv_list.with_mut(|v| {
70 v.push(PotentialCodePoint::from_u24(start).to_unaligned());
71 v.push(PotentialCodePoint::from_u24(end + 1).to_unaligned());
72 });
73 }
74 inv_list
75 } else {
76 ZeroVec::<PotentialCodePoint>::deserialize(deserializer)?
77 };
78 CodePointInversionList::try_from_inversion_list(parsed_inv_list).map_err(|e| {
79 Error::custom(format!(
80 "Cannot deserialize invalid inversion list for CodePointInversionList: {e:?}"
81 ))
82 })
83 }
84}
85
86#[cfg(feature = "databake")]
87impl databake::Bake for CodePointInversionList<'_> {
88 fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
89 env.insert("icu_collections");
90 let inv_list = self.inv_list.bake(env);
91 let size = self.size.bake(env);
92 databake::quote! { unsafe {
94 #[allow(unused_unsafe)]
95 icu_collections::codepointinvlist::CodePointInversionList::from_parts_unchecked(#inv_list, #size)
96 }}
97 }
98}
99
100#[cfg(feature = "databake")]
101impl databake::BakeSize for CodePointInversionList<'_> {
102 fn borrows_size(&self) -> usize {
103 self.inv_list.borrows_size()
104 }
105}
106
107#[cfg(feature = "serde")]
108#[derive(Debug, Copy, Clone)]
109struct UnicodeCodePoint(u32);
110
111#[cfg(feature = "serde")]
112impl UnicodeCodePoint {
113 fn from_u32(cp: u32) -> Result<Self, String> {
114 if cp < SENTINEL {
115 Ok(Self(cp))
116 } else {
117 Err(format!("Not a Unicode code point {cp}"))
118 }
119 }
120
121 fn parse(value: &str) -> Option<(Self, &str)> {
122 Some(if let Some(hex) = value.strip_prefix("U+") {
123 let (escape, remainder) = (hex.get(..4)?, hex.get(4..)?);
124 (Self(u32::from_str_radix(escape, 16).ok()?), remainder)
125 } else {
126 let c = value.chars().next()?;
127 (Self(c as u32), value.get(c.len_utf8()..)?)
128 })
129 }
130}
131
132#[cfg(feature = "serde")]
133impl core::fmt::Display for UnicodeCodePoint {
134 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
135 match self.0 {
136 s @ 0xD800..=0xDFFF => write!(f, "U+{s:X}"),
137 c => write!(
140 f,
141 "{}",
142 char::from_u32(c).unwrap_or(char::REPLACEMENT_CHARACTER)
143 ),
144 }
145 }
146}
147
148#[cfg(feature = "serde")]
149impl serde::Serialize for CodePointInversionList<'_> {
150 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
151 where
152 S: serde::Serializer,
153 {
154 if serializer.is_human_readable() {
155 use serde::ser::Error;
156 use serde::ser::SerializeSeq;
157 let mut seq = serializer.serialize_seq(Some(self.inv_list.len() / 2))?;
158 for range in self.iter_ranges() {
159 let start = UnicodeCodePoint::from_u32(*range.start()).map_err(S::Error::custom)?;
160 if range.start() == range.end() {
161 seq.serialize_element(&format!("{start}"))?;
162 } else {
163 let end = UnicodeCodePoint::from_u32(*range.end()).map_err(S::Error::custom)?;
164 seq.serialize_element(&format!("{start}-{end}",))?;
165 }
166 }
167 seq.end()
168 } else {
169 self.inv_list.serialize(serializer)
173 }
174 }
175}
176
177impl<'data> CodePointInversionList<'data> {
178 pub fn try_from_inversion_list(
216 inv_list: ZeroVec<'data, PotentialCodePoint>,
217 ) -> Result<Self, InvalidSetError> {
218 #[expect(clippy::indexing_slicing)] if is_valid_zv(&inv_list) {
220 let size = inv_list
221 .as_ule_slice()
222 .chunks(2)
223 .map(|end_points| {
224 u32::from(<PotentialCodePoint as AsULE>::from_unaligned(end_points[1]))
225 - u32::from(<PotentialCodePoint as AsULE>::from_unaligned(end_points[0]))
226 })
227 .sum::<u32>();
228 Ok(Self { inv_list, size })
229 } else {
230 Err(InvalidSetError(
231 #[cfg(feature = "alloc")]
232 inv_list.to_vec(),
233 ))
234 }
235 }
236
237 #[doc(hidden)] pub const unsafe fn from_parts_unchecked(
240 inv_list: ZeroVec<'data, PotentialCodePoint>,
241 size: u32,
242 ) -> Self {
243 Self { inv_list, size }
244 }
245
246 #[cfg(feature = "alloc")]
279 pub fn try_from_u32_inversion_list_slice(inv_list: &[u32]) -> Result<Self, InvalidSetError> {
280 let inv_list_zv: ZeroVec<PotentialCodePoint> = inv_list
281 .iter()
282 .copied()
283 .map(PotentialCodePoint::from_u24)
284 .collect();
285 CodePointInversionList::try_from_inversion_list(inv_list_zv)
286 }
287
288 #[cfg(feature = "alloc")]
292 pub fn into_owned(self) -> CodePointInversionList<'static> {
293 CodePointInversionList {
294 inv_list: self.inv_list.into_owned(),
295 size: self.size,
296 }
297 }
298
299 #[cfg(feature = "alloc")]
303 pub fn get_inversion_list_vec(&self) -> Vec<u32> {
304 self.as_inversion_list().iter().map(u32::from).collect()
305 }
306
307 pub const fn all() -> Self {
327 Self {
328 inv_list: ::zerovec::ZeroSlice::<PotentialCodePoint>::from_ule_slice(const {
&[PotentialCodePoint::to_unaligned(PotentialCodePoint::from_u24(0)),
PotentialCodePoint::to_unaligned(PotentialCodePoint::from_u24(SENTINEL))]
}).as_zerovec()zerovec!(PotentialCodePoint; PotentialCodePoint::to_unaligned; [PotentialCodePoint::from_u24(0), PotentialCodePoint::from_u24(SENTINEL)]),
329 size: SENTINEL,
330 }
331 }
332
333 pub fn bmp() -> Self {
355 const BMP_SENTINEL: u32 = 0x10000;
357
358 Self {
359 inv_list: ::zerovec::ZeroSlice::<PotentialCodePoint>::from_ule_slice(const {
&[PotentialCodePoint::to_unaligned(PotentialCodePoint::from_u24(0)),
PotentialCodePoint::to_unaligned(PotentialCodePoint::from_u24(BMP_SENTINEL))]
}).as_zerovec()zerovec!(PotentialCodePoint; PotentialCodePoint::to_unaligned; [PotentialCodePoint::from_u24(0), PotentialCodePoint::from_u24(BMP_SENTINEL)]),
360 size: BMP_SENTINEL,
361 }
362 }
363
364 #[cfg(feature = "alloc")]
368 pub(crate) fn as_inversion_list(&self) -> &ZeroVec<'_, PotentialCodePoint> {
369 &self.inv_list
370 }
371
372 pub fn iter_chars(&self) -> impl Iterator<Item = char> + '_ {
391 #[expect(clippy::indexing_slicing)] self.inv_list
393 .as_ule_slice()
394 .chunks(2)
395 .flat_map(|pair| {
396 u32::from(PotentialCodePoint::from_unaligned(pair[0]))
397 ..u32::from(PotentialCodePoint::from_unaligned(pair[1]))
398 })
399 .filter_map(char::from_u32)
400 }
401
402 pub fn iter_ranges(&self) -> impl ExactSizeIterator<Item = RangeInclusive<u32>> + '_ {
424 #[expect(clippy::indexing_slicing)] self.inv_list.as_ule_slice().chunks(2).map(|pair| {
426 let range_start = u32::from(PotentialCodePoint::from_unaligned(pair[0]));
427 let range_limit = u32::from(PotentialCodePoint::from_unaligned(pair[1]));
428 range_start..=(range_limit - 1)
429 })
430 }
431
432 pub fn iter_ranges_complemented(&self) -> impl Iterator<Item = RangeInclusive<u32>> + '_ {
455 let inv_ule = self.inv_list.as_ule_slice();
456 let middle = inv_ule.get(1..inv_ule.len() - 1).unwrap_or(&[]);
457 let beginning = if let Some(first) = self.inv_list.first() {
458 let first = u32::from(first);
459 if first == 0 {
460 None
461 } else {
462 Some(0..=first - 1)
463 }
464 } else {
465 None
466 };
467 let end = if let Some(last) = self.inv_list.last() {
468 let last = u32::from(last);
469 if last == SENTINEL {
470 None
471 } else {
472 Some(last..=char::MAX as u32)
473 }
474 } else {
475 None
476 };
477 #[expect(clippy::indexing_slicing)] let chunks = middle.chunks(2).map(|pair| {
479 let range_start = u32::from(PotentialCodePoint::from_unaligned(pair[0]));
480 let range_limit = u32::from(PotentialCodePoint::from_unaligned(pair[1]));
481 range_start..=(range_limit - 1)
482 });
483 beginning.into_iter().chain(chunks).chain(end)
484 }
485
486 pub fn get_range_count(&self) -> usize {
488 self.inv_list.len() / 2
489 }
490
491 pub fn get_nth_range(&self, idx: usize) -> Option<RangeInclusive<u32>> {
494 let start_idx = idx * 2;
495 let end_idx = start_idx + 1;
496 let start = u32::from(self.inv_list.get(start_idx)?);
497 let end = u32::from(self.inv_list.get(end_idx)?);
498 Some(start..=(end - 1))
499 }
500
501 pub fn size(&self) -> usize {
503 if self.is_empty() {
504 return 0;
505 }
506 self.size as usize
507 }
508
509 pub fn is_empty(&self) -> bool {
511 self.inv_list.is_empty()
512 }
513
514 fn contains_query(&self, query: u32) -> Option<usize> {
519 let query = PotentialCodePoint::try_from(query).ok()?;
520 match self.inv_list.binary_search(&query) {
521 Ok(pos) => {
522 if pos % 2 == 0 {
523 Some(pos)
524 } else {
525 None
526 }
527 }
528 Err(pos) => {
529 if pos % 2 != 0 && pos < self.inv_list.len() {
530 Some(pos - 1)
531 } else {
532 None
533 }
534 }
535 }
536 }
537
538 pub fn contains(&self, query: char) -> bool {
556 self.contains_query(query as u32).is_some()
557 }
558
559 pub fn contains32(&self, query: u32) -> bool {
582 self.contains_query(query).is_some()
583 }
584
585 #[inline]
602 pub fn position(&self, query: char) -> Option<usize> {
603 self.position32(query as u32)
604 }
605
606 #[inline]
610 pub fn position32(&self, query: u32) -> Option<usize> {
611 let mut offset = 0;
612 for r in self.iter_ranges() {
613 if query < *r.start() {
614 return None;
615 }
616 if query <= *r.end() {
617 return Some((offset + (query - r.start())) as usize);
618 }
619 offset += r.end() - r.start() + 1;
620 }
621 None
622 }
623
624 pub fn contains_range(&self, range: impl RangeBounds<char>) -> bool {
666 let (from, till) = deconstruct_range(range);
667 if from >= till {
668 return false;
669 }
670 match self.contains_query(from) {
671 Some(pos) => {
672 if let Some(x) = self.inv_list.get(pos + 1) {
673 (till) <= x.into()
674 } else {
675 if true {
if !false {
{
::core::panicking::panic_fmt(format_args!("Inversion list query should not return out of bounds index"));
}
};
};debug_assert!(
676 false,
677 "Inversion list query should not return out of bounds index"
678 );
679 false
680 }
681 }
682 None => false,
683 }
684 }
685
686 pub fn contains_set(&self, set: &Self) -> bool {
714 if set.size() > self.size() {
715 return false;
716 }
717
718 let mut set_ranges = set.iter_ranges();
719 let mut check_elem = set_ranges.next();
720
721 let ranges = self.iter_ranges();
722 for range in ranges {
723 loop {
724 let Some(ref check_range) = check_elem else {
725 return true;
726 };
727 if check_range.start() >= range.start() && check_range.end() <= &(range.end() + 1) {
728 check_elem = set_ranges.next();
729 } else {
730 break;
731 }
732 }
733 }
734 check_elem.is_none()
735 }
736
737 pub fn span(&self, span_str: &str, contained: bool) -> usize {
755 span_str
756 .chars()
757 .take_while(|&x| self.contains(x) == contained)
758 .count()
759 }
760
761 pub fn span_back(&self, span_str: &str, contained: bool) -> usize {
778 span_str.len()
779 - span_str
780 .chars()
781 .rev()
782 .take_while(|&x| self.contains(x) == contained)
783 .count()
784 }
785}
786
787#[cfg(test)]
788mod tests {
789 use super::{CodePointInversionList, InvalidSetError};
790 use std::{char, vec::Vec};
791 use zerovec::ZeroVec;
792
793 #[test]
794 fn test_codepointinversionlist_try_from_vec() {
795 let ex = vec![0x2, 0x3, 0x4, 0x5];
796 let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
797 assert_eq!(ex, check.get_inversion_list_vec());
798 assert_eq!(2, check.size());
799 }
800
801 #[test]
802 fn test_codepointinversionlist_try_from_vec_error() {
803 let check = vec![0x1, 0x1, 0x2, 0x3, 0x4];
804 let set = CodePointInversionList::try_from_u32_inversion_list_slice(&check);
805 assert!(matches!(set, Err(InvalidSetError(_))));
806 if let Err(InvalidSetError(actual)) = set {
807 assert_eq!(
808 &check,
809 &actual.into_iter().map(u32::from).collect::<Vec<_>>()
810 );
811 }
812 }
813
814 #[test]
816 fn test_codepointinversionlist_contains_query() {
817 let ex = vec![0x41, 0x46, 0x4B, 0x55];
818 let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
819 assert!(check.contains_query(0x40).is_none());
820 assert_eq!(check.contains_query(0x41).unwrap(), 0);
821 assert_eq!(check.contains_query(0x44).unwrap(), 0);
822 assert!(check.contains_query(0x46).is_none());
823 assert_eq!(check.contains_query(0x4C).unwrap(), 2);
824 assert!(check.contains_query(0x56).is_none());
825 }
826
827 #[test]
828 fn test_codepointinversionlist_contains() {
829 let ex = vec![0x2, 0x5, 0xA, 0xF];
830 let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
831 assert!(check.contains(0x2 as char));
832 assert!(check.contains(0x4 as char));
833 assert!(check.contains(0xA as char));
834 assert!(check.contains(0xE as char));
835 }
836
837 #[test]
838 fn test_codepointinversionlist_contains_false() {
839 let ex = vec![0x2, 0x5, 0xA, 0xF];
840 let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
841 assert!(!check.contains(0x1 as char));
842 assert!(!check.contains(0x5 as char));
843 assert!(!check.contains(0x9 as char));
844 assert!(!check.contains(0xF as char));
845 assert!(!check.contains(0x10 as char));
846 }
847
848 #[test]
849 fn test_codepointinversionlist_contains_range() {
850 let ex = vec![0x41, 0x46, 0x4B, 0x55];
851 let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
852 assert!(check.contains_range('A'..='E')); assert!(check.contains_range('C'..'D')); assert!(check.contains_range('L'..'P')); assert!(!check.contains_range('L'..='U')); }
857
858 #[test]
859 fn test_codepointinversionlist_contains_range_false() {
860 let ex = vec![0x41, 0x46, 0x4B, 0x55];
861 let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
862 assert!(!check.contains_range('!'..'A')); assert!(!check.contains_range('F'..'K')); assert!(!check.contains_range('U'..)); }
866
867 #[test]
868 fn test_codepointinversionlist_contains_range_invalid() {
869 let check = CodePointInversionList::all();
870 assert!(!check.contains_range('A'..'!')); assert!(!check.contains_range('A'..'A')); }
873
874 #[test]
875 fn test_codepointinversionlist_contains_set_u() {
876 let ex = vec![0xA, 0x14, 0x28, 0x32, 0x46, 0x50, 0x64, 0x6E];
877 let u = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
878 let inside = vec![0xF, 0x14, 0x2C, 0x31, 0x46, 0x50, 0x64, 0x6D];
879 let s = CodePointInversionList::try_from_u32_inversion_list_slice(&inside).unwrap();
880 assert!(u.contains_set(&s));
881 }
882
883 #[test]
884 fn test_codepointinversionlist_contains_set_u_false() {
885 let ex = vec![0xA, 0x14, 0x28, 0x32, 0x46, 0x50, 0x64, 0x78];
886 let u = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
887 let outside = vec![0x0, 0xA, 0x16, 0x2C, 0x32, 0x46, 0x4F, 0x51, 0x6D, 0x6F];
888 let s = CodePointInversionList::try_from_u32_inversion_list_slice(&outside).unwrap();
889 assert!(!u.contains_set(&s));
890 }
891
892 #[test]
893 fn test_codepointinversionlist_size() {
894 let ex = vec![0x2, 0x5, 0xA, 0xF];
895 let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
896 assert_eq!(8, check.size());
897 let check = CodePointInversionList::all();
898 let expected = (char::MAX as u32) + 1;
899 assert_eq!(expected as usize, check.size());
900 let inv_list_vec = vec![];
901 let check = CodePointInversionList {
902 inv_list: ZeroVec::from_slice_or_alloc(&inv_list_vec),
903 size: 0,
904 };
905 assert_eq!(check.size(), 0);
906 }
907
908 #[test]
909 fn test_codepointinversionlist_is_empty() {
910 let inv_list_vec = vec![];
911 let check = CodePointInversionList {
912 inv_list: ZeroVec::from_slice_or_alloc(&inv_list_vec),
913 size: 0,
914 };
915 assert!(check.is_empty());
916 }
917
918 #[test]
919 fn test_codepointinversionlist_is_not_empty() {
920 let check = CodePointInversionList::all();
921 assert!(!check.is_empty());
922 }
923
924 #[test]
925 fn test_codepointinversionlist_iter_chars() {
926 let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
927 let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
928 let mut iter = check.iter_chars();
929 assert_eq!(Some('A'), iter.next());
930 assert_eq!(Some('B'), iter.next());
931 assert_eq!(Some('C'), iter.next());
932 assert_eq!(Some('E'), iter.next());
933 assert_eq!(None, iter.next());
934 }
935
936 #[test]
937 fn test_codepointinversionlist_iter_ranges() {
938 let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
939 let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
940 let mut ranges = set.iter_ranges();
941 assert_eq!(Some(0x41..=0x43), ranges.next());
942 assert_eq!(Some(0x45..=0x45), ranges.next());
943 assert_eq!(Some(0xD800..=0xD800), ranges.next());
944 assert_eq!(None, ranges.next());
945 }
946
947 #[test]
948 fn test_codepointinversionlist_iter_ranges_exactsizeiter_trait() {
949 let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
950 let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
951 let ranges = set.iter_ranges();
952 assert_eq!(3, ranges.len());
953 }
954
955 #[test]
956 fn test_codepointinversionlist_range_count() {
957 let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
958 let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
959 assert_eq!(3, set.get_range_count());
960 }
961
962 #[test]
963 fn test_codepointinversionlist_get_nth_range() {
964 let ex = vec![0x41, 0x44, 0x45, 0x46, 0xD800, 0xD801];
965 let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
966 assert_eq!(Some(0x41..=0x43), set.get_nth_range(0));
967 assert_eq!(Some(0x45..=0x45), set.get_nth_range(1));
968 assert_eq!(Some(0xD800..=0xD800), set.get_nth_range(2));
969 assert_eq!(None, set.get_nth_range(3));
970 }
971
972 #[test]
975 fn test_codepointinversionlist_iter_ranges_with_max_code_point() {
976 let ex = vec![0x80, (char::MAX as u32) + 1];
977 let set = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
978 let mut ranges = set.iter_ranges();
979 assert_eq!(Some(0x80..=(char::MAX as u32)), ranges.next());
980 assert_eq!(None, ranges.next());
981 }
982
983 #[test]
984 fn test_codepointinversionlist_span_contains() {
985 let ex = vec![0x41, 0x44, 0x46, 0x4B]; let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
987 assert_eq!(check.span("ABCDE", true), 3);
988 assert_eq!(check.span("E", true), 0);
989 }
990
991 #[test]
992 fn test_codepointinversionlist_span_does_not_contain() {
993 let ex = vec![0x41, 0x44, 0x46, 0x4B]; let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
995 assert_eq!(check.span("DEF", false), 2);
996 assert_eq!(check.span("KLMA", false), 3);
997 }
998
999 #[test]
1000 fn test_codepointinversionlist_span_back_contains() {
1001 let ex = vec![0x41, 0x44, 0x46, 0x4B]; let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1003 assert_eq!(check.span_back("XYZABFH", true), 3);
1004 assert_eq!(check.span_back("ABCXYZ", true), 6);
1005 }
1006
1007 #[test]
1008 fn test_codepointinversionlist_span_back_does_not_contain() {
1009 let ex = vec![0x41, 0x44, 0x46, 0x4B]; let check = CodePointInversionList::try_from_u32_inversion_list_slice(&ex).unwrap();
1011 assert_eq!(check.span_back("ABCXYZ", false), 3);
1012 assert_eq!(check.span_back("XYZABC", false), 6);
1013 }
1014
1015 #[test]
1016 fn test_uniset_to_inv_list() {
1017 let inv_list = [
1018 0x9, 0xE, 0x20, 0x21, 0x85, 0x86, 0xA0, 0xA1, 0x1626, 0x1627, 0x2000, 0x2003, 0x2028,
1019 0x202A, 0x202F, 0x2030, 0x205F, 0x2060, 0x3000, 0x3001,
1020 ];
1021 let s: CodePointInversionList =
1022 CodePointInversionList::try_from_u32_inversion_list_slice(&inv_list).unwrap();
1023 let round_trip_inv_list = s.get_inversion_list_vec();
1024 assert_eq!(
1025 round_trip_inv_list.into_iter().collect::<ZeroVec<u32>>(),
1026 inv_list
1027 );
1028 }
1029
1030 #[test]
1031 fn test_serde_serialize() {
1032 let inv_list = [0x41, 0x46, 0x4B, 0x55];
1033 let uniset = CodePointInversionList::try_from_u32_inversion_list_slice(&inv_list).unwrap();
1034 let json_str = serde_json::to_string(&uniset).unwrap();
1035 assert_eq!(json_str, r#"["A-E","K-T"]"#);
1036 }
1037
1038 #[test]
1039 fn test_serde_serialize_surrogates() {
1040 let inv_list = [0xDFAB, 0xDFFF];
1041 let uniset = CodePointInversionList::try_from_u32_inversion_list_slice(&inv_list).unwrap();
1042 let json_str = serde_json::to_string(&uniset).unwrap();
1043 assert_eq!(json_str, r#"["U+DFAB-U+DFFE"]"#);
1044 }
1045
1046 #[test]
1047 fn test_serde_deserialize() {
1048 let inv_list_str = r#"["A-E","K-T"]"#;
1049 let exp_inv_list = [0x41, 0x46, 0x4B, 0x55];
1050 let exp_uniset =
1051 CodePointInversionList::try_from_u32_inversion_list_slice(&exp_inv_list).unwrap();
1052 let act_uniset: CodePointInversionList = serde_json::from_str(inv_list_str).unwrap();
1053 assert_eq!(act_uniset, exp_uniset);
1054 }
1055
1056 #[test]
1057 fn test_serde_deserialize_surrogates() {
1058 let inv_list_str = r#"["U+DFAB-U+DFFE"]"#;
1059 let exp_inv_list = [0xDFAB, 0xDFFF];
1060 let exp_uniset =
1061 CodePointInversionList::try_from_u32_inversion_list_slice(&exp_inv_list).unwrap();
1062 let act_uniset: CodePointInversionList = serde_json::from_str(inv_list_str).unwrap();
1063 assert_eq!(act_uniset, exp_uniset);
1064 }
1065
1066 #[test]
1067 fn test_serde_deserialize_invalid() {
1068 assert!(serde_json::from_str::<CodePointInversionList>("[65,70,98775,85]").is_err());
1069 assert!(serde_json::from_str::<CodePointInversionList>("[65,70,U+FFFFFFFFFF,85]").is_err());
1070 }
1071
1072 #[test]
1073 fn test_serde_with_postcard_roundtrip() -> Result<(), postcard::Error> {
1074 let set = CodePointInversionList::bmp();
1075 let set_serialized: Vec<u8> = postcard::to_allocvec(&set).unwrap();
1076 let set_deserialized: CodePointInversionList =
1077 postcard::from_bytes::<CodePointInversionList>(&set_serialized)?;
1078
1079 assert_eq!(&set, &set_deserialized);
1080 assert!(!set_deserialized.inv_list.is_owned());
1081
1082 Ok(())
1083 }
1084
1085 #[test]
1086 fn databake() {
1087 databake::test_bake!(
1088 CodePointInversionList<'static>,
1089 const,
1090 unsafe {
1091 #[allow(unused_unsafe)]
1092 crate::codepointinvlist::CodePointInversionList::from_parts_unchecked(
1093 unsafe {
1094 zerovec::ZeroVec::from_bytes_unchecked(
1095 b"0\0\0\0:\0\0\0A\0\0\0G\0\0\0a\0\0\0g\0\0\0",
1096 )
1097 },
1098 22u32,
1099 )
1100 },
1101 icu_collections,
1102 [zerovec],
1103 );
1104 }
1105}