• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
42 mod litemap;
43 
44 #[cfg(feature = "alloc")]
45 use alloc::boxed::Box;
46 #[cfg(feature = "alloc")]
47 use alloc::vec;
48 #[cfg(feature = "alloc")]
49 use alloc::vec::Vec;
50 use core::ops::Deref;
51 use 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(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
56 pub(crate) enum ShortBoxSliceInner<T> {
57     ZeroOne(Option<T>),
58     #[cfg(feature = "alloc")]
59     Multi(Box<[T]>),
60 }
61 
62 impl<T> Default for ShortBoxSliceInner<T> {
default() -> Self63     fn default() -> Self {
64         use ShortBoxSliceInner::*;
65         ZeroOne(None)
66     }
67 }
68 
69 /// A boxed slice that supports no-allocation, constant values if length 0 or 1.
70 ///
71 /// Supports mutation but always reallocs when mutated.
72 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
73 pub(crate) struct ShortBoxSlice<T>(ShortBoxSliceInner<T>);
74 
75 impl<T> Default for ShortBoxSlice<T> {
default() -> Self76     fn default() -> Self {
77         Self(Default::default())
78     }
79 }
80 
81 impl<T> ShortBoxSlice<T> {
82     /// Creates a new, empty [`ShortBoxSlice`].
83     #[inline]
new() -> Self84     pub const fn new() -> Self {
85         use ShortBoxSliceInner::*;
86         Self(ZeroOne(None))
87     }
88 
89     /// Creates a new [`ShortBoxSlice`] containing a single element.
90     #[inline]
new_single(item: T) -> Self91     pub const fn new_single(item: T) -> Self {
92         use ShortBoxSliceInner::*;
93         Self(ZeroOne(Some(item)))
94     }
95 
96     /// Pushes an element onto this [`ShortBoxSlice`].
97     ///
98     /// Reallocs if more than 1 item is already in the collection.
99     #[cfg(feature = "alloc")]
push(&mut self, item: T)100     pub fn push(&mut self, item: T) {
101         use ShortBoxSliceInner::*;
102         self.0 = match core::mem::replace(&mut self.0, ZeroOne(None)) {
103             ZeroOne(None) => ZeroOne(Some(item)),
104             ZeroOne(Some(prev_item)) => Multi(vec![prev_item, item].into_boxed_slice()),
105             Multi(items) => {
106                 let mut items = items.into_vec();
107                 items.push(item);
108                 Multi(items.into_boxed_slice())
109             }
110         };
111     }
112 
113     /// Gets a single element from the [`ShortBoxSlice`].
114     ///
115     /// Returns `None` if empty or more than one element.
116     #[inline]
single(&self) -> Option<&T>117     pub const fn single(&self) -> Option<&T> {
118         use ShortBoxSliceInner::*;
119         match self.0 {
120             ZeroOne(Some(ref v)) => Some(v),
121             _ => None,
122         }
123     }
124 
125     /// Destruct into a single element of the [`ShortBoxSlice`].
126     ///
127     /// Returns `None` if empty or more than one element.
into_single(self) -> Option<T>128     pub fn into_single(self) -> Option<T> {
129         use ShortBoxSliceInner::*;
130         match self.0 {
131             ZeroOne(Some(v)) => Some(v),
132             _ => None,
133         }
134     }
135 
136     /// Returns the number of elements in the collection.
137     #[inline]
len(&self) -> usize138     pub fn len(&self) -> usize {
139         use ShortBoxSliceInner::*;
140         match self.0 {
141             ZeroOne(None) => 0,
142             ZeroOne(_) => 1,
143             #[cfg(feature = "alloc")]
144             Multi(ref v) => v.len(),
145         }
146     }
147 
148     /// Returns whether the collection is empty.
149     #[inline]
is_empty(&self) -> bool150     pub const fn is_empty(&self) -> bool {
151         use ShortBoxSliceInner::*;
152         matches!(self.0, ZeroOne(None))
153     }
154 
155     /// Inserts an element at the specified index into the collection.
156     ///
157     /// Reallocs if more than 1 item is already in the collection.
158     #[cfg(feature = "alloc")]
insert(&mut self, index: usize, elt: T)159     pub fn insert(&mut self, index: usize, elt: T) {
160         use ShortBoxSliceInner::*;
161         assert!(
162             index <= self.len(),
163             "insertion index (is {}) should be <= len (is {})",
164             index,
165             self.len()
166         );
167 
168         self.0 = match core::mem::replace(&mut self.0, ZeroOne(None)) {
169             ZeroOne(None) => ZeroOne(Some(elt)),
170             ZeroOne(Some(item)) => {
171                 let items = if index == 0 {
172                     vec![elt, item].into_boxed_slice()
173                 } else {
174                     vec![item, elt].into_boxed_slice()
175                 };
176                 Multi(items)
177             }
178             Multi(items) => {
179                 let mut items = items.into_vec();
180                 items.insert(index, elt);
181                 Multi(items.into_boxed_slice())
182             }
183         }
184     }
185 
186     /// Removes the element at the specified index from the collection.
187     ///
188     /// Reallocs if more than 2 items are in the collection.
remove(&mut self, index: usize) -> T189     pub fn remove(&mut self, index: usize) -> T {
190         use ShortBoxSliceInner::*;
191         assert!(
192             index < self.len(),
193             "removal index (is {}) should be < len (is {})",
194             index,
195             self.len()
196         );
197 
198         let (replaced, removed_item) = match core::mem::replace(&mut self.0, ZeroOne(None)) {
199             ZeroOne(None) => unreachable!(),
200             ZeroOne(Some(v)) => (ZeroOne(None), v),
201             #[cfg(feature = "alloc")]
202             Multi(v) => {
203                 let mut v = v.into_vec();
204                 let removed_item = v.remove(index);
205                 match v.len() {
206                     #[allow(clippy::unwrap_used)]
207                     // we know that the vec has exactly one element left
208                     1 => (ZeroOne(Some(v.pop().unwrap())), removed_item),
209                     // v has at least 2 elements, create a Multi variant
210                     _ => (Multi(v.into_boxed_slice()), removed_item),
211                 }
212             }
213         };
214         self.0 = replaced;
215         removed_item
216     }
217 
218     /// Removes all elements from the collection.
219     #[inline]
clear(&mut self)220     pub fn clear(&mut self) {
221         use ShortBoxSliceInner::*;
222         let _ = core::mem::replace(&mut self.0, ZeroOne(None));
223     }
224 
225     /// Retains only the elements specified by the predicate.
226     #[allow(dead_code)]
retain<F>(&mut self, mut f: F) where F: FnMut(&T) -> bool,227     pub fn retain<F>(&mut self, mut f: F)
228     where
229         F: FnMut(&T) -> bool,
230     {
231         use ShortBoxSliceInner::*;
232         match core::mem::take(&mut self.0) {
233             ZeroOne(Some(one)) if f(&one) => self.0 = ZeroOne(Some(one)),
234             ZeroOne(_) => self.0 = ZeroOne(None),
235             #[cfg(feature = "alloc")]
236             Multi(slice) => {
237                 let mut vec = slice.into_vec();
238                 vec.retain(f);
239                 *self = ShortBoxSlice::from(vec)
240             }
241         };
242     }
243 }
244 
245 impl<T> Deref for ShortBoxSlice<T> {
246     type Target = [T];
247 
deref(&self) -> &Self::Target248     fn deref(&self) -> &Self::Target {
249         use ShortBoxSliceInner::*;
250         match self.0 {
251             ZeroOne(None) => &[],
252             ZeroOne(Some(ref v)) => core::slice::from_ref(v),
253             #[cfg(feature = "alloc")]
254             Multi(ref v) => v,
255         }
256     }
257 }
258 
259 impl<T> DerefMut for ShortBoxSlice<T> {
deref_mut(&mut self) -> &mut Self::Target260     fn deref_mut(&mut self) -> &mut Self::Target {
261         use ShortBoxSliceInner::*;
262         match self.0 {
263             ZeroOne(None) => &mut [],
264             ZeroOne(Some(ref mut v)) => core::slice::from_mut(v),
265             #[cfg(feature = "alloc")]
266             Multi(ref mut v) => v,
267         }
268     }
269 }
270 
271 #[cfg(feature = "alloc")]
272 impl<T> From<Vec<T>> for ShortBoxSlice<T> {
from(v: Vec<T>) -> Self273     fn from(v: Vec<T>) -> Self {
274         use ShortBoxSliceInner::*;
275         match v.len() {
276             0 => Self(ZeroOne(None)),
277             #[allow(clippy::unwrap_used)] // we know that the vec is not empty
278             1 => Self(ZeroOne(Some(v.into_iter().next().unwrap()))),
279             _ => Self(Multi(v.into_boxed_slice())),
280         }
281     }
282 }
283 
284 #[cfg(feature = "alloc")]
285 impl<T> FromIterator<T> for ShortBoxSlice<T> {
from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self286     fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
287         use ShortBoxSliceInner::*;
288         let mut iter = iter.into_iter();
289         match (iter.next(), iter.next()) {
290             (Some(first), Some(second)) => {
291                 // Size hint behaviour same as `Vec::extend` + 2
292                 let mut vec = Vec::with_capacity(iter.size_hint().0.saturating_add(3));
293                 vec.push(first);
294                 vec.push(second);
295                 vec.extend(iter);
296                 Self(Multi(vec.into_boxed_slice()))
297             }
298             (first, _) => Self(ZeroOne(first)),
299         }
300     }
301 }
302 
303 /// An iterator that yields elements from a [`ShortBoxSlice`].
304 #[derive(Debug)]
305 pub struct ShortBoxSliceIntoIter<T>(ShortBoxSliceIntoIterInner<T>);
306 
307 #[derive(Debug)]
308 pub(crate) enum ShortBoxSliceIntoIterInner<T> {
309     ZeroOne(Option<T>),
310     #[cfg(feature = "alloc")]
311     Multi(alloc::vec::IntoIter<T>),
312 }
313 
314 impl<T> Iterator for ShortBoxSliceIntoIter<T> {
315     type Item = T;
next(&mut self) -> Option<T>316     fn next(&mut self) -> Option<T> {
317         use ShortBoxSliceIntoIterInner::*;
318         match &mut self.0 {
319             ZeroOne(option) => option.take(),
320             #[cfg(feature = "alloc")]
321             Multi(into_iter) => into_iter.next(),
322         }
323     }
324 }
325 
326 impl<T> IntoIterator for ShortBoxSlice<T> {
327     type Item = T;
328     type IntoIter = ShortBoxSliceIntoIter<T>;
329 
into_iter(self) -> Self::IntoIter330     fn into_iter(self) -> Self::IntoIter {
331         match self.0 {
332             ShortBoxSliceInner::ZeroOne(option) => {
333                 ShortBoxSliceIntoIter(ShortBoxSliceIntoIterInner::ZeroOne(option))
334             }
335             // TODO: Use a boxed slice IntoIter impl when available:
336             // <https://github.com/rust-lang/rust/issues/59878>
337             #[cfg(feature = "alloc")]
338             ShortBoxSliceInner::Multi(boxed_slice) => ShortBoxSliceIntoIter(
339                 ShortBoxSliceIntoIterInner::Multi(boxed_slice.into_vec().into_iter()),
340             ),
341         }
342     }
343 }
344 
345 #[cfg(test)]
346 mod tests {
347     use super::*;
348 
349     #[test]
350     #[allow(clippy::get_first)]
test_new_single_const()351     fn test_new_single_const() {
352         const MY_CONST_SLICE: ShortBoxSlice<i32> = ShortBoxSlice::new_single(42);
353 
354         assert_eq!(MY_CONST_SLICE.len(), 1);
355         assert_eq!(MY_CONST_SLICE.get(0), Some(&42));
356     }
357 
358     #[test]
359     #[allow(clippy::redundant_pattern_matching)]
test_get_single()360     fn test_get_single() {
361         let mut vec = ShortBoxSlice::new();
362         assert!(matches!(vec.single(), None));
363 
364         vec.push(100);
365         assert!(matches!(vec.single(), Some(_)));
366 
367         vec.push(200);
368         assert!(matches!(vec.single(), None));
369     }
370 }
371