use core::alloc::Layout;
use core::marker::PhantomData;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ptr::{self, NonNull};
use bun_alloc::core_alloc::{Allocator, Global};
use bun_alloc::AllocError;
#[macro_export]
macro_rules! multi_array_columns {
(
$vis:vis trait $trait:ident for $elem:ty {
$( $field:ident : $ty:ty ),* $(,)?
}
) => {
$crate::multi_array_columns! {
@emit $vis $trait [] [] $elem { $( $field : $ty, )* }
}
};
(
$vis:vis trait $trait:ident [ $($lt:lifetime),+ ] for $elem:ty {
$( $field:ident : $ty:ty ),* $(,)?
}
) => {
$crate::multi_array_columns! {
@emit $vis $trait [$($lt),+] [$($lt),+] $elem { $( $field : $ty, )* }
}
};
(
$vis:vis trait $trait:ident [ $param:ident : $($bound:tt)+ ] for $elem:ty {
$( $field:ident : $ty:ty ),* $(,)?
}
) => {
$crate::multi_array_columns! {
@emit $vis $trait [$param: $($bound)+] [$param] $elem { $( $field : $ty, )* }
}
};
(@emit $vis:vis $trait:ident [$($decl:tt)*] [$($use:tt)*] $elem:ty {
$( $field:ident : $ty:ty, )*
}) => {
$crate::__mal_paste! {
#[allow(dead_code, non_snake_case)]
$vis struct [<$trait Mut>] <'__mal, $($decl)*> {
$( pub $field: &'__mal mut [$ty], )*
#[doc(hidden)]
pub __mal: ::core::marker::PhantomData<&'__mal mut $elem>,
}
#[allow(dead_code, non_snake_case)]
$vis struct [<$trait Raw>] <$($decl)*> {
$( pub $field: *mut [$ty], )*
#[doc(hidden)]
pub __mal: ::core::marker::PhantomData<*mut $elem>,
}
#[allow(dead_code, non_snake_case)]
impl <$($decl)*> ::core::marker::Copy for [<$trait Raw>] <$($use)*> {}
#[allow(dead_code, non_snake_case)]
impl <$($decl)*> ::core::clone::Clone for [<$trait Raw>] <$($use)*> {
#[inline] fn clone(&self) -> Self { *self }
}
#[allow(dead_code, non_snake_case)]
$vis trait $trait <$($decl)*> {
$( $crate::__mal_column_sig!($field : $ty); )*
fn split_mut(&mut self) -> [<$trait Mut>]<'_, $($use)*>;
fn split_raw(&self) -> [<$trait Raw>]<$($use)*>;
}
#[allow(dead_code, non_snake_case)]
impl <$($decl)*> $trait <$($use)*> for $crate::MultiArrayList<$elem> {
$( $crate::__mal_column_impl!($field : $ty); )*
$crate::__mal_split_mut_impl!([<$trait Mut>] [$($use)*] { $( $field : $ty, )* });
$crate::__mal_split_raw_impl!([<$trait Raw>] [$($use)*] { $( $field : $ty, )* });
}
#[allow(dead_code, non_snake_case)]
impl <$($decl)*> $trait <$($use)*> for $crate::multi_array_list::Slice<$elem> {
$( $crate::__mal_column_impl!($field : $ty); )*
$crate::__mal_split_mut_impl!([<$trait Mut>] [$($use)*] { $( $field : $ty, )* });
$crate::__mal_split_raw_impl!([<$trait Raw>] [$($use)*] { $( $field : $ty, )* });
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __mal_column_sig {
($field:ident : $ty:ty) => {
$crate::__mal_paste! {
fn [<items_ $field>](&self) -> &[$ty];
fn [<items_ $field _mut>](&mut self) -> &mut [$ty];
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __mal_split_mut_impl {
($struct:ident [$($use:tt)*] { $( $field:ident : $ty:ty, )* }) => {
#[inline]
fn split_mut(&mut self) -> $struct<'_, $($use)*> {
let __len = self.len();
unsafe {
$struct {
$( $field: ::core::slice::from_raw_parts_mut(
self.items_raw_named::<$ty>(::core::stringify!($field)),
__len,
), )*
__mal: ::core::marker::PhantomData,
}
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __mal_split_raw_impl {
($struct:ident [$($use:tt)*] { $( $field:ident : $ty:ty, )* }) => {
#[inline]
fn split_raw(&self) -> $struct<$($use)*> {
let __len = self.len();
$struct {
$( $field: ::core::ptr::slice_from_raw_parts_mut(
self.items_raw_named::<$ty>(::core::stringify!($field)),
__len,
), )*
__mal: ::core::marker::PhantomData,
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __mal_column_impl {
($field:ident : $ty:ty) => {
$crate::__mal_paste! {
#[inline]
fn [<items_ $field>](&self) -> &[$ty] {
self.items_named::<$ty>(::core::stringify!($field))
}
#[inline]
fn [<items_ $field _mut>](&mut self) -> &mut [$ty] {
self.items_named_mut::<$ty>(::core::stringify!($field))
}
}
};
}
pub(crate) const MAX_FIELDS: usize = 32;
pub struct SoaFieldInfo {
pub name: &'static str,
pub size: usize,
pub offset: usize,
}
pub trait SoaRow {
const SOA_FIELDS: &'static [SoaFieldInfo];
}
#[inline(always)]
pub(crate) const fn field_count<T: SoaRow>() -> usize {
T::SOA_FIELDS.len()
}
const fn align_sort_key(size: usize, struct_align: usize) -> usize {
if size == 0 {
return 1;
}
let pow2 = size & size.wrapping_neg();
if pow2 < struct_align {
pow2
} else {
struct_align
}
}
#[derive(Clone, Copy)]
struct FieldMeta {
size: usize,
offset: usize,
align: usize,
}
const ZERO_META: FieldMeta = FieldMeta {
size: 0,
offset: 0,
align: 1,
};
struct Reflected<T: SoaRow>(PhantomData<T>);
impl<T: SoaRow> Reflected<T> {
const COUNT: usize = field_count::<T>();
const ALIGN: usize = core::mem::align_of::<T>();
const DANGLING: NonNull<u8> = NonNull::<T>::dangling().cast::<u8>();
const META: [FieldMeta; MAX_FIELDS] = {
let fields = T::SOA_FIELDS;
let n = fields.len();
assert!(
n <= MAX_FIELDS,
"MultiArrayList: too many fields (raise MAX_FIELDS)",
);
let mut out = [ZERO_META; MAX_FIELDS];
let struct_align = core::mem::align_of::<T>();
let mut i = 0;
while i < n {
let size = fields[i].size;
let align = align_sort_key(size, struct_align);
out[i] = FieldMeta {
size,
offset: fields[i].offset,
align,
};
i += 1;
}
out
};
const SIZES: ([usize; MAX_FIELDS], [usize; MAX_FIELDS]) = {
let n = Self::COUNT;
let mut idx = [0usize; MAX_FIELDS];
let mut k = 0;
while k < n {
idx[k] = k;
k += 1;
}
let mut i = 0;
while i < n {
let mut j = 0;
while j + 1 + i < n {
if Self::META[idx[j]].align < Self::META[idx[j + 1]].align {
let tmp = idx[j];
idx[j] = idx[j + 1];
idx[j + 1] = tmp;
}
j += 1;
}
i += 1;
}
let mut bytes = [0usize; MAX_FIELDS];
let mut k = 0;
while k < n {
bytes[k] = Self::META[idx[k]].size;
k += 1;
}
(bytes, idx)
};
const ELEM_BYTES: usize = {
let mut sum = 0;
let mut i = 0;
while i < Self::COUNT {
sum += Self::META[i].size;
i += 1;
}
sum
};
const COLUMN_OFFSET_PER_CAP: [usize; MAX_FIELDS] = {
let n = Self::COUNT;
let (bytes, fields) = Self::SIZES;
let mut out = [0usize; MAX_FIELDS];
let mut running = 0usize;
let mut k = 0;
while k < n {
out[fields[k]] = running;
running += bytes[k];
k += 1;
}
out
};
#[cfg(test)]
#[cfg(test)]
fn index_of(name: &str) -> usize {
let fields = T::SOA_FIELDS;
let mut i = 0;
while i < fields.len() {
if fields[i].name == name {
return i;
}
i += 1;
}
panic!("MultiArrayList: no such field {:?}", name);
}
fn check_named<F>(name: &str) -> usize {
let fields = T::SOA_FIELDS;
let mut i = 0;
while i < fields.len() {
if fields[i].name == name {
assert!(
Self::META[i].size == core::mem::size_of::<F>(),
"MultiArrayList: column type does not match field type",
);
return i;
}
i += 1;
}
panic!("MultiArrayList: no such field {:?}", name);
}
}
#[inline(always)]
fn column_base<T: SoaRow>(bytes: NonNull<u8>, cap: usize, fi: usize) -> NonNull<u8> {
debug_assert!(fi < Reflected::<T>::COUNT);
let off = Reflected::<T>::COLUMN_OFFSET_PER_CAP[fi] * cap;
unsafe { bytes.add(off) }
}
struct Col<'a, F> {
ptr: NonNull<F>,
len: usize,
_marker: PhantomData<&'a [F]>,
}
impl<'a, F> Col<'a, F> {
#[inline(always)]
fn new(ptr: NonNull<F>, len: usize) -> Self {
Self {
ptr,
len,
_marker: PhantomData,
}
}
#[inline(always)]
fn as_slice(self) -> &'a [F] {
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
}
struct ColMut<'a, F> {
ptr: NonNull<F>,
len: usize,
_marker: PhantomData<&'a mut [F]>,
}
impl<'a, F> ColMut<'a, F> {
#[inline(always)]
fn new(ptr: NonNull<F>, len: usize) -> Self {
Self {
ptr,
len,
_marker: PhantomData,
}
}
#[inline(always)]
fn as_mut_slice(self) -> &'a mut [F] {
unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
}
pub trait SortContext {
fn less_than(&self, a_index: usize, b_index: usize) -> bool;
}
pub struct MultiArrayList<T: SoaRow, A: Allocator = Global> {
bytes: NonNull<u8>,
len: usize,
capacity: usize,
alloc: A,
_marker: PhantomData<T>,
}
unsafe impl<T: SoaRow + Send, A: Allocator + Send> Send for MultiArrayList<T, A> {}
pub struct Slice<T: SoaRow> {
ptrs: [NonNull<u8>; MAX_FIELDS],
len: usize,
capacity: usize,
_marker: PhantomData<T>,
}
impl<T: SoaRow> Clone for Slice<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: SoaRow> Copy for Slice<T> {}
impl<T: SoaRow> Slice<T> {
pub const EMPTY: Self = Self {
ptrs: [Reflected::<T>::DANGLING; MAX_FIELDS],
len: 0,
capacity: 0,
_marker: PhantomData,
};
#[inline]
fn from_raw(bytes: NonNull<u8>, len: usize, cap: usize) -> Self {
let mut ptrs = [Reflected::<T>::DANGLING; MAX_FIELDS];
let mut fi = 0;
while fi < Reflected::<T>::COUNT {
ptrs[fi] = column_base::<T>(bytes, cap, fi);
fi += 1;
}
Self {
ptrs,
len,
capacity: cap,
_marker: PhantomData,
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline(always)]
fn col_ptr<F>(&self, fi: usize) -> NonNull<F> {
if core::mem::size_of::<F>() == 0 {
return NonNull::<F>::dangling();
}
self.ptrs[fi].cast::<F>()
}
#[inline]
pub fn items_named<F>(&self, name: &str) -> &[F] {
let fi = Reflected::<T>::check_named::<F>(name);
Col::new(self.col_ptr::<F>(fi), self.len).as_slice()
}
#[inline]
pub fn items_named_mut<F>(&mut self, name: &str) -> &mut [F] {
let fi = Reflected::<T>::check_named::<F>(name);
ColMut::new(self.col_ptr::<F>(fi), self.len).as_mut_slice()
}
#[inline]
pub fn items_raw_named<F>(&self, name: &str) -> *mut F {
let fi = Reflected::<T>::check_named::<F>(name);
self.col_ptr::<F>(fi).as_ptr()
}
#[inline(always)]
fn column_uninit(&self, fi: usize) -> &[MaybeUninit<u8>] {
let sz = Reflected::<T>::META[fi].size;
Col::new(self.ptrs[fi].cast::<MaybeUninit<u8>>(), self.len * sz).as_slice()
}
#[inline(always)]
fn column_uninit_mut(&mut self, fi: usize) -> &mut [MaybeUninit<u8>] {
let sz = Reflected::<T>::META[fi].size;
ColMut::new(self.ptrs[fi].cast::<MaybeUninit<u8>>(), self.len * sz).as_mut_slice()
}
#[inline]
pub unsafe fn column_bytes_mut(&mut self, field_index: usize) -> &mut [u8] {
debug_assert!(field_index < Reflected::<T>::COUNT);
let sz = Reflected::<T>::META[field_index].size;
ColMut::new(self.ptrs[field_index].cast::<u8>(), self.len * sz).as_mut_slice()
}
#[inline]
pub fn field_size(field_index: usize) -> usize {
Reflected::<T>::META[field_index].size
}
pub fn set(&mut self, index: usize, elem: T) {
assert!(
index < self.len,
"MultiArrayList::Slice::set: index out of bounds"
);
self.scatter(index, elem);
}
pub fn get(&self, index: usize) -> ManuallyDrop<T> {
assert!(
index < self.len,
"MultiArrayList::Slice::get: index out of bounds"
);
ManuallyDrop::new(self.gather(index))
}
pub fn to_multi_array_list(self) -> MultiArrayList<T> {
if Reflected::<T>::COUNT == 0 || self.capacity == 0 {
return MultiArrayList::default();
}
let base = self.ptrs[Reflected::<T>::SIZES.1[0]];
MultiArrayList {
bytes: base,
len: self.len,
capacity: self.capacity,
alloc: Global,
_marker: PhantomData,
}
}
#[inline]
fn copy_rows_within(&mut self, src: usize, dst: usize, n: usize) {
if n == 0 {
return;
}
debug_assert!(src.max(dst) + n <= self.len);
for fi in 0..Reflected::<T>::COUNT {
let sz = Reflected::<T>::META[fi].size;
if sz == 0 {
continue;
}
self.column_uninit_mut(fi)
.copy_within(src * sz..(src + n) * sz, dst * sz);
}
}
#[inline]
fn swap_rows(&mut self, a: usize, b: usize) {
if a == b {
return;
}
let (lo, hi) = if a < b { (a, b) } else { (b, a) };
debug_assert!(hi < self.len);
for fi in 0..Reflected::<T>::COUNT {
let sz = Reflected::<T>::META[fi].size;
if sz == 0 {
continue;
}
let col = self.column_uninit_mut(fi);
let (l, r) = col.split_at_mut(hi * sz);
l[lo * sz..(lo + 1) * sz].swap_with_slice(&mut r[..sz]);
}
}
#[inline]
fn copy_rows_from(&mut self, dst_off: usize, src: &Slice<T>, n: usize) {
if n == 0 {
return;
}
debug_assert!(n <= src.len);
debug_assert!(dst_off + n <= self.len);
for fi in 0..Reflected::<T>::COUNT {
let sz = Reflected::<T>::META[fi].size;
if sz == 0 {
continue;
}
debug_assert_ne!(
self.ptrs[fi], src.ptrs[fi],
"copy_rows_from: aliased columns"
);
let dst = &mut self.column_uninit_mut(fi)[dst_off * sz..(dst_off + n) * sz];
dst.copy_from_slice(&src.column_uninit(fi)[..n * sz]);
}
}
#[inline]
fn scatter(&mut self, index: usize, elem: T) {
debug_assert!(index < self.len);
let elem = ManuallyDrop::new(elem);
let src = (&raw const *elem).cast::<u8>();
unsafe {
let mut i = 0;
while i < Reflected::<T>::COUNT {
let m = Reflected::<T>::META[i];
if m.size != 0 {
ptr::copy_nonoverlapping(
src.add(m.offset),
self.ptrs[i].as_ptr().add(index * m.size),
m.size,
);
}
i += 1;
}
}
}
#[inline]
fn gather(&self, index: usize) -> T {
debug_assert!(index < self.len);
let mut out = MaybeUninit::<T>::uninit();
let dst = out.as_mut_ptr().cast::<u8>();
unsafe {
let mut i = 0;
while i < Reflected::<T>::COUNT {
let m = Reflected::<T>::META[i];
if m.size != 0 {
ptr::copy_nonoverlapping(
self.ptrs[i].as_ptr().add(index * m.size),
dst.add(m.offset),
m.size,
);
}
i += 1;
}
out.assume_init()
}
}
pub fn deinit_owned(self) {
drop(self.to_multi_array_list());
}
}
impl<T: SoaRow, A: Allocator + Default> Default for MultiArrayList<T, A> {
fn default() -> Self {
Self::new_in(A::default())
}
}
impl<T: SoaRow> MultiArrayList<T, Global> {
pub const EMPTY: Self = Self {
bytes: Reflected::<T>::DANGLING,
len: 0,
capacity: 0,
alloc: Global,
_marker: PhantomData,
};
}
impl<T: SoaRow, A: Allocator> MultiArrayList<T, A> {
#[inline]
pub const fn new_in(alloc: A) -> Self {
Self {
bytes: Reflected::<T>::DANGLING,
len: 0,
capacity: 0,
alloc,
_marker: PhantomData,
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn to_owned_slice(&mut self) -> Slice<T>
where
A: Default,
{
let old = ManuallyDrop::new(core::mem::replace(self, Self::new_in(A::default())));
old.slice()
}
#[inline]
pub fn slice(&self) -> Slice<T> {
Slice::from_raw(self.bytes, self.len, self.capacity)
}
#[inline(always)]
fn col_ptr<F>(&self, fi: usize) -> NonNull<F> {
if core::mem::size_of::<F>() == 0 {
return NonNull::<F>::dangling();
}
column_base::<T>(self.bytes, self.capacity, fi).cast::<F>()
}
#[inline]
pub fn items_named<F>(&self, name: &str) -> &[F] {
let fi = Reflected::<T>::check_named::<F>(name);
Col::new(self.col_ptr::<F>(fi), self.len).as_slice()
}
#[inline]
pub fn items_named_mut<F>(&mut self, name: &str) -> &mut [F] {
let fi = Reflected::<T>::check_named::<F>(name);
ColMut::new(self.col_ptr::<F>(fi), self.len).as_mut_slice()
}
#[inline]
pub fn items_raw_named<F>(&self, name: &str) -> *mut F {
let fi = Reflected::<T>::check_named::<F>(name);
self.col_ptr::<F>(fi).as_ptr()
}
pub fn set(&mut self, index: usize, elem: T) {
let mut s = self.slice();
s.set(index, elem);
}
pub fn get(&self, index: usize) -> ManuallyDrop<T> {
self.slice().get(index)
}
pub fn push(&mut self, elem: T) -> Result<(), AllocError> {
self.ensure_unused_capacity(1)?;
self.append_assume_capacity(elem);
Ok(())
}
#[inline]
pub fn append(&mut self, elem: T) -> Result<(), AllocError> {
self.push(elem)
}
pub fn append_assume_capacity(&mut self, elem: T) {
debug_assert!(self.len < self.capacity);
self.len += 1;
let mut s = self.slice();
s.set(self.len - 1, elem);
}
pub fn add_one(&mut self) -> Result<usize, AllocError> {
self.ensure_unused_capacity(1)?;
Ok(self.add_one_assume_capacity())
}
pub fn add_one_assume_capacity(&mut self) -> usize {
debug_assert!(self.len < self.capacity);
let index = self.len;
self.len += 1;
index
}
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
let val = self.get(self.len - 1);
self.len -= 1;
Some(ManuallyDrop::into_inner(val))
}
pub fn insert(&mut self, index: usize, elem: T) -> Result<(), AllocError> {
self.ensure_unused_capacity(1)?;
self.insert_assume_capacity(index, elem);
Ok(())
}
pub fn insert_assume_capacity(&mut self, index: usize, elem: T) {
debug_assert!(self.len < self.capacity);
debug_assert!(index <= self.len);
let tail = self.len - index;
self.len += 1;
let mut s = self.slice();
s.copy_rows_within(index, index + 1, tail);
s.scatter(index, elem);
}
pub fn append_list_assume_capacity(&mut self, other: &Self) {
let offset = self.len;
self.len += other.len;
let mut s = self.slice();
s.copy_rows_from(offset, &other.slice(), other.len);
}
pub fn swap_remove(&mut self, index: usize) {
assert!(
index < self.len,
"MultiArrayList::swap_remove: index out of bounds"
);
let last = self.len - 1;
let mut s = self.slice();
s.copy_rows_within(last, index, 1);
self.len -= 1;
}
pub fn ordered_remove(&mut self, index: usize) {
assert!(
index < self.len,
"MultiArrayList::ordered_remove: index out of bounds"
);
let tail = self.len - 1 - index;
let mut s = self.slice();
s.copy_rows_within(index + 1, index, tail);
self.len -= 1;
}
pub fn resize(&mut self, new_len: usize) -> Result<(), AllocError> {
self.ensure_total_capacity(new_len)?;
self.len = new_len;
Ok(())
}
pub fn shrink_and_free(&mut self, new_len: usize) {
if new_len == 0 {
return self.clear_and_free();
}
debug_assert!(new_len <= self.capacity);
debug_assert!(new_len <= self.len);
let new_bytes = match aligned_alloc::<T, _>(&self.alloc, layout_for::<T>(new_len)) {
Ok(p) => p,
Err(_) => {
self.len = new_len;
return;
}
};
self.len = new_len;
let mut dst = Slice::<T>::from_raw(new_bytes, new_len, new_len);
dst.copy_rows_from(0, &self.slice(), new_len);
self.free_allocated_bytes();
self.bytes = new_bytes;
self.capacity = new_len;
}
pub fn clear_and_free(&mut self) {
self.free_allocated_bytes();
self.bytes = Reflected::<T>::DANGLING;
self.len = 0;
self.capacity = 0;
}
pub fn drop_elements(&mut self) {
if core::mem::needs_drop::<T>() && self.len != 0 {
let s = self.slice();
for i in 0..self.len {
drop(s.gather(i));
}
}
self.len = 0;
}
pub fn shrink_retaining_capacity(&mut self, new_len: usize) {
self.len = new_len;
}
pub fn clear_retaining_capacity(&mut self) {
self.len = 0;
}
pub fn ensure_total_capacity(&mut self, new_capacity: usize) -> Result<(), AllocError> {
if self.capacity >= new_capacity {
return Ok(());
}
self.set_capacity(grow_capacity::<T>(self.capacity, new_capacity))
}
pub fn ensure_unused_capacity(&mut self, additional_count: usize) -> Result<(), AllocError> {
self.ensure_total_capacity(self.len + additional_count)
}
pub fn set_capacity(&mut self, new_capacity: usize) -> Result<(), AllocError> {
debug_assert!(new_capacity >= self.len);
let new_bytes = aligned_alloc::<T, _>(&self.alloc, layout_for::<T>(new_capacity))?;
if self.len != 0 {
let mut dst = Slice::<T>::from_raw(new_bytes, self.len, new_capacity);
dst.copy_rows_from(0, &self.slice(), self.len);
}
self.free_allocated_bytes();
self.bytes = new_bytes;
self.capacity = new_capacity;
Ok(())
}
pub fn clone(&self) -> Result<Self, AllocError>
where
A: Clone,
{
let mut result = Self::new_in(self.alloc.clone());
result.ensure_total_capacity(self.len)?;
result.len = self.len;
let mut dst = result.slice();
dst.copy_rows_from(0, &self.slice(), self.len);
Ok(result)
}
fn sort_internal<C: SortContext, const STABLE: bool>(&mut self, a: usize, b: usize, ctx: &C) {
let mut slice = self.slice();
let swap = |ai: usize, bi: usize| slice.swap_rows(ai, bi);
let less = |ai: usize, bi: usize| ctx.less_than(ai, bi);
match STABLE {
true => bun_collections_sort_context(a, b, less, swap),
false => bun_collections_sort_unstable_context(a, b, less, swap),
}
}
pub fn sort<C: SortContext>(&mut self, ctx: &C) {
self.sort_internal::<C, true>(0, self.len, ctx);
}
pub fn sort_span<C: SortContext>(&mut self, a: usize, b: usize, ctx: &C) {
self.sort_internal::<C, true>(a, b, ctx);
}
pub fn sort_unstable<C: SortContext>(&mut self, ctx: &C) {
self.sort_internal::<C, false>(0, self.len, ctx);
}
pub fn sort_span_unstable<C: SortContext>(&mut self, a: usize, b: usize, ctx: &C) {
self.sort_internal::<C, false>(a, b, ctx);
}
pub fn capacity_in_bytes(capacity: usize) -> usize {
Reflected::<T>::ELEM_BYTES * capacity
}
pub fn memory_cost(&self) -> usize {
Self::capacity_in_bytes(self.capacity)
}
pub fn zero(&mut self) {
let n = Self::capacity_in_bytes(self.capacity);
if n != 0 {
unsafe { ptr::write_bytes(self.bytes.as_ptr(), 0, n) };
}
}
fn free_allocated_bytes(&mut self) {
if let Some(layout) = layout_for::<T>(self.capacity) {
unsafe { self.alloc.deallocate(self.bytes, layout) };
self.capacity = 0;
}
}
#[inline]
pub unsafe fn set_len(&mut self, new_len: usize) {
debug_assert!(new_len <= self.capacity);
self.len = new_len;
}
}
impl<T: SoaRow, A: Allocator> Drop for MultiArrayList<T, A> {
fn drop(&mut self) {
self.free_allocated_bytes();
}
}
const CACHE_LINE: usize = 128;
const fn init_capacity<T: SoaRow>() -> usize {
let mut max = 1usize;
let mut i = 0;
while i < Reflected::<T>::COUNT {
if Reflected::<T>::META[i].size > max {
max = Reflected::<T>::META[i].size;
}
i += 1;
}
let cl = CACHE_LINE / max;
if cl > 1 { cl } else { 1 }
}
fn grow_capacity<T: SoaRow>(current: usize, minimum: usize) -> usize {
let init = init_capacity::<T>();
let mut new = current;
loop {
new = new.saturating_add(new / 2 + init);
if new >= minimum {
return new;
}
}
}
#[inline]
fn layout_for<T: SoaRow>(capacity: usize) -> Option<Layout> {
let n = Reflected::<T>::ELEM_BYTES * capacity;
if n == 0 {
return None;
}
Some(Layout::from_size_align(n, Reflected::<T>::ALIGN).expect("MultiArrayList layout overflow"))
}
fn aligned_alloc<T: SoaRow, A: Allocator>(
alloc: &A,
layout: Option<Layout>,
) -> Result<NonNull<u8>, AllocError> {
let Some(layout) = layout else {
return Ok(Reflected::<T>::DANGLING);
};
alloc
.allocate(layout)
.map(|p| p.cast::<u8>())
.map_err(|_| AllocError)
}
fn bun_collections_sort_context(
a: usize,
b: usize,
less: impl Fn(usize, usize) -> bool,
mut swap: impl FnMut(usize, usize),
) {
debug_assert!(a <= b);
if a >= b {
return;
}
let mut i = a + 1;
while i < b {
let mut j = i;
while j > a && less(j, j - 1) {
swap(j, j - 1);
j -= 1;
}
i += 1;
}
}
fn bun_collections_sort_unstable_context(
a: usize,
b: usize,
less: impl Fn(usize, usize) -> bool,
mut swap: impl FnMut(usize, usize),
) {
debug_assert!(a <= b);
if b - a < 2 {
return;
}
let mut i = a + (b - a) / 2;
while i > a {
i -= 1;
sift_down(a, i, b, &less, &mut swap);
}
i = b;
while i > a {
i -= 1;
if i == a {
break;
}
swap(a, i);
sift_down(a, a, i, &less, &mut swap);
}
}
fn sift_down(
a: usize,
target: usize,
b: usize,
less: &impl Fn(usize, usize) -> bool,
swap: &mut impl FnMut(usize, usize),
) {
let mut cur = target;
loop {
let Some(twice) = (cur - a).checked_mul(2) else {
break;
};
let mut child = twice + a + 1;
if !(child < b) {
break;
}
let next_child = child + 1;
if next_child < b && less(child, next_child) {
child = next_child;
}
if less(child, cur) {
break;
}
swap(child, cur);
cur = child;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, PartialEq, Debug, crate::SoaRowDerive)]
struct Foo {
a: u32,
b: u8,
c: u64,
}
#[test]
fn reflected_metadata() {
assert_eq!(Reflected::<Foo>::COUNT, 3);
assert_eq!(&Reflected::<Foo>::SIZES.0[..3], &[8, 4, 1]);
assert_eq!(&Reflected::<Foo>::SIZES.1[..3], &[2, 0, 1]);
assert_eq!(Reflected::<Foo>::index_of("b"), 1);
}
#[test]
fn roundtrip() {
let mut list = MultiArrayList::<Foo>::default();
for i in 0..10u32 {
list.push(Foo {
a: i,
b: i as u8,
c: i as u64 * 100,
})
.unwrap();
}
let s = list.slice();
assert_eq!(s.items_named::<u64>("c")[7], 700);
assert_eq!(s.items_named::<u32>("a")[3], 3);
assert_eq!(*list.get(5), Foo { a: 5, b: 5, c: 500 });
}
#[test]
fn list_items() {
let mut list = MultiArrayList::<Foo>::default();
for i in 0..4u32 {
list.push(Foo {
a: i,
b: i as u8,
c: i as u64 * 10,
})
.unwrap();
}
assert_eq!(list.items_named::<u64>("c"), &[0u64, 10, 20, 30]);
list.items_named_mut::<u32>("a")[2] = 99;
assert_eq!(list.get(2).a, 99);
assert_eq!(list.pop().unwrap().c, 30);
assert_eq!(list.len(), 3);
}
#[allow(dead_code)]
#[derive(crate::SoaRowDerive)]
struct Borrowed<'a> {
name: &'a [u8],
n: u32,
}
#[test]
fn generic_lifetime() {
let mut list = MultiArrayList::<Borrowed<'static>>::default();
list.push(Borrowed { name: b"hi", n: 7 }).unwrap();
assert_eq!(list.items_named::<&[u8]>("name")[0], b"hi");
assert_eq!(list.items_named::<u32>("n")[0], 7);
}
#[test]
fn empty_items_aligned() {
let list = MultiArrayList::<Foo>::default();
assert_eq!(list.items_named::<u64>("c"), &[] as &[u64]);
let s = Slice::<Foo>::EMPTY;
assert_eq!(s.items_named::<u64>("c"), &[] as &[u64]);
}
#[test]
fn insert_ordered_remove_memmove() {
let mut list = MultiArrayList::<Foo>::default();
for i in 0..6u32 {
list.push(Foo {
a: i,
b: i as u8,
c: i as u64,
})
.unwrap();
}
list.insert(
2,
Foo {
a: 99,
b: 99,
c: 99,
},
)
.unwrap();
assert_eq!(list.items_named::<u32>("a"), &[0, 1, 99, 2, 3, 4, 5]);
list.ordered_remove(2);
assert_eq!(list.items_named::<u32>("a"), &[0, 1, 2, 3, 4, 5]);
list.swap_remove(1);
assert_eq!(list.items_named::<u32>("a"), &[0, 5, 2, 3, 4]);
}
#[test]
fn sort_swaps_all_columns() {
let mut list = MultiArrayList::<Foo>::default();
for i in (0..5u32).rev() {
list.push(Foo {
a: i,
b: i as u8,
c: i as u64 * 10,
})
.unwrap();
}
let raw = list.items_raw_named::<u32>("a");
let len = list.len();
struct Ctx {
a: *const u32,
len: usize,
}
impl SortContext for Ctx {
fn less_than(&self, ai: usize, bi: usize) -> bool {
debug_assert!(ai < self.len && bi < self.len);
unsafe { *self.a.add(ai) < *self.a.add(bi) }
}
}
list.sort(&Ctx { a: raw, len });
assert_eq!(list.items_named::<u32>("a"), &[0, 1, 2, 3, 4]);
assert_eq!(list.items_named::<u64>("c"), &[0, 10, 20, 30, 40]);
}
}