use std::any::Any;
use std::collections::TryReserveError;
pub trait Component: Send + Sync + 'static {}
impl<T: Send + Sync + 'static> Component for T {}
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct Ticks<'a> {
pub(crate) added: &'a [u64],
pub(crate) changed: &'a [u64],
}
#[doc(hidden)]
#[derive(Debug)]
pub struct ColumnData<T> {
values: Vec<T>,
added: Vec<u64>,
changed: Vec<u64>,
}
impl<T> Default for ColumnData<T> {
fn default() -> Self {
Self {
values: Vec::new(),
added: Vec::new(),
changed: Vec::new(),
}
}
}
impl<T> ColumnData<T> {
pub(crate) fn len(&self) -> usize {
self.values.len()
}
pub(crate) fn values(&self) -> &[T] {
&self.values
}
pub(crate) fn ticks(&self) -> Ticks<'_> {
Ticks {
added: &self.added,
changed: &self.changed,
}
}
pub(crate) fn parts_mut(&mut self) -> (&mut [T], &mut [u64]) {
(&mut self.values, &mut self.changed)
}
pub(crate) fn get_mut(&mut self, row: usize) -> Option<(&mut T, &mut u64)> {
Some((self.values.get_mut(row)?, self.changed.get_mut(row)?))
}
pub(crate) fn push(&mut self, value: T, added: u64, changed: u64) {
self.values.push(value);
self.added.push(added);
self.changed.push(changed);
}
pub(crate) fn swap_remove(&mut self, row: usize) -> Option<(T, u64, u64)> {
if row >= self.values.len() || row >= self.added.len() || row >= self.changed.len() {
return None;
}
let added = self.added.swap_remove(row);
let changed = self.changed.swap_remove(row);
Some((self.values.swap_remove(row), added, changed))
}
}
pub(crate) trait Column: Send + Sync {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn empty(&self) -> Box<dyn Column>;
fn reserve_rows(&mut self, additional: usize) -> Result<(), TryReserveError>;
fn ticks(&self) -> Ticks<'_>;
fn swap_remove_drop(&mut self, row: usize) -> bool;
fn swap_remove_into(&mut self, row: usize, dst: &mut dyn Column) -> bool;
}
impl<T: Component> Column for ColumnData<T> {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn empty(&self) -> Box<dyn Column> {
Box::new(Self::default())
}
fn reserve_rows(&mut self, additional: usize) -> Result<(), TryReserveError> {
reserve_staggered(&mut self.values, additional)?;
reserve_staggered(&mut self.added, additional)?;
reserve_staggered(&mut self.changed, additional)
}
fn ticks(&self) -> Ticks<'_> {
ColumnData::ticks(self)
}
fn swap_remove_drop(&mut self, row: usize) -> bool {
self.swap_remove(row).is_some()
}
fn swap_remove_into(&mut self, row: usize, dst: &mut dyn Column) -> bool {
let Some(dst) = dst.as_any_mut().downcast_mut::<Self>() else {
return false;
};
match self.swap_remove(row) {
Some((value, added, changed)) => {
dst.push(value, added, changed);
true
}
None => false,
}
}
}
const PAGE: usize = 4096;
const STAGGER: usize = PAGE / 2;
#[inline]
fn reserve_staggered<U>(vec: &mut Vec<U>, additional: usize) -> Result<(), TryReserveError> {
if vec.capacity().wrapping_sub(vec.len()) >= additional {
return Ok(()); }
grow_staggered(vec, additional)
}
#[cold]
#[inline(never)]
fn grow_staggered<U>(vec: &mut Vec<U>, additional: usize) -> Result<(), TryReserveError> {
let size = size_of::<U>();
let len = vec.len();
let Some(needed) = len.checked_add(additional) else {
return vec.try_reserve(additional); };
let min = match size {
1 => 8,
2..=1024 => 4,
_ => 1,
};
let mut rows = needed.max(vec.capacity().saturating_mul(2)).max(min);
let bytes = rows.saturating_mul(size);
if bytes >= PAGE {
let pad = (STAGGER.saturating_add(PAGE).saturating_sub(bytes % PAGE)) % PAGE;
rows = rows.saturating_add(pad.div_ceil(size));
}
vec.try_reserve_exact(rows.saturating_sub(len))
}
#[cfg(test)]
mod tests {
use super::*;
fn column(values: &[u32]) -> ColumnData<u32> {
let mut column = ColumnData::default();
for (tick, value) in (1..).zip(values) {
column.push(*value, tick, tick.saturating_mul(10));
}
column
}
#[test]
fn moves_between_columns_of_the_same_type_only() {
let mut src = column(&[1, 2, 3]);
let mut dst = src.empty();
let mut wrong: Box<dyn Column> = Box::new(ColumnData::<u64>::default());
assert!(!src.swap_remove_into(0, wrong.as_mut()));
assert!(!src.swap_remove_into(3, dst.as_mut()));
assert!(src.swap_remove_into(0, dst.as_mut()));
assert_eq!(src.values(), [3, 2]);
assert_eq!(
(src.ticks().added, src.ticks().changed),
(&[3, 2][..], &[30, 20][..])
);
let moved = dst.as_any().downcast_ref::<ColumnData<u32>>().unwrap();
assert_eq!(moved.values(), [1]);
assert_eq!(
(dst.ticks().added, dst.ticks().changed),
(&[1][..], &[10][..])
);
assert!(src.swap_remove_drop(1));
assert!(!src.swap_remove_drop(1));
assert_eq!(src.values(), [3]);
assert_eq!(src.len(), 1);
}
#[test]
fn grown_blocks_end_half_a_page_past_a_page_multiple() {
let mut values: Vec<[f32; 3]> = Vec::new();
let mut ticks: Vec<u64> = Vec::new();
let mut grows = 0;
for _ in 0..100_000 {
let before = values.capacity();
reserve_staggered(&mut values, 1).unwrap();
reserve_staggered(&mut ticks, 1).unwrap();
values.push([0.0; 3]);
ticks.push(0);
if values.capacity() != before {
grows += 1;
assert!(values.capacity() >= before * 2, "still doubling");
}
for bytes in [values.capacity() * 12, ticks.capacity() * 8] {
assert!(bytes < PAGE || bytes % PAGE >= STAGGER, "{bytes} bytes");
assert!(bytes < PAGE || bytes % PAGE < STAGGER + 12, "{bytes} bytes");
}
}
assert!(grows < 20, "amortized: {grows} reallocations");
let mut empty: Vec<()> = Vec::new();
reserve_staggered(&mut empty, usize::MAX).unwrap();
assert!(reserve_staggered(&mut ticks, usize::MAX).is_err());
assert!(reserve_staggered(&mut ticks, usize::MAX / 4).is_err());
}
}