#![forbid(unsafe_code)]
use core::{
convert::{TryFrom, TryInto},
fmt, iter,
marker::PhantomData,
ops::Sub,
str,
};
use tinyvec::TinyVec;
#[derive(Clone)]
pub struct FlatVec<T, IndexTy: Default, BackingTy, const INDEX_INLINE_LEN: usize> {
data: Box<[BackingTy]>,
data_len: usize,
ends: TinyVec<[IndexTy; INDEX_INLINE_LEN]>,
marker: PhantomData<T>,
}
pub type MyFlatVec<T> = FlatVec<T, usize, u8, 3>;
impl<T, IndexTy, BackingTy, const INDEX_INLINE_LEN: usize> fmt::Debug
for FlatVec<T, IndexTy, BackingTy, INDEX_INLINE_LEN>
where
IndexTy: fmt::Debug + Default,
BackingTy: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("FlatVec")
.field("data", &&self.data[..self.data_len])
.field("ends", &self.ends)
.finish()
}
}
impl<T, IndexTy, BackingTy, const INDEX_INLINE_LEN: usize> Default
for FlatVec<T, IndexTy, BackingTy, INDEX_INLINE_LEN>
where
IndexTy: Default,
{
#[inline]
fn default() -> Self {
Self {
data: Box::default(),
data_len: 0,
ends: TinyVec::default(),
marker: PhantomData::default(),
}
}
}
impl<'a, T: 'a, IndexTy, BackingTy, const INDEX_INLINE_LEN: usize>
FlatVec<T, IndexTy, BackingTy, INDEX_INLINE_LEN>
where
IndexTy: Default,
IndexTy: TryFrom<usize> + Copy + Sub,
usize: TryFrom<IndexTy>,
<IndexTy as TryFrom<usize>>::Error: fmt::Debug,
<usize as TryFrom<IndexTy>>::Error: fmt::Debug,
{
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.ends.len()
}
#[inline]
#[must_use]
pub fn data_len(&self) -> usize {
self.data_len
}
#[inline]
#[must_use]
pub fn data_capacity(&self) -> usize {
self.data.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.ends.len() == 0
}
#[inline]
pub fn clear(&mut self) {
self.data_len = 0;
self.ends.clear();
}
#[inline]
pub fn push<Source>(&mut self, input: Source)
where
Source: IntoFlat<BackingTy, T>,
{
input.into_flat(Storage {
data: &mut self.data,
data_len: &mut self.data_len,
});
self.ends.push(self.data_len.try_into().unwrap());
}
#[inline]
#[must_use]
pub fn get<Dest: 'a>(&'a self, index: usize) -> Option<Dest>
where
Dest: FromFlat<'a, BackingTy, T>,
{
if index >= self.ends.len() {
None
} else {
let end = self.ends[index].try_into().unwrap();
let start = if index == 0 {
0
} else {
self.ends[index - 1].try_into().unwrap()
};
Some(Dest::from_flat(&self.data[start..end]))
}
}
#[inline]
pub fn iter<Dest: 'a>(&'a self) -> impl Iterator<Item = Dest> + 'a
where
Dest: FromFlat<'a, BackingTy, T>,
{
iter::once(0)
.chain(self.ends.iter().copied().map(|v| v.try_into().unwrap()))
.zip(self.ends.iter().copied().map(|v| v.try_into().unwrap()))
.map(move |(start, end)| Dest::from_flat(&self.data[start..end]))
}
}
impl<'a, T: 'a, IndexTy, BackingTy, const INDEX_INLINE_LEN: usize>
FlatVec<T, IndexTy, BackingTy, INDEX_INLINE_LEN>
where
IndexTy: Default,
IndexTy: TryFrom<usize> + Copy + Sub,
usize: TryFrom<IndexTy>,
<IndexTy as TryFrom<usize>>::Error: fmt::Debug,
<usize as TryFrom<IndexTy>>::Error: fmt::Debug,
BackingTy: Copy,
{
#[inline]
pub fn remove(&mut self, index: usize) {
let end: usize = self.ends[index].try_into().unwrap();
let start = if index == 0 {
0
} else {
self.ends[index - 1].try_into().unwrap()
};
self.data.copy_within(end.., start);
self.ends.remove(index);
let removed_len = end - start;
self.data_len -= removed_len;
self.ends.iter_mut().skip(index).for_each(|end| {
let change = usize::try_from(*end).unwrap() - removed_len;
*end = change.try_into().unwrap();
});
}
}
pub struct Storage<'a, BackingTy> {
data: &'a mut Box<[BackingTy]>,
data_len: &'a mut usize,
}
impl<BackingTy> Storage<'_, BackingTy>
where
BackingTy: Default,
{
#[inline(never)]
fn allocate_slow_path(&mut self, requested: usize) {
let mut data = std::mem::take(self.data).into_vec();
data.resize_with(
std::cmp::max(requested + data.len(), 2 * data.len()),
BackingTy::default,
);
*self.data = data.into_boxed_slice();
}
#[inline]
pub fn allocate(&mut self, requested: usize) -> &mut [BackingTy] {
self.reserve(requested);
let old_len = *self.data_len;
*self.data_len += requested;
&mut self.data[old_len..old_len + requested]
}
#[inline]
pub fn reserve(&mut self, requested: usize) {
if self.data.len() < *self.data_len + requested {
self.allocate_slow_path(requested);
}
}
#[inline]
pub fn extend<Iter>(&mut self, iter: Iter)
where
Iter: IntoIterator<Item = BackingTy>,
{
let mut iter = iter.into_iter();
let hint = iter.size_hint();
if Some(hint.0) == hint.1 {
let data = self.allocate(hint.0);
for (src, dst) in iter.into_iter().zip(data.iter_mut()) {
*dst = src;
}
return;
}
for (out, val) in self.data.iter_mut().skip(*self.data_len).zip(&mut iter) {
*out = val;
*self.data_len += 1;
}
if let Some(val) = iter.next() {
let mut data = std::mem::take(self.data).into_vec();
data.push(val);
*self.data_len += 1;
for val in iter {
data.push(val);
*self.data_len += 1;
}
if data.capacity() > data.len() {
data.resize_with(data.capacity(), BackingTy::default);
}
let mut data = data.into_boxed_slice();
std::mem::swap(self.data, &mut data);
std::mem::forget(data);
}
}
}
pub trait IntoFlat<BackingTy, Flattened> {
fn into_flat(self, storage: Storage<BackingTy>);
}
pub trait FromFlat<'a, BackingTy, Flattened> {
fn from_flat(data: &'a [BackingTy]) -> Self;
}
impl IntoFlat<u8, String> for &str {
#[inline]
fn into_flat(self, mut store: Storage<u8>) {
store
.allocate(self.as_bytes().len())
.copy_from_slice(self.as_bytes());
}
}
impl<'a> FromFlat<'a, u8, String> for &'a str {
#[inline]
fn from_flat(data: &'a [u8]) -> &'a str {
str::from_utf8(&data).unwrap()
}
}
impl<Iter, BackingTy> IntoFlat<BackingTy, Vec<BackingTy>> for Iter
where
Iter: IntoIterator<Item = BackingTy>,
BackingTy: Default,
{
#[inline]
fn into_flat(self, mut store: Storage<BackingTy>) {
store.extend(self);
}
}
impl<'a, BackingTy> FromFlat<'a, BackingTy, Vec<BackingTy>> for &'a [BackingTy] {
#[inline]
fn from_flat(data: &'a [BackingTy]) -> &'a [BackingTy] {
data
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_get() {
let mut names: FlatVec<String, usize, u8, 3> = FlatVec::new();
assert!(names.is_empty());
assert_eq!(names.len(), 0);
assert_eq!(names.data_len(), 0);
names.push("Cerryl");
assert_eq!(names.len(), 1);
assert!(!names.is_empty());
assert_eq!(names.data_len(), 6);
names.push("Jeslek");
assert_eq!(names.len(), 2);
assert_eq!(names.data_len(), 12);
assert_eq!(names.get(0), Some("Cerryl"));
assert_eq!(names.get(1), Some("Jeslek"));
assert_eq!(names.get::<&str>(2), None);
assert_eq!(names.get(0), Some("Cerryl"));
assert_eq!(names.get(1), Some("Jeslek"));
assert_eq!(names.get::<&str>(2), None);
names.clear();
assert_eq!(names.len(), 0);
assert!(names.is_empty());
assert_eq!(names.data_len(), 0);
}
#[test]
fn iter() {
let mut names: FlatVec<String, usize, u8, 3> = FlatVec::new();
names.push("Cerryl");
names.push("Jeslek");
let as_vec = names.iter().collect::<Vec<&str>>();
assert_eq!(as_vec, vec!["Cerryl".to_string(), "Jeslek".to_string()]);
}
#[test]
fn remove() {
let mut places: FlatVec<String, usize, u8, 3> = FlatVec::new();
places.push("Cyador");
places.push("Recluce");
places.push("Hamor");
assert_eq!(places.get(1), Some("Recluce"));
assert_eq!(places.get(2), Some("Hamor"));
places.remove(1);
assert_eq!(places.get(1), Some("Hamor"));
assert_eq!(places.get::<&str>(2), None);
places.clear();
places.push("Cyador");
places.push("Recluce");
places.push("Hamor");
assert_eq!(places.get(0), Some("Cyador"));
assert_eq!(places.get(1), Some("Recluce"));
places.remove(0);
assert_eq!(places.get(0), Some("Recluce"));
assert_eq!(places.get(1), Some("Hamor"));
}
struct Expander(usize);
impl IntoFlat<usize, Vec<usize>> for Expander {
fn into_flat(self, mut storage: Storage<'_, usize>) {
storage.extend(0..self.0);
}
}
#[test]
fn storage_extend() {
let mut data: FlatVec<Vec<usize>, usize, usize, 3> = FlatVec::new();
data.push(Expander(10));
assert_eq!(data.data_len(), 10);
assert_eq!(data.get(0), Some(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9][..]));
data.push(0..2);
assert_eq!(data.data_len(), 12);
assert_eq!(data.get(1), Some(&[0, 1][..]));
}
}