Fix dropping issues with StaticVec and use it everywhere.

This commit is contained in:
Stephen Chung
2020-05-17 00:24:07 +08:00
parent 0cb781c1aa
commit a2c50879fe
11 changed files with 313 additions and 145 deletions

View File

@@ -1,6 +1,10 @@
//! Module containing various utility types and functions.
//!
//! # Safety
//!
//! The `StaticVec` type has some `unsafe` blocks.
use crate::r#unsafe::unsafe_zeroed;
use crate::r#unsafe::unsafe_uninit;
use crate::stdlib::{
any::TypeId,
@@ -8,6 +12,7 @@ use crate::stdlib::{
hash::{Hash, Hasher},
iter::FromIterator,
mem,
ops::Drop,
vec::Vec,
};
@@ -47,6 +52,8 @@ pub fn calc_fn_spec<'a>(
s.finish()
}
const MAX_STATIC_VEC: usize = 4;
/// A type to hold a number of values in static storage for speed, and any spill-overs in a `Vec`.
///
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
@@ -54,22 +61,57 @@ pub fn calc_fn_spec<'a>(
///
/// # Safety
///
/// This type uses some unsafe code (mainly to zero out unused array slots) for efficiency.
/// This type uses some unsafe code (mainly for uninitialized/unused array slots) for efficiency.
//
// TODO - remove unsafe code
#[derive(Clone, Hash)]
pub struct StaticVec<T> {
/// Total number of values held.
len: usize,
/// Static storage. 4 slots should be enough for most cases - i.e. four levels of indirection.
list: [T; 4],
/// Static storage. 4 slots should be enough for most cases - i.e. four items of fast, no-allocation access.
list: [mem::MaybeUninit<T>; MAX_STATIC_VEC],
/// Dynamic storage. For spill-overs.
more: Vec<T>,
}
impl<T> Drop for StaticVec<T> {
fn drop(&mut self) {
self.clear();
}
}
impl<T> Default for StaticVec<T> {
fn default() -> Self {
Self {
len: 0,
list: unsafe_uninit(),
more: Vec::new(),
}
}
}
impl<T: PartialEq> PartialEq for StaticVec<T> {
fn eq(&self, other: &Self) -> bool {
self.len == other.len && self.list == other.list && self.more == other.more
self.len == other.len
//&& self.list[0..self.len] == other.list[0..self.len]
&& self.more == other.more
}
}
impl<T: Clone> Clone for StaticVec<T> {
fn clone(&self) -> Self {
let mut value: Self = Default::default();
value.len = self.len;
if self.len <= self.list.len() {
for x in 0..self.len {
let item: &T = unsafe { mem::transmute(self.list.get(x).unwrap()) };
value.list[x] = mem::MaybeUninit::new(item.clone());
}
} else {
value.more = self.more.clone();
}
value
}
}
@@ -87,35 +129,75 @@ impl<T> FromIterator<T> for StaticVec<T> {
}
}
impl<T> Default for StaticVec<T> {
fn default() -> Self {
Self {
len: 0,
list: unsafe_zeroed(),
more: Vec::new(),
}
}
}
impl<T> StaticVec<T> {
fn extract(value: mem::MaybeUninit<T>) -> T {
unsafe { value.assume_init() }
}
/// Create a new `StaticVec`.
pub fn new() -> Self {
Default::default()
}
/// Empty the `StaticVec`.
pub fn clear(&mut self) {
if self.len <= self.list.len() {
for x in 0..self.len {
Self::extract(mem::replace(
self.list.get_mut(x).unwrap(),
mem::MaybeUninit::uninit(),
));
}
} else {
self.more.clear();
}
self.len = 0;
}
/// Push a new value to the end of this `StaticVec`.
pub fn push<X: Into<T>>(&mut self, value: X) {
if self.len == self.list.len() {
// Move the fixed list to the Vec
for x in 0..self.list.len() {
let def_val: T = unsafe_zeroed();
self.more
.push(mem::replace(self.list.get_mut(x).unwrap(), def_val));
}
self.more.push(value.into());
} else if self.len > self.list.len() {
self.more.extend(
self.list
.iter_mut()
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
.map(Self::extract),
);
self.more.push(value.into());
} else if self.len < self.list.len() {
mem::replace(
self.list.get_mut(self.len).unwrap(),
mem::MaybeUninit::new(value.into()),
);
} else {
self.list[self.len] = value.into();
self.more.push(value.into());
}
self.len += 1;
}
/// Insert a new value to this `StaticVec` at a particular position.
pub fn insert<X: Into<T>>(&mut self, index: usize, value: X) {
if index > self.len {
panic!("index OOB in StaticVec");
}
if self.len == self.list.len() {
// Move the fixed list to the Vec
self.more.extend(
self.list
.iter_mut()
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
.map(Self::extract),
);
self.more.insert(index, value.into());
} else if self.len < self.list.len() {
for x in (index..self.len).rev() {
let temp = mem::replace(self.list.get_mut(x).unwrap(), mem::MaybeUninit::uninit());
mem::replace(self.list.get_mut(x + 1).unwrap(), temp);
}
mem::replace(
self.list.get_mut(index).unwrap(),
mem::MaybeUninit::new(value.into()),
);
} else {
self.more.insert(index, value.into());
}
self.len += 1;
}
@@ -125,18 +207,25 @@ impl<T> StaticVec<T> {
///
/// Panics if the `StaticVec` is empty.
pub fn pop(&mut self) -> T {
let result = if self.len <= 0 {
panic!("nothing to pop!")
} else if self.len <= self.list.len() {
let def_val: T = unsafe_zeroed();
mem::replace(self.list.get_mut(self.len - 1).unwrap(), def_val)
if self.len <= 0 {
panic!("nothing to pop!");
}
let result = if self.len <= self.list.len() {
Self::extract(mem::replace(
self.list.get_mut(self.len - 1).unwrap(),
mem::MaybeUninit::uninit(),
))
} else {
let r = self.more.pop().unwrap();
// Move back to the fixed list
if self.more.len() == self.list.len() {
for x in 0..self.list.len() {
self.list[self.list.len() - 1 - x] = self.more.pop().unwrap();
for index in (0..self.list.len()).rev() {
mem::replace(
self.list.get_mut(index).unwrap(),
mem::MaybeUninit::new(self.more.pop().unwrap()),
);
}
}
@@ -151,6 +240,10 @@ impl<T> StaticVec<T> {
pub fn len(&self) -> usize {
self.len
}
/// Is this `StaticVec` empty?
pub fn is_empty(&self) -> bool {
self.len == 0
}
/// Get a reference to the item at a particular index.
///
/// # Panics
@@ -161,8 +254,10 @@ impl<T> StaticVec<T> {
panic!("index OOB in StaticVec");
}
if self.len < self.list.len() {
self.list.get(index).unwrap()
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
if self.len <= list.len() {
list.get(index).unwrap()
} else {
self.more.get(index).unwrap()
}
@@ -177,46 +272,52 @@ impl<T> StaticVec<T> {
panic!("index OOB in StaticVec");
}
if self.len < self.list.len() {
self.list.get_mut(index).unwrap()
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
if self.len <= list.len() {
list.get_mut(index).unwrap()
} else {
self.more.get_mut(index).unwrap()
}
}
/// Get an iterator to entries in the `StaticVec`.
pub fn iter(&self) -> impl Iterator<Item = &T> {
if self.len > self.list.len() {
self.more.iter()
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
if self.len <= list.len() {
list[..self.len].iter()
} else {
self.list[..self.len].iter()
self.more.iter()
}
}
/// Get a mutable iterator to entries in the `StaticVec`.
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
if self.len > self.list.len() {
self.more.iter_mut()
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
if self.len <= list.len() {
list[..self.len].iter_mut()
} else {
self.list[..self.len].iter_mut()
self.more.iter_mut()
}
}
}
impl<T: Copy> StaticVec<T> {
/// Get the item at a particular index.
impl<T: Default> StaticVec<T> {
/// Get the item at a particular index, replacing it with the default.
///
/// # Panics
///
/// Panics if the index is out of bounds.
pub fn get(&self, index: usize) -> T {
pub fn get(&mut self, index: usize) -> T {
if index >= self.len {
panic!("index OOB in StaticVec");
}
if self.len < self.list.len() {
*self.list.get(index).unwrap()
mem::take(if self.len <= self.list.len() {
unsafe { mem::transmute(self.list.get_mut(index).unwrap()) }
} else {
*self.more.get(index).unwrap()
}
self.more.get_mut(index).unwrap()
})
}
}
@@ -230,20 +331,61 @@ impl<T: fmt::Debug> fmt::Debug for StaticVec<T> {
impl<T> AsRef<[T]> for StaticVec<T> {
fn as_ref(&self) -> &[T] {
if self.len > self.list.len() {
&self.more[..]
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
if self.len <= list.len() {
&list[..self.len]
} else {
&self.list[..self.len]
&self.more[..]
}
}
}
impl<T> AsMut<[T]> for StaticVec<T> {
fn as_mut(&mut self) -> &mut [T] {
if self.len > self.list.len() {
&mut self.more[..]
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
if self.len <= list.len() {
&mut list[..self.len]
} else {
&mut self.list[..self.len]
&mut self.more[..]
}
}
}
impl<T> From<StaticVec<T>> for Vec<T> {
fn from(mut value: StaticVec<T>) -> Self {
if value.len <= value.list.len() {
value
.list
.iter_mut()
.map(|v| mem::replace(v, mem::MaybeUninit::uninit()))
.map(StaticVec::extract)
.collect()
} else {
let mut arr = Self::new();
arr.append(&mut value.more);
arr
}
}
}
impl<T> From<Vec<T>> for StaticVec<T> {
fn from(mut value: Vec<T>) -> Self {
let mut arr: Self = Default::default();
arr.len = value.len();
if arr.len <= arr.list.len() {
for x in (0..arr.len).rev() {
mem::replace(
arr.list.get_mut(x).unwrap(),
mem::MaybeUninit::new(value.pop().unwrap()),
);
}
} else {
arr.more = value;
}
arr
}
}