mod macros;
mod raw_vec;
mod raw_vec_inner;
mod soable;
use core::marker::PhantomData;
use raw_vec::RawSoAVec;
use raw_vec_inner::AllocError;
pub use soable::{SoATuple, SoAble};
#[cfg(feature = "derive")]
pub use soavec_derive::*;
#[repr(C)]
pub struct SoAVec<T: SoAble> {
buf: RawSoAVec<T>,
}
impl<T: SoAble> SoAVec<T> {
pub fn new() -> Self {
SoAVec {
buf: unsafe { RawSoAVec::with_capacity(0).unwrap_unchecked() },
}
}
pub fn with_capacity(cap: u32) -> Result<Self, AllocError> {
Ok(SoAVec {
buf: RawSoAVec::with_capacity(cap)?,
})
}
#[inline]
pub fn len(&self) -> u32 {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
#[inline]
pub fn capacity(&self) -> u32 {
self.buf.capacity()
}
pub fn reserve(&mut self, additional: u32) -> Result<(), AllocError> {
self.buf.reserve(additional)
}
pub fn push(&mut self, value: T) -> Result<(), AllocError> {
let len = self.len();
if len == self.capacity() {
self.buf.reserve(1)?;
}
unsafe {
T::TupleRepr::write(
self.buf.as_mut_ptr(),
T::into_tuple(value),
len,
self.capacity(),
)
};
unsafe { self.buf.set_len(self.len().unchecked_add(1)) };
Ok(())
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
let len = self.len();
if len == 0 {
None
} else {
unsafe {
self.buf.set_len(len - 1);
core::hint::assert_unchecked(self.len() < self.capacity());
Some(T::from_tuple(T::TupleRepr::read(
self.buf.as_ptr(),
self.len(),
self.capacity(),
)))
}
}
}
#[inline]
#[must_use]
pub fn get(&self, index: u32) -> Option<T::Ref<'_>> {
if self.len() <= index {
return None;
}
let ptrs = unsafe { T::TupleRepr::get_pointers(self.buf.as_ptr(), index, self.capacity()) };
Some(T::as_ref(PhantomData, ptrs))
}
#[inline]
#[must_use]
pub fn get_mut(&mut self, index: u32) -> Option<T::Mut<'_>> {
if self.len() <= index {
return None;
}
let ptrs =
unsafe { T::TupleRepr::get_pointers(self.buf.as_mut_ptr(), index, self.capacity()) };
Some(T::as_mut(PhantomData, ptrs))
}
pub fn as_slice(&self) -> T::Slice<'_> {
let ptrs = unsafe { T::TupleRepr::get_pointers(self.buf.as_ptr(), 0, self.capacity()) };
let len = self.len();
T::as_slice(PhantomData, ptrs, len)
}
pub fn as_mut_slice(&mut self) -> T::SliceMut<'_> {
let ptrs = unsafe { T::TupleRepr::get_pointers(self.buf.as_ptr(), 0, self.capacity()) };
let len = self.len();
T::as_mut_slice(PhantomData, ptrs, len)
}
pub fn retain_mut<F>(&mut self, mut f: F)
where
F: FnMut(T::Mut<'_>) -> bool,
{
let original_len = self.len();
if original_len == 0 {
return;
}
unsafe { self.buf.set_len(0) };
struct BackshiftOnDrop<'a, T: SoAble> {
v: &'a mut SoAVec<T>,
processed_len: u32,
deleted_cnt: u32,
original_len: u32,
}
impl<T: SoAble> Drop for BackshiftOnDrop<'_, T> {
fn drop(&mut self) {
if self.deleted_cnt > 0 {
let cap = self.v.buf.capacity();
unsafe {
T::TupleRepr::copy(
T::TupleRepr::get_pointers(
self.v.buf.as_ptr(),
self.processed_len,
cap,
),
T::TupleRepr::get_pointers(
self.v.buf.as_mut_ptr(),
self.processed_len - self.deleted_cnt,
cap,
),
self.original_len - self.processed_len,
);
}
}
unsafe {
self.v.buf.set_len(self.original_len - self.deleted_cnt);
}
}
}
let mut g = BackshiftOnDrop {
v: self,
processed_len: 0,
deleted_cnt: 0,
original_len,
};
fn process_loop<F, T: SoAble, const DELETED: bool>(
original_len: u32,
f: &mut F,
g: &mut BackshiftOnDrop<'_, T>,
) where
F: FnMut(T::Mut<'_>) -> bool,
{
while g.processed_len != original_len {
let cur_ptrs = unsafe {
T::TupleRepr::get_pointers(
g.v.buf.as_mut_ptr(),
g.processed_len,
g.v.capacity(),
)
};
let cur = T::as_mut(PhantomData, cur_ptrs);
if !f(cur) {
let cur_len = g.processed_len;
g.processed_len += 1;
g.deleted_cnt += 1;
if T::MUST_DROP_AS_SELF {
let ptr = g.v.buf.as_mut_ptr();
let cap = g.v.buf.capacity();
let _ = T::from_tuple(unsafe { T::TupleRepr::read(ptr, cur_len, cap) });
} else if const { core::mem::needs_drop::<T::TupleRepr>() } {
unsafe { T::TupleRepr::drop_in_place(cur_ptrs, 1) };
}
if DELETED {
continue;
} else {
break;
}
}
if DELETED {
unsafe {
let hole_slot = T::TupleRepr::get_pointers(
g.v.buf.as_mut_ptr(),
g.processed_len - g.deleted_cnt,
g.v.buf.capacity(),
);
T::TupleRepr::copy(cur_ptrs, hole_slot, 1);
}
}
g.processed_len += 1;
}
}
process_loop::<F, T, false>(original_len, &mut f, &mut g);
process_loop::<F, T, true>(original_len, &mut f, &mut g);
drop(g);
}
}
impl<T: SoAble> Drop for SoAVec<T> {
fn drop(&mut self) {
if T::MUST_DROP_AS_SELF {
let ptr = self.buf.as_mut_ptr();
let cap = self.buf.capacity();
let len = self.len();
for i in 0..len {
let _ = T::from_tuple(unsafe { T::TupleRepr::read(ptr, i, cap) });
}
} else if const { core::mem::needs_drop::<T::TupleRepr>() } {
let ptr = self.buf.as_mut_ptr();
let cap = self.buf.capacity();
let len = self.len();
unsafe { T::TupleRepr::drop_in_place(T::TupleRepr::get_pointers(ptr, 0, cap), len) };
}
}
}
impl<T: SoAble> core::default::Default for SoAVec<T> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl<T: SoAble> core::fmt::Debug for SoAVec<T>
where
for<'a> T::Slice<'a>: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(&self.as_slice(), f)
}
}
impl<T: SoAble> AsRef<SoAVec<T>> for SoAVec<T> {
#[inline(always)]
fn as_ref(&self) -> &SoAVec<T> {
self
}
}
impl<T: SoAble> AsMut<SoAVec<T>> for SoAVec<T> {
#[inline(always)]
fn as_mut(&mut self) -> &mut SoAVec<T> {
self
}
}
#[cfg(test)]
mod tests {
use core::marker::PhantomData;
use soavec_derive::SoAble;
use crate as soavec;
use crate::{SoATuple, SoAVec, SoAble};
#[test]
fn basic_usage() {
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, SoAble)]
struct Foo {
a: u64,
b: u32,
}
const _ARRAY: [Foo; 16] = [Foo { a: 0, b: 1 }; 16];
const _SOA_ARRAY: ([u64; 16], [u32; 16]) = ([0; 16], [1; 16]);
let mut foo = SoAVec::<Foo>::with_capacity(16).unwrap();
foo.push(Foo { a: 0, b: 2 }).unwrap();
let first = foo.get(0).unwrap();
assert_eq!(first.a, &0);
assert_eq!(first.b, &2);
let first = foo.get_mut(0).unwrap();
*first.a = 52;
*first.b = 66;
assert_eq!(first.a, &52);
assert_eq!(first.b, &66);
let first = foo.get(0).unwrap();
assert_eq!(first.a, &52);
assert_eq!(first.b, &66);
foo.reserve(32).unwrap();
let first = foo.get(0).unwrap();
assert_eq!(first.a, &52);
assert_eq!(first.b, &66);
foo.push(Foo { a: 4, b: 8 }).unwrap();
let FooSlice {
a: a_slice,
b: b_slice,
} = foo.as_slice();
assert_eq!(a_slice.len(), b_slice.len());
assert_eq!(a_slice.len(), 2);
assert_eq!(a_slice, &[52, 4]);
assert_eq!(b_slice, &[66, 8]);
assert_eq!(foo.pop(), Some(Foo { a: 4, b: 8 }));
}
#[test]
fn basic_usage_with_lifetime() {
#[repr(C)]
#[derive(Debug, Clone, Copy, SoAble)]
struct Foo<'a> {
a: &'a u64,
b: &'a u32,
}
let mut foo = SoAVec::<Foo>::with_capacity(16).unwrap();
foo.push(Foo { a: &0, b: &2 }).unwrap();
let first = foo.get(0).unwrap();
assert_eq!(first.a, &&0);
assert_eq!(first.b, &&2);
let first = foo.get_mut(0).unwrap();
*first.a = &52;
*first.b = &66;
assert_eq!(first.a, &&52);
assert_eq!(first.b, &&66);
let first = foo.get(0).unwrap();
assert_eq!(first.a, &&52);
assert_eq!(first.b, &&66);
foo.reserve(32).unwrap();
let first = foo.get(0).unwrap();
assert_eq!(first.a, &&52);
assert_eq!(first.b, &&66);
foo.push(Foo { a: &4, b: &8 }).unwrap();
let FooSlice {
a: a_slice,
b: b_slice,
} = foo.as_slice();
assert_eq!(a_slice.len(), b_slice.len());
assert_eq!(a_slice.len(), 2);
assert_eq!(a_slice, &[&52, &4]);
assert_eq!(b_slice, &[&66, &8]);
}
#[test]
fn more_basic_usage() {
#[repr(C)]
#[derive(Debug, Clone, Copy, SoAble)]
struct Bar {
a: u64,
b: u32,
c: u8,
}
let mut bar = SoAVec::<Bar>::with_capacity(16).unwrap();
bar.reserve(32).unwrap();
bar.push(Bar { a: 0, b: 2, c: 255 }).unwrap();
let first = bar.get(0).unwrap();
assert_eq!(first.a, &0);
assert_eq!(first.b, &2);
assert_eq!(first.c, &255);
}
#[test]
fn basic_usage_with_bad_alignment() {
#[repr(C)]
#[derive(Debug, Clone, Copy, SoAble)]
struct Foo {
b: u32,
a: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, SoAble)]
struct Bar {
c: u8,
b: u32,
a: u64,
}
let mut foo = SoAVec::<Foo>::with_capacity(5).unwrap();
foo.reserve(9).unwrap();
foo.push(Foo { b: 2, a: 0 }).unwrap();
let first = foo.get(0).unwrap();
assert_eq!(first.b, &2);
assert_eq!(first.a, &0);
let mut bar = SoAVec::<Bar>::with_capacity(7).unwrap();
bar.reserve(11).unwrap();
bar.push(Bar { c: 255, b: 2, a: 0 }).unwrap();
let first = bar.get(0).unwrap();
assert_eq!(first.c, &255);
assert_eq!(first.b, &2);
assert_eq!(first.a, &0);
}
#[test]
fn basic_usage_with_zst() {
#[repr(C)]
#[derive(Debug, Clone, Copy, SoAble)]
struct Foo {
b: (),
a: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, SoAble)]
struct Bar {
c: u8,
b: (),
a: u64,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, SoAble)]
struct Baz {
c: (),
b: (),
a: (),
}
let mut foo = SoAVec::<Foo>::with_capacity(5).unwrap();
foo.reserve(9).unwrap();
foo.push(Foo { a: 2, b: () }).unwrap();
let first = foo.get(0).unwrap();
assert_eq!(first.b, &());
assert_eq!(first.a, &2);
let mut bar = SoAVec::<Bar>::with_capacity(7).unwrap();
bar.reserve(11).unwrap();
bar.push(Bar {
c: 255,
b: (),
a: 0,
})
.unwrap();
let first = bar.get(0).unwrap();
assert_eq!(first.c, &255);
assert_eq!(first.b, &());
assert_eq!(first.a, &0);
let mut baz = SoAVec::<Baz>::with_capacity(7).unwrap();
baz.reserve(11).unwrap();
baz.push(Baz {
a: (),
b: (),
c: (),
})
.unwrap();
let first = baz.get(0).unwrap();
assert_eq!(first.a, &());
assert_eq!(first.b, &());
assert_eq!(first.c, &());
}
#[test]
fn droppable_types() {
#[repr(C)]
#[derive(Debug, Clone, SoAble)]
struct Foo {
a: Vec<u64>,
b: Box<u32>,
}
let mut foo = SoAVec::<Foo>::with_capacity(16).unwrap();
foo.push(Foo {
a: vec![0],
b: Box::new(2),
})
.unwrap();
let first = foo.get(0).unwrap();
assert_eq!(first.a, &[0]);
assert_eq!(**first.b, 2);
let first = foo.get_mut(0).unwrap();
first.a.push(52);
*first.b = Box::new(66u32);
assert_eq!(first.a, &[0, 52]);
assert_eq!(**first.b, 66u32);
let first = foo.get(0).unwrap();
assert_eq!(first.a, &[0, 52]);
assert_eq!(**first.b, 66u32);
foo.reserve(32).unwrap();
let first = foo.get(0).unwrap();
assert_eq!(first.a, &[0, 52]);
assert_eq!(**first.b, 66u32);
foo.push(Foo {
a: vec![4],
b: Box::new(8),
})
.unwrap();
let FooSlice {
a: a_slice,
b: b_slice,
} = foo.as_slice();
assert_eq!(a_slice.len(), b_slice.len());
assert_eq!(a_slice.len(), 2);
assert_eq!(a_slice, &[vec![0, 52], vec![4]]);
assert_eq!(b_slice, &[Box::new(66), Box::new(8)]);
}
#[test]
fn must_drop_as_self_type() {
#[repr(C)]
struct LoudDrop {
a: (),
b: (),
}
static mut DROP_COUNT: usize = 0;
impl Drop for LoudDrop {
fn drop(&mut self) {
println!("LoudDrop");
unsafe {
DROP_COUNT += 1;
}
}
}
unsafe impl SoAble for LoudDrop {
type TupleRepr = ((), ());
const MUST_DROP_AS_SELF: bool = true;
type Ref<'a>
= (&'a (), &'a ())
where
Self: 'a;
type Mut<'a>
= (&'a mut (), &'a mut ())
where
Self: 'a;
type Slice<'a>
= (&'a [()], &'a [()])
where
Self: 'a;
type SliceMut<'a>
= (&'a mut [()], &'a mut [()])
where
Self: 'a;
fn into_tuple(value: Self) -> Self::TupleRepr {
core::mem::forget(value);
((), ())
}
fn from_tuple(_value: Self::TupleRepr) -> Self {
Self { a: (), b: () }
}
fn as_ref<'a>(
_: PhantomData<&'a Self>,
value: <Self::TupleRepr as SoATuple>::Pointers,
) -> Self::Ref<'a> {
unsafe { (value.0.as_ref(), value.1.as_ref()) }
}
fn as_mut<'a>(
_: PhantomData<&'a mut Self>,
mut value: <Self::TupleRepr as SoATuple>::Pointers,
) -> Self::Mut<'a> {
unsafe { (value.0.as_mut(), value.1.as_mut()) }
}
fn as_slice<'a>(
_: PhantomData<&'a Self>,
value: <Self::TupleRepr as SoATuple>::Pointers,
len: u32,
) -> Self::Slice<'a> {
unsafe {
(
core::slice::from_raw_parts(value.0.as_ptr(), len as usize),
core::slice::from_raw_parts(value.1.as_ptr(), len as usize),
)
}
}
fn as_mut_slice<'a>(
_: PhantomData<&'a mut Self>,
value: <Self::TupleRepr as SoATuple>::Pointers,
len: u32,
) -> Self::SliceMut<'a> {
unsafe {
(
core::slice::from_raw_parts_mut(value.0.as_ptr(), len as usize),
core::slice::from_raw_parts_mut(value.1.as_ptr(), len as usize),
)
}
}
}
let mut foo = SoAVec::<LoudDrop>::with_capacity(16).unwrap();
foo.push(LoudDrop { a: (), b: () }).unwrap();
let _first = foo.get(0).unwrap();
drop(foo);
assert_eq!(
unsafe { DROP_COUNT },
1,
"should have dropped a single LoudDrop item"
);
}
}