icu_locale_core/shortvec/
mod.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//! This module includes variable-length data types that are const-constructible for single
6//! values and overflow to the heap.
7//!
8//! # Why?
9//!
10//! This module is far from the first stack-or-heap vector in the Rust ecosystem. It was created
11//! with the following value proposition:
12//!
13//! 1. Enable safe const construction of stack collections.
14//! 2. Avoid stack size penalties common with stack-or-heap collections.
15//!
16//! As of this writing, `heapless` and `tinyvec` don't support const construction except
17//! for empty vectors, and `smallvec` supports it on unstable.
18//!
19//! Additionally, [`ShortBoxSlice`] has a smaller stack size than any of these:
20//!
21//! ```ignore
22//! use core::mem::size_of;
23//!
24//! // NonZeroU64 has a niche that this module utilizes
25//! use core::num::NonZeroU64;
26//!
27//! // ShortBoxSlice is the same size as `Box<[]>` for small or nichey values
28//! assert_eq!(16, size_of::<shortvec::ShortBoxSlice::<NonZeroU64>>());
29//!
30//! // Note: SmallVec supports pushing and therefore has a capacity field
31//! assert_eq!(24, size_of::<smallvec::SmallVec::<[NonZeroU64; 1]>>());
32//!
33//! // Note: heapless doesn't support spilling to the heap
34//! assert_eq!(16, size_of::<heapless::Vec::<NonZeroU64, 1>>());
35//!
36//! // Note: TinyVec only supports types that implement `Default`
37//! assert_eq!(24, size_of::<tinyvec::TinyVec::<[u64; 1]>>());
38//! ```
39//!
40//! The module is `no_std` with `alloc`.
41
42mod litemap;
43
44#[cfg(feature = "alloc")]
45use alloc::boxed::Box;
46#[cfg(feature = "alloc")]
47use alloc::vec;
48#[cfg(feature = "alloc")]
49use alloc::vec::Vec;
50use core::ops::Deref;
51use core::ops::DerefMut;
52
53/// A boxed slice that supports no-allocation, constant values if length 0 or 1.
54/// Using ZeroOne(Option<T>) saves 8 bytes in ShortBoxSlice via niche optimization.
55#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for ShortBoxSliceInner<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            ShortBoxSliceInner::ZeroOne(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "ZeroOne", &__self_0),
            ShortBoxSliceInner::Two(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Two",
                    &__self_0),
        }
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for ShortBoxSliceInner<T> {
    #[inline]
    fn clone(&self) -> ShortBoxSliceInner<T> {
        match self {
            ShortBoxSliceInner::ZeroOne(__self_0) =>
                ShortBoxSliceInner::ZeroOne(::core::clone::Clone::clone(__self_0)),
            ShortBoxSliceInner::Two(__self_0) =>
                ShortBoxSliceInner::Two(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone, #[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for
    ShortBoxSliceInner<T> {
    #[inline]
    fn eq(&self, other: &ShortBoxSliceInner<T>) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (ShortBoxSliceInner::ZeroOne(__self_0),
                    ShortBoxSliceInner::ZeroOne(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (ShortBoxSliceInner::Two(__self_0),
                    ShortBoxSliceInner::Two(__arg1_0)) => __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, #[automatically_derived]
impl<T: ::core::cmp::Eq> ::core::cmp::Eq for ShortBoxSliceInner<T> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<Option<T>>;
        let _: ::core::cmp::AssertParamIsEq<[T; 2]>;
    }
}Eq, #[automatically_derived]
impl<T: ::core::hash::Hash> ::core::hash::Hash for ShortBoxSliceInner<T> {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state);
        match self {
            ShortBoxSliceInner::ZeroOne(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            ShortBoxSliceInner::Two(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
        }
    }
}Hash, #[automatically_derived]
impl<T: ::core::cmp::PartialOrd> ::core::cmp::PartialOrd for
    ShortBoxSliceInner<T> {
    #[inline]
    fn partial_cmp(&self, other: &ShortBoxSliceInner<T>)
        -> ::core::option::Option<::core::cmp::Ordering> {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        match (self, other) {
            (ShortBoxSliceInner::ZeroOne(__self_0),
                ShortBoxSliceInner::ZeroOne(__arg1_0)) =>
                ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
            (ShortBoxSliceInner::Two(__self_0),
                ShortBoxSliceInner::Two(__arg1_0)) =>
                ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
            _ =>
                ::core::cmp::PartialOrd::partial_cmp(&__self_discr,
                    &__arg1_discr),
        }
    }
}PartialOrd, #[automatically_derived]
impl<T: ::core::cmp::Ord> ::core::cmp::Ord for ShortBoxSliceInner<T> {
    #[inline]
    fn cmp(&self, other: &ShortBoxSliceInner<T>) -> ::core::cmp::Ordering {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        match ::core::cmp::Ord::cmp(&__self_discr, &__arg1_discr) {
            ::core::cmp::Ordering::Equal =>
                match (self, other) {
                    (ShortBoxSliceInner::ZeroOne(__self_0),
                        ShortBoxSliceInner::ZeroOne(__arg1_0)) =>
                        ::core::cmp::Ord::cmp(__self_0, __arg1_0),
                    (ShortBoxSliceInner::Two(__self_0),
                        ShortBoxSliceInner::Two(__arg1_0)) =>
                        ::core::cmp::Ord::cmp(__self_0, __arg1_0),
                    _ => unsafe { ::core::intrinsics::unreachable() }
                },
            cmp => cmp,
        }
    }
}Ord)]
56pub(crate) enum ShortBoxSliceInner<T> {
57    ZeroOne(Option<T>),
58    #[cfg(feature = "alloc")]
59    Multi(Box<[T]>),
60    #[cfg(not(feature = "alloc"))]
61    Two([T; 2]),
62}
63
64impl<T> Default for ShortBoxSliceInner<T> {
65    fn default() -> Self {
66        use ShortBoxSliceInner::*;
67        ZeroOne(None)
68    }
69}
70
71/// A boxed slice that supports no-allocation, constant values if length 0 or 1.
72///
73/// Supports mutation but always reallocs when mutated.
74#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for ShortBoxSlice<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "ShortBoxSlice",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for ShortBoxSlice<T> {
    #[inline]
    fn clone(&self) -> ShortBoxSlice<T> {
        ShortBoxSlice(::core::clone::Clone::clone(&self.0))
    }
}Clone, #[automatically_derived]
impl<T: ::core::cmp::PartialEq> ::core::cmp::PartialEq for ShortBoxSlice<T> {
    #[inline]
    fn eq(&self, other: &ShortBoxSlice<T>) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl<T: ::core::cmp::Eq> ::core::cmp::Eq for ShortBoxSlice<T> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<ShortBoxSliceInner<T>>;
    }
}Eq, #[automatically_derived]
impl<T: ::core::hash::Hash> ::core::hash::Hash for ShortBoxSlice<T> {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        ::core::hash::Hash::hash(&self.0, state)
    }
}Hash, #[automatically_derived]
impl<T: ::core::cmp::PartialOrd> ::core::cmp::PartialOrd for ShortBoxSlice<T>
    {
    #[inline]
    fn partial_cmp(&self, other: &ShortBoxSlice<T>)
        -> ::core::option::Option<::core::cmp::Ordering> {
        ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0)
    }
}PartialOrd, #[automatically_derived]
impl<T: ::core::cmp::Ord> ::core::cmp::Ord for ShortBoxSlice<T> {
    #[inline]
    fn cmp(&self, other: &ShortBoxSlice<T>) -> ::core::cmp::Ordering {
        ::core::cmp::Ord::cmp(&self.0, &other.0)
    }
}Ord)]
75pub(crate) struct ShortBoxSlice<T>(ShortBoxSliceInner<T>);
76
77impl<T> Default for ShortBoxSlice<T> {
78    fn default() -> Self {
79        Self(Default::default())
80    }
81}
82
83impl<T> ShortBoxSlice<T> {
84    /// Creates a new, empty [`ShortBoxSlice`].
85    #[inline]
86    pub const fn new() -> Self {
87        use ShortBoxSliceInner::*;
88        Self(ZeroOne(None))
89    }
90
91    /// Creates a new [`ShortBoxSlice`] containing a single element.
92    #[inline]
93    pub const fn new_single(item: T) -> Self {
94        use ShortBoxSliceInner::*;
95        Self(ZeroOne(Some(item)))
96    }
97
98    pub fn new_double(first: T, second: T) -> Self {
99        use ShortBoxSliceInner::*;
100        #[cfg(feature = "alloc")]
101        return Self(Multi(vec![first, second].into_boxed_slice()));
102        #[cfg(not(feature = "alloc"))]
103        return Self(Two([first, second]));
104    }
105
106    /// Pushes an element onto this [`ShortBoxSlice`].
107    ///
108    /// Reallocs if more than 1 item is already in the collection.
109    #[cfg(feature = "alloc")]
110    pub fn push(&mut self, item: T) {
111        use ShortBoxSliceInner::*;
112        self.0 = match core::mem::replace(&mut self.0, ZeroOne(None)) {
113            ZeroOne(None) => ZeroOne(Some(item)),
114            ZeroOne(Some(prev_item)) => Multi(vec![prev_item, item].into_boxed_slice()),
115            Multi(items) => {
116                let mut items = items.into_vec();
117                items.push(item);
118                Multi(items.into_boxed_slice())
119            }
120        };
121    }
122
123    /// Gets a single element from the [`ShortBoxSlice`].
124    ///
125    /// Returns `None` if empty or more than one element.
126    #[inline]
127    pub const fn single(&self) -> Option<&T> {
128        use ShortBoxSliceInner::*;
129        match self.0 {
130            ZeroOne(Some(ref v)) => Some(v),
131            _ => None,
132        }
133    }
134
135    /// Destruct into a single element of the [`ShortBoxSlice`].
136    ///
137    /// Returns `None` if empty or more than one element.
138    pub fn into_single(self) -> Option<T> {
139        use ShortBoxSliceInner::*;
140        match self.0 {
141            ZeroOne(Some(v)) => Some(v),
142            _ => None,
143        }
144    }
145
146    /// Returns the number of elements in the collection.
147    #[inline]
148    pub fn len(&self) -> usize {
149        use ShortBoxSliceInner::*;
150        match self.0 {
151            ZeroOne(None) => 0,
152            ZeroOne(_) => 1,
153            #[cfg(feature = "alloc")]
154            Multi(ref v) => v.len(),
155            #[cfg(not(feature = "alloc"))]
156            Two(_) => 2,
157        }
158    }
159
160    /// Returns whether the collection is empty.
161    #[inline]
162    pub const fn is_empty(&self) -> bool {
163        use ShortBoxSliceInner::*;
164        #[allow(non_exhaustive_omitted_patterns)] match self.0 {
    ZeroOne(None) => true,
    _ => false,
}matches!(self.0, ZeroOne(None))
165    }
166
167    /// Inserts an element at the specified index into the collection.
168    ///
169    /// Reallocs if more than 1 item is already in the collection.
170    #[cfg(feature = "alloc")]
171    pub fn insert(&mut self, index: usize, elt: T) {
172        use ShortBoxSliceInner::*;
173        assert!(
174            index <= self.len(),
175            "insertion index (is {}) should be <= len (is {})",
176            index,
177            self.len()
178        );
179
180        self.0 = match core::mem::replace(&mut self.0, ZeroOne(None)) {
181            ZeroOne(None) => ZeroOne(Some(elt)),
182            ZeroOne(Some(item)) => {
183                let items = if index == 0 {
184                    vec![elt, item].into_boxed_slice()
185                } else {
186                    vec![item, elt].into_boxed_slice()
187                };
188                Multi(items)
189            }
190            Multi(items) => {
191                let mut items = items.into_vec();
192                items.insert(index, elt);
193                Multi(items.into_boxed_slice())
194            }
195        }
196    }
197
198    /// Removes the element at the specified index from the collection.
199    ///
200    /// Reallocs if more than 2 items are in the collection.
201    pub fn remove(&mut self, index: usize) -> T {
202        use ShortBoxSliceInner::*;
203        if !(index < self.len()) {
    {
        ::core::panicking::panic_fmt(format_args!("removal index (is {0}) should be < len (is {1})",
                index, self.len()));
    }
};assert!(
204            index < self.len(),
205            "removal index (is {}) should be < len (is {})",
206            index,
207            self.len()
208        );
209
210        let (replaced, removed_item) = match core::mem::replace(&mut self.0, ZeroOne(None)) {
211            ZeroOne(None) => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
212            ZeroOne(Some(v)) => (ZeroOne(None), v),
213            #[cfg(feature = "alloc")]
214            Multi(v) => {
215                let mut v = v.into_vec();
216                let removed_item = v.remove(index);
217                match v.len() {
218                    #[expect(clippy::unwrap_used)]
219                    // we know that the vec has exactly one element left
220                    1 => (ZeroOne(Some(v.pop().unwrap())), removed_item),
221                    // v has at least 2 elements, create a Multi variant
222                    _ => (Multi(v.into_boxed_slice()), removed_item),
223                }
224            }
225            #[cfg(not(feature = "alloc"))]
226            Two([f, s]) => (ZeroOne(Some(f)), s),
227        };
228        self.0 = replaced;
229        removed_item
230    }
231
232    /// Removes all elements from the collection.
233    #[inline]
234    pub fn clear(&mut self) {
235        use ShortBoxSliceInner::*;
236        let _ = core::mem::replace(&mut self.0, ZeroOne(None));
237    }
238
239    /// Retains only the elements specified by the predicate.
240    #[allow(dead_code)]
241    pub fn retain<F>(&mut self, mut f: F)
242    where
243        F: FnMut(&T) -> bool,
244    {
245        use ShortBoxSliceInner::*;
246        match core::mem::take(&mut self.0) {
247            ZeroOne(Some(one)) if f(&one) => self.0 = ZeroOne(Some(one)),
248            ZeroOne(_) => self.0 = ZeroOne(None),
249            #[cfg(feature = "alloc")]
250            Multi(slice) => {
251                let mut vec = slice.into_vec();
252                vec.retain(f);
253                *self = ShortBoxSlice::from(vec)
254            }
255            #[cfg(not(feature = "alloc"))]
256            Two([first, second]) => {
257                *self = match (Some(first).filter(&mut f), Some(second).filter(&mut f)) {
258                    (None, None) => ShortBoxSlice::new(),
259                    (None, Some(x)) | (Some(x), None) => ShortBoxSlice::new_single(x),
260                    (Some(f), Some(s)) => ShortBoxSlice::new_double(f, s),
261                }
262            }
263        };
264    }
265}
266
267impl<T> Deref for ShortBoxSlice<T> {
268    type Target = [T];
269
270    fn deref(&self) -> &Self::Target {
271        use ShortBoxSliceInner::*;
272        match self.0 {
273            ZeroOne(None) => &[],
274            ZeroOne(Some(ref v)) => core::slice::from_ref(v),
275            #[cfg(feature = "alloc")]
276            Multi(ref v) => v,
277            #[cfg(not(feature = "alloc"))]
278            Two(ref v) => v,
279        }
280    }
281}
282
283impl<T> DerefMut for ShortBoxSlice<T> {
284    fn deref_mut(&mut self) -> &mut Self::Target {
285        use ShortBoxSliceInner::*;
286        match self.0 {
287            ZeroOne(None) => &mut [],
288            ZeroOne(Some(ref mut v)) => core::slice::from_mut(v),
289            #[cfg(feature = "alloc")]
290            Multi(ref mut v) => v,
291            #[cfg(not(feature = "alloc"))]
292            Two(ref mut v) => v,
293        }
294    }
295}
296
297#[cfg(feature = "alloc")]
298impl<T> From<Vec<T>> for ShortBoxSlice<T> {
299    fn from(v: Vec<T>) -> Self {
300        use ShortBoxSliceInner::*;
301        match v.len() {
302            0 => Self(ZeroOne(None)),
303            #[expect(clippy::unwrap_used)] // we know that the vec is not empty
304            1 => Self(ZeroOne(Some(v.into_iter().next().unwrap()))),
305            _ => Self(Multi(v.into_boxed_slice())),
306        }
307    }
308}
309
310#[cfg(feature = "alloc")]
311impl<T> FromIterator<T> for ShortBoxSlice<T> {
312    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
313        use ShortBoxSliceInner::*;
314        let mut iter = iter.into_iter();
315        match (iter.next(), iter.next()) {
316            (Some(first), Some(second)) => {
317                // Size hint behaviour same as `Vec::extend` + 2
318                let mut vec = Vec::with_capacity(iter.size_hint().0.saturating_add(3));
319                vec.push(first);
320                vec.push(second);
321                vec.extend(iter);
322                Self(Multi(vec.into_boxed_slice()))
323            }
324            (first, _) => Self(ZeroOne(first)),
325        }
326    }
327}
328
329/// An iterator that yields elements from a [`ShortBoxSlice`].
330#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for ShortBoxSliceIntoIter<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "ShortBoxSliceIntoIter", &&self.0)
    }
}Debug)]
331pub struct ShortBoxSliceIntoIter<T>(ShortBoxSliceIntoIterInner<T>);
332
333#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for
    ShortBoxSliceIntoIterInner<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            ShortBoxSliceIntoIterInner::ZeroOne(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "ZeroOne", &__self_0),
            ShortBoxSliceIntoIterInner::Two(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Two",
                    &__self_0),
        }
    }
}Debug)]
334pub(crate) enum ShortBoxSliceIntoIterInner<T> {
335    ZeroOne(Option<T>),
336    #[cfg(feature = "alloc")]
337    Multi(alloc::vec::IntoIter<T>),
338    #[cfg(not(feature = "alloc"))]
339    Two(core::array::IntoIter<T, 2>),
340}
341
342impl<T> Iterator for ShortBoxSliceIntoIter<T> {
343    type Item = T;
344    fn next(&mut self) -> Option<T> {
345        use ShortBoxSliceIntoIterInner::*;
346        match &mut self.0 {
347            ZeroOne(option) => option.take(),
348            #[cfg(feature = "alloc")]
349            Multi(into_iter) => into_iter.next(),
350            #[cfg(not(feature = "alloc"))]
351            Two(into_iter) => into_iter.next(),
352        }
353    }
354}
355
356impl<T> IntoIterator for ShortBoxSlice<T> {
357    type Item = T;
358    type IntoIter = ShortBoxSliceIntoIter<T>;
359
360    fn into_iter(self) -> Self::IntoIter {
361        match self.0 {
362            ShortBoxSliceInner::ZeroOne(option) => {
363                ShortBoxSliceIntoIter(ShortBoxSliceIntoIterInner::ZeroOne(option))
364            }
365            // TODO: Use a boxed slice IntoIter impl when available:
366            // <https://github.com/rust-lang/rust/issues/59878>
367            #[cfg(feature = "alloc")]
368            ShortBoxSliceInner::Multi(boxed_slice) => ShortBoxSliceIntoIter(
369                ShortBoxSliceIntoIterInner::Multi(boxed_slice.into_vec().into_iter()),
370            ),
371            #[cfg(not(feature = "alloc"))]
372            ShortBoxSliceInner::Two(arr) => {
373                ShortBoxSliceIntoIter(ShortBoxSliceIntoIterInner::Two(arr.into_iter()))
374            }
375        }
376    }
377}
378
379#[cfg(test)]
380mod tests {
381    use super::*;
382
383    #[test]
384    #[expect(clippy::get_first)]
385    fn test_new_single_const() {
386        const MY_CONST_SLICE: ShortBoxSlice<i32> = ShortBoxSlice::new_single(42);
387
388        assert_eq!(MY_CONST_SLICE.len(), 1);
389        assert_eq!(MY_CONST_SLICE.get(0), Some(&42));
390    }
391
392    #[test]
393    #[expect(clippy::redundant_pattern_matching)]
394    fn test_get_single() {
395        let mut vec = ShortBoxSlice::new();
396        assert!(matches!(vec.single(), None));
397
398        vec.push(100);
399        assert!(matches!(vec.single(), Some(_)));
400
401        vec.push(200);
402        assert!(matches!(vec.single(), None));
403    }
404}