vec_array/
lib.rs

1//! This library provides `VecArray`, an array-like type that holds a number of values backed by
2//! a fixed-sized array for no-allocation, quick access.
3//! If more items than the array's capacity are stored, it automatically converts into using a `Vec`.
4//!
5//! This crate similar to the [`staticvec`](https://crates.io/crates/staticvec) crate but has
6//! different emphasis: e.g. it can grow beyond the array's capacity, and it compiles on stable.
7//!
8//! # Implementation
9//!
10//! A `VecArray` holds data in _either one_ of two storages:
11//!
12//! 1) a fixed-size array of `MAX_ARRAY_SIZE` (defaults to 4) items, and
13//! 2) a dynamic `Vec` with unlimited items.
14//!
15//! At any time, either one of them (or both) must be empty, depending on the capacity of the array.
16//!
17//! There is a `len` field containing the total number of items held by the `VecArray`.
18//!
19//! The fixed-size array is not initialized (i.e. initialized with `MaybeUninit::uninit()`).
20//!
21//! When `len <= MAX_ARRAY_SIZE`, all elements are stored in the fixed-size array.
22//! Array slots `>= len` are `MaybeUninit::uninit()` while slots `< len` are considered actual data.
23//! In this scenario, the `Vec` (`more`) is empty.
24//!
25//! As soon as we try to push a new item into the `VecArray` that makes the total number exceed
26//! `MAX_ARRAY_SIZE`, all the items in the fixed-sized array are taken out, replaced with
27//! `MaybeUninit::uninit()` (via `mem::replace`) and pushed into the `Vec`.
28//! Then the new item is added to the `Vec`.
29//!
30//! Therefore, if `len > MAX_ARRAY_SIZE`, then the fixed-size array is considered empty and
31//! uninitialized while all data resides in the `Vec`.
32//!
33//! When popping an item off of the `VecArray`, the reverse is true.  If `len == MAX_ARRAY_SIZE + 1`,
34//! after popping the item, all the items residing in the `Vec` are moved back to the fixed-size array.
35//! The `Vec` will then be empty.
36//!
37//! Therefore, if `len <= MAX_ARRAY_SIZE`, data is in the fixed-size array.
38//! Otherwise, data is in the `Vec`.
39//!
40//! # Limitations
41//!
42//! 1) The constant `MAX_ARRAY_SIZE` must be compiled in, at least until constant generics
43//!    land in Rust.  It defaults to 4, and you must clone this repo and change it to another number.
44//!
45//! 2) It automatically converts itself into a `Vec` when over `MAX_ARRAY_SIZE` and back into an array
46//!    when the number of items drops below this threshold.  If it so happens that the data is constantly
47//!    added and removed from the `VecArray` that straddles this threshold, you'll see excessive
48//!    moving and copying of data back-and-forth, plus allocations and deallocations of the `Vec`.
49
50#![cfg_attr(feature = "no_std", no_std)]
51
52#[cfg(feature = "no_std")]
53extern crate alloc;
54
55#[cfg(not(feature = "no_std"))]
56use std::{
57    fmt,
58    hash::{Hash, Hasher},
59    iter::FromIterator,
60    mem::{self, MaybeUninit},
61    ops::{Deref, DerefMut, Index, IndexMut},
62};
63
64#[cfg(feature = "no_std")]
65use core::{
66    fmt,
67    hash::{Hash, Hasher},
68    iter::FromIterator,
69    mem::{self, MaybeUninit},
70    ops::{Deref, DerefMut, Index, IndexMut},
71};
72
73#[cfg(feature = "no_std")]
74use alloc::{boxed::Box, vec::Vec};
75
76/// An array-like type that holds a number of values in static storage for no-allocation, quick access.
77///
78/// # Safety
79///
80/// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency.
81pub struct VecArray<T> {
82    /// Total number of values held.
83    len: usize,
84    /// Fixed-size storage for fast, no-allocation access.
85    array_store: [MaybeUninit<T>; MAX_ARRAY_SIZE],
86    /// Dynamic storage. For spill-overs.
87    vec_store: Vec<T>,
88}
89
90/// Maximum slots of fixed-size storage for a `VecArray`.
91/// Defaults to 4, which should be enough for many cases and is a good balance between
92/// memory consumption (for the fixed-size array) and reduced allocations.
93///
94/// # Usage Considerations
95///
96/// To alter this size right now, unfortunately you must clone this repo and modify the code directly.
97///
98/// This cannot be avoided until constant generics land in Rust.
99pub const MAX_ARRAY_SIZE: usize = 4;
100
101impl<T> Drop for VecArray<T> {
102    fn drop(&mut self) {
103        self.clear();
104    }
105}
106
107impl<T: Hash> Hash for VecArray<T> {
108    fn hash<H: Hasher>(&self, state: &mut H) {
109        self.iter().for_each(|x| x.hash(state));
110    }
111}
112
113impl<T> Default for VecArray<T> {
114    fn default() -> Self {
115        Self {
116            len: 0,
117            array_store: unsafe { mem::MaybeUninit::uninit().assume_init() },
118            vec_store: Vec::new(),
119        }
120    }
121}
122
123impl<T: PartialEq> PartialEq for VecArray<T> {
124    fn eq(&self, other: &Self) -> bool {
125        if self.len != other.len || self.vec_store != other.vec_store {
126            return false;
127        }
128
129        if self.len > MAX_ARRAY_SIZE {
130            return true;
131        }
132
133        unsafe {
134            mem::transmute::<_, &[T; MAX_ARRAY_SIZE]>(&self.array_store)
135                == mem::transmute::<_, &[T; MAX_ARRAY_SIZE]>(&other.array_store)
136        }
137    }
138}
139
140impl<T: Clone> Clone for VecArray<T> {
141    fn clone(&self) -> Self {
142        let mut value: Self = Default::default();
143        value.len = self.len;
144
145        if self.is_fixed_storage() {
146            for x in 0..self.len {
147                let item = self.array_store.get(x).unwrap();
148                let item_value = unsafe { mem::transmute::<_, &T>(item) };
149                value.array_store[x] = MaybeUninit::new(item_value.clone());
150            }
151        } else {
152            value.vec_store = self.vec_store.clone();
153        }
154
155        value
156    }
157}
158
159impl<T: Eq> Eq for VecArray<T> {}
160
161impl<T> FromIterator<T> for VecArray<T> {
162    fn from_iter<X: IntoIterator<Item = T>>(iter: X) -> Self {
163        let mut vec = VecArray::new();
164
165        for x in iter {
166            vec.push(x);
167        }
168
169        vec
170    }
171}
172
173impl<T: 'static> IntoIterator for VecArray<T> {
174    type Item = T;
175    type IntoIter = Box<dyn Iterator<Item = T>>;
176
177    fn into_iter(self) -> Self::IntoIter {
178        self.into_iter()
179    }
180}
181
182impl<T> VecArray<T> {
183    /// Create a new `VecArray`.
184    pub fn new() -> Self {
185        Default::default()
186    }
187
188    /// Empty the `VecArray`.
189    pub fn clear(&mut self) {
190        if self.is_fixed_storage() {
191            for x in 0..self.len {
192                self.extract_from_list(x);
193            }
194        } else {
195            self.vec_store.clear();
196        }
197        self.len = 0;
198    }
199
200    /// Extract a `MaybeUninit` into a concrete initialized type.
201    fn extract(value: MaybeUninit<T>) -> T {
202        unsafe { value.assume_init() }
203    }
204
205    /// Extract an item from the fixed-size array, replacing it with `MaybeUninit::uninit()`.
206    ///
207    /// # Panics
208    ///
209    /// Panics if fixed-size storage is not used, or if the `index` is out of bounds.
210    fn extract_from_list(&mut self, index: usize) -> T {
211        if !self.is_fixed_storage() {
212            panic!("not fixed storage in VecArray");
213        }
214        if index >= self.len {
215            panic!("index OOB in VecArray");
216        }
217        Self::extract(mem::replace(
218            self.array_store.get_mut(index).unwrap(),
219            MaybeUninit::uninit(),
220        ))
221    }
222
223    /// Set an item into the fixed-size array.
224    /// If `drop` is `true`, the original value is extracted then automatically dropped.
225    ///
226    /// # Panics
227    ///
228    /// Panics if fixed-size storage is not used, or if the `index` is out of bounds.
229    fn set_into_list(&mut self, index: usize, value: T, drop: bool) {
230        if !self.is_fixed_storage() {
231            panic!("not fixed storage in VecArray");
232        }
233        // Allow setting at most one slot to the right
234        if index > self.len {
235            panic!("index OOB in VecArray");
236        }
237        let temp = mem::replace(
238            self.array_store.get_mut(index).unwrap(),
239            MaybeUninit::new(value),
240        );
241        if drop {
242            // Extract the original value - which will drop it automatically
243            Self::extract(temp);
244        }
245    }
246
247    /// Move item in the fixed-size array into the `Vec`.
248    ///
249    /// # Panics
250    ///
251    /// Panics if fixed-size storage is not used, or if the fixed-size storage is not full.
252    fn move_fixed_into_vec(&mut self, num: usize) {
253        if !self.is_fixed_storage() {
254            panic!("not fixed storage in VecArray");
255        }
256        if self.len != num {
257            panic!("fixed storage is not full in VecArray");
258        }
259        self.vec_store.extend(
260            self.array_store
261                .iter_mut()
262                .take(num)
263                .map(|v| mem::replace(v, MaybeUninit::uninit()))
264                .map(Self::extract),
265        );
266    }
267
268    /// Is data stored in fixed-size storage?
269    fn is_fixed_storage(&self) -> bool {
270        self.len <= MAX_ARRAY_SIZE
271    }
272
273    /// Push a new value to the end of this `VecArray`.
274    pub fn push<X: Into<T>>(&mut self, value: X) {
275        if self.len == MAX_ARRAY_SIZE {
276            self.move_fixed_into_vec(MAX_ARRAY_SIZE);
277            self.vec_store.push(value.into());
278        } else if self.is_fixed_storage() {
279            self.set_into_list(self.len, value.into(), false);
280        } else {
281            self.vec_store.push(value.into());
282        }
283        self.len += 1;
284    }
285
286    /// Insert a new value to this `VecArray` at a particular position.
287    pub fn insert<X: Into<T>>(&mut self, index: usize, value: X) {
288        let index = if index > self.len { self.len } else { index };
289
290        if self.len == MAX_ARRAY_SIZE {
291            self.move_fixed_into_vec(MAX_ARRAY_SIZE);
292            self.vec_store.insert(index, value.into());
293        } else if self.is_fixed_storage() {
294            // Move all items one slot to the right
295            for x in (index..self.len).rev() {
296                let orig_value = self.extract_from_list(x);
297                self.set_into_list(x + 1, orig_value, false);
298            }
299            self.set_into_list(index, value.into(), false);
300        } else {
301            self.vec_store.insert(index, value.into());
302        }
303        self.len += 1;
304    }
305
306    /// Pop a value from the end of this `VecArray`.
307    pub fn pop(&mut self) -> Option<T> {
308        if self.is_empty() {
309            return None;
310        }
311
312        Some(if self.is_fixed_storage() {
313            let value = self.extract_from_list(self.len - 1);
314            self.len -= 1;
315            value
316        } else {
317            let value = self.vec_store.pop().unwrap();
318            self.len -= 1;
319
320            // Move back to the fixed list
321            if self.vec_store.len() == MAX_ARRAY_SIZE {
322                for index in (0..MAX_ARRAY_SIZE).rev() {
323                    let item = self.vec_store.pop().unwrap();
324                    self.set_into_list(index, item, false);
325                }
326            }
327
328            value
329        })
330    }
331
332    /// Remove a value from this `VecArray` at a particular position.
333    pub fn remove(&mut self, index: usize) -> Option<T> {
334        if index >= self.len {
335            return None;
336        }
337
338        Some(if self.is_fixed_storage() {
339            let value = self.extract_from_list(index);
340
341            // Move all items one slot to the left
342            for x in index + 1..self.len {
343                let orig_value = self.extract_from_list(x);
344                self.set_into_list(x - 1, orig_value, false);
345            }
346            self.len -= 1;
347
348            value
349        } else {
350            let value = self.vec_store.remove(index);
351            self.len -= 1;
352
353            // Move back to the fixed list
354            if self.vec_store.len() == MAX_ARRAY_SIZE {
355                for index in (0..MAX_ARRAY_SIZE).rev() {
356                    let item = self.vec_store.pop().unwrap();
357                    self.set_into_list(index, item, false);
358                }
359            }
360
361            value
362        })
363    }
364
365    /// Get the number of items in this `VecArray`.
366    pub fn len(&self) -> usize {
367        self.len
368    }
369
370    /// Is this `VecArray` empty?
371    pub fn is_empty(&self) -> bool {
372        self.len == 0
373    }
374
375    /// Get a reference to the item at a particular index.
376    pub fn get(&self, index: usize) -> Option<&T> {
377        if index >= self.len {
378            return None;
379        }
380
381        let list = unsafe { mem::transmute::<_, &[T; MAX_ARRAY_SIZE]>(&self.array_store) };
382
383        if self.is_fixed_storage() {
384            list.get(index)
385        } else {
386            self.vec_store.get(index)
387        }
388    }
389
390    /// Get a mutable reference to the item at a particular index.
391    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
392        if index >= self.len {
393            return None;
394        }
395
396        let list = unsafe { mem::transmute::<_, &mut [T; MAX_ARRAY_SIZE]>(&mut self.array_store) };
397
398        if self.is_fixed_storage() {
399            list.get_mut(index)
400        } else {
401            self.vec_store.get_mut(index)
402        }
403    }
404
405    /// Get an iterator to entries in the `VecArray`.
406    pub fn iter(&self) -> impl Iterator<Item = &T> {
407        let list = unsafe { mem::transmute::<_, &[T; MAX_ARRAY_SIZE]>(&self.array_store) };
408
409        if self.is_fixed_storage() {
410            list[..self.len].iter()
411        } else {
412            self.vec_store.iter()
413        }
414    }
415
416    /// Get a mutable iterator to entries in the `VecArray`.
417    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
418        let list = unsafe { mem::transmute::<_, &mut [T; MAX_ARRAY_SIZE]>(&mut self.array_store) };
419
420        if self.is_fixed_storage() {
421            list[..self.len].iter_mut()
422        } else {
423            self.vec_store.iter_mut()
424        }
425    }
426}
427
428impl<T: 'static> VecArray<T> {
429    /// Get a mutable iterator to entries in the `VecArray`.
430    pub fn into_iter(mut self) -> Box<dyn Iterator<Item = T>> {
431        if self.is_fixed_storage() {
432            let mut it = FixedStorageIterator {
433                data: unsafe { mem::MaybeUninit::uninit().assume_init() },
434                index: 0,
435                limit: self.len,
436            };
437
438            for x in 0..self.len {
439                it.data[x] =
440                    mem::replace(self.array_store.get_mut(x).unwrap(), MaybeUninit::uninit());
441            }
442            self.len = 0;
443
444            Box::new(it)
445        } else {
446            Box::new(Vec::from(self).into_iter())
447        }
448    }
449}
450
451/// An iterator that takes control of the fixed-size storage of a `VecArray` and returns its values.
452struct FixedStorageIterator<T> {
453    data: [MaybeUninit<T>; MAX_ARRAY_SIZE],
454    index: usize,
455    limit: usize,
456}
457
458impl<T> Iterator for FixedStorageIterator<T> {
459    type Item = T;
460
461    fn next(&mut self) -> Option<Self::Item> {
462        if self.index >= self.limit {
463            None
464        } else {
465            self.index += 1;
466
467            let value = mem::replace(
468                self.data.get_mut(self.index - 1).unwrap(),
469                MaybeUninit::uninit(),
470            );
471
472            unsafe { Some(value.assume_init()) }
473        }
474    }
475}
476
477impl<T: Default> VecArray<T> {
478    /// Get the item at a particular index, replacing it with the default.
479    pub fn take(&mut self, index: usize) -> Option<T> {
480        if index >= self.len {
481            return None;
482        }
483
484        if self.is_fixed_storage() {
485            self.array_store
486                .get_mut(index)
487                .map(|v| unsafe { mem::transmute(v) })
488        } else {
489            self.vec_store.get_mut(index)
490        }
491        .map(mem::take)
492    }
493}
494
495impl<T: fmt::Debug> fmt::Debug for VecArray<T> {
496    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497        fmt::Debug::fmt(&self.iter().collect::<Vec<_>>(), f)
498    }
499}
500
501impl<T> AsRef<[T]> for VecArray<T> {
502    fn as_ref(&self) -> &[T] {
503        let list = unsafe { mem::transmute::<_, &[T; MAX_ARRAY_SIZE]>(&self.array_store) };
504
505        if self.is_fixed_storage() {
506            &list[..self.len]
507        } else {
508            &self.vec_store[..]
509        }
510    }
511}
512
513impl<T> AsMut<[T]> for VecArray<T> {
514    fn as_mut(&mut self) -> &mut [T] {
515        let list = unsafe { mem::transmute::<_, &mut [T; MAX_ARRAY_SIZE]>(&mut self.array_store) };
516
517        if self.is_fixed_storage() {
518            &mut list[..self.len]
519        } else {
520            &mut self.vec_store[..]
521        }
522    }
523}
524
525impl<T> Deref for VecArray<T> {
526    type Target = [T];
527    fn deref(&self) -> &Self::Target {
528        self.as_ref()
529    }
530}
531
532impl<T> DerefMut for VecArray<T> {
533    fn deref_mut(&mut self) -> &mut Self::Target {
534        self.as_mut()
535    }
536}
537
538impl<T> Index<usize> for VecArray<T> {
539    type Output = T;
540
541    fn index(&self, index: usize) -> &Self::Output {
542        self.get(index).unwrap()
543    }
544}
545
546impl<T> IndexMut<usize> for VecArray<T> {
547    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
548        self.get_mut(index).unwrap()
549    }
550}
551
552impl<T> From<VecArray<T>> for Vec<T> {
553    fn from(mut value: VecArray<T>) -> Self {
554        if value.len <= MAX_ARRAY_SIZE {
555            value.move_fixed_into_vec(value.len);
556        }
557        value.len = 0;
558
559        let mut arr = Self::new();
560        arr.append(&mut value.vec_store);
561        arr
562    }
563}
564
565impl<T> From<Vec<T>> for VecArray<T> {
566    fn from(mut value: Vec<T>) -> Self {
567        let mut arr: Self = Default::default();
568        arr.len = value.len();
569
570        if arr.len <= MAX_ARRAY_SIZE {
571            for x in (0..arr.len).rev() {
572                arr.set_into_list(x, value.pop().unwrap(), false);
573            }
574        } else {
575            arr.vec_store = value;
576        }
577
578        arr
579    }
580}