const DEFAULT_BLOCK_SIZE: usize = 256;
const DEFAULT_MAX_RETAINED_SIZE: usize = 1_000_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ArcRun {
block: u32,
start: u32,
len: u32,
}
impl ArcRun {
#[inline(always)]
pub fn len(&self) -> usize {
self.len as usize
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
pub struct ArcArena<A> {
retired: Vec<Vec<A>>,
current: Vec<A>,
start: usize,
block_size: usize,
first_block_size: usize,
total_size: usize,
max_retained_size: usize,
}
impl<A> Default for ArcArena<A> {
fn default() -> Self {
Self::new()
}
}
impl<A> ArcArena<A> {
pub fn new() -> Self {
Self::with_options(DEFAULT_BLOCK_SIZE, DEFAULT_MAX_RETAINED_SIZE)
}
pub fn with_block_size(block_size: usize) -> Self {
Self::with_options(block_size, DEFAULT_MAX_RETAINED_SIZE)
}
pub fn with_options(block_size: usize, max_retained_size: usize) -> Self {
let block_size = block_size.max(1);
Self {
retired: Vec::new(),
current: Vec::with_capacity(block_size),
start: 0,
block_size,
first_block_size: block_size,
total_size: block_size,
max_retained_size,
}
}
#[inline(always)]
pub fn size(&self) -> usize {
self.total_size
}
#[inline(always)]
pub fn pending(&self) -> &[A] {
&self.current[self.start..]
}
#[inline(always)]
pub fn arcs(&self, run: ArcRun) -> &[A] {
let block = run.block as usize;
let block = if block == self.retired.len() {
&self.current
} else {
&self.retired[block]
};
let start = run.start as usize;
&block[start..start + run.len as usize]
}
pub fn reserve_arcs(&mut self, n: usize) {
if self.current.capacity() - self.current.len() >= n {
return;
}
self.new_block(n);
}
#[inline]
pub fn push_arc(&mut self, arc: A) {
let len = self.current.len();
if len == self.current.capacity() {
self.new_block((len - self.start).max(1));
self.current.push(arc);
return;
}
unsafe {
std::ptr::write(self.current.as_mut_ptr().add(len), arc);
self.current.set_len(len + 1);
}
}
pub fn commit_arcs(&mut self) -> ArcRun {
let block = self.retired.len();
let start = self.start;
let len = self.current.len() - start;
self.start += len;
debug_assert!(block <= u32::MAX as usize && start + len <= u32::MAX as usize);
ArcRun {
block: block as u32,
start: start as u32,
len: len as u32,
}
}
pub fn drop_arcs(&mut self) {
self.current.truncate(self.start);
}
pub fn clear(&mut self) {
self.retired.clear();
if self.total_size > self.first_block_size {
self.first_block_size = self.max_retained_size.min(self.total_size);
self.current = Vec::with_capacity(self.first_block_size);
} else {
self.current.clear();
}
self.total_size = self.first_block_size;
self.start = 0;
}
fn new_block(&mut self, n: usize) {
let pending_len = self.current.len() - self.start;
let new_block_size = (pending_len + n).max(self.block_size);
let mut new_block = Vec::with_capacity(new_block_size);
new_block.extend(self.current.drain(self.start..));
self.total_size += new_block_size;
self.retired
.push(std::mem::replace(&mut self.current, new_block));
self.start = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arc::{Arc, StdArc};
use crate::weight::Weight;
use crate::weights::float_weight::TropicalWeight;
use std::cell::Cell;
use std::rc::Rc;
fn arc(label: i32) -> StdArc {
StdArc::new(label, label, TropicalWeight::one(), label)
}
fn push_run(arena: &mut ArcArena<StdArc>, labels: std::ops::Range<i32>) -> ArcRun {
for label in labels {
arena.push_arc(arc(label));
}
arena.commit_arcs()
}
#[test]
fn committed_runs_are_contiguous_and_in_order() {
let mut arena = ArcArena::with_block_size(4);
let run = push_run(&mut arena, 0..3);
let arcs = arena.arcs(run);
assert_eq!(arcs.len(), 3);
assert_eq!(
arcs.iter().map(|a| a.ilabel).collect::<Vec<_>>(),
vec![0, 1, 2]
);
}
#[test]
fn earlier_runs_stay_valid_after_the_arena_grows() {
let mut arena = ArcArena::with_block_size(4);
let runs: Vec<_> = (0..8)
.map(|i| push_run(&mut arena, i * 3..i * 3 + 3))
.collect();
for (i, run) in runs.iter().enumerate() {
let expected: Vec<i32> = (i as i32 * 3..i as i32 * 3 + 3).collect();
assert_eq!(
arena
.arcs(*run)
.iter()
.map(|a| a.ilabel)
.collect::<Vec<_>>(),
expected,
"run {i} was invalidated"
);
}
}
#[test]
fn a_run_longer_than_a_block_stays_contiguous() {
let mut arena = ArcArena::with_block_size(4);
let run = push_run(&mut arena, 0..37);
let arcs = arena.arcs(run);
assert_eq!(arcs.len(), 37);
assert!(arcs.iter().enumerate().all(|(i, a)| a.ilabel == i as i32));
}
#[test]
fn reserve_arcs_mid_sequence_keeps_the_run_contiguous() {
let mut arena = ArcArena::with_block_size(4);
for label in 0..300 {
arena.push_arc(arc(label));
}
arena.reserve_arcs(250);
assert_eq!(arena.pending().len(), 300);
for label in 300..400 {
arena.push_arc(arc(label));
}
let run = arena.commit_arcs();
let arcs = arena.arcs(run);
assert_eq!(arcs.len(), 400);
assert!(arcs.iter().enumerate().all(|(i, a)| a.ilabel == i as i32));
}
#[test]
fn reserve_arcs_does_not_grow_when_the_block_already_fits() {
let mut arena = ArcArena::<StdArc>::with_block_size(64);
let before = arena.size();
arena.reserve_arcs(64);
assert_eq!(arena.size(), before);
}
#[test]
fn drop_arcs_reuses_the_space() {
let mut arena = ArcArena::with_block_size(16);
let first = push_run(&mut arena, 0..4);
for label in 100..104 {
arena.push_arc(arc(label));
}
arena.drop_arcs();
assert!(arena.pending().is_empty());
let second = push_run(&mut arena, 200..202);
assert_eq!(arena.arcs(first).len(), 4);
assert_eq!(
arena
.arcs(second)
.iter()
.map(|a| a.ilabel)
.collect::<Vec<_>>(),
vec![200, 201]
);
assert_eq!(arena.size(), 16);
}
#[test]
fn clear_retains_capacity_for_the_next_round() {
let mut arena = ArcArena::with_block_size(4);
push_run(&mut arena, 0..40);
let grown = arena.size();
assert!(grown > 4);
arena.clear();
assert_eq!(arena.size(), grown, "clear should retain the peak capacity");
push_run(&mut arena, 0..40);
assert_eq!(arena.size(), grown);
}
#[test]
fn clear_caps_retained_capacity() {
let mut arena = ArcArena::with_options(4, 16);
push_run(&mut arena, 0..100);
arena.clear();
assert_eq!(arena.size(), 16);
}
#[test]
fn clear_on_an_ungrown_arena_keeps_the_first_block() {
let mut arena = ArcArena::with_block_size(8);
push_run(&mut arena, 0..3);
arena.clear();
assert_eq!(arena.size(), 8);
assert!(arena.pending().is_empty());
}
#[test]
fn empty_runs_are_representable() {
let mut arena = ArcArena::<StdArc>::with_block_size(4);
let run = arena.commit_arcs();
assert!(run.is_empty());
assert!(arena.arcs(run).is_empty());
}
#[test]
fn elements_are_dropped_exactly_once() {
struct Counted(Rc<Cell<usize>>);
impl Drop for Counted {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
let drops = Rc::new(Cell::new(0));
{
let mut arena = ArcArena::with_block_size(2);
for _ in 0..10 {
arena.push_arc(Counted(Rc::clone(&drops)));
}
arena.commit_arcs();
for _ in 0..3 {
arena.push_arc(Counted(Rc::clone(&drops)));
}
arena.drop_arcs();
assert_eq!(drops.get(), 3);
arena.clear();
assert_eq!(drops.get(), 13);
for _ in 0..5 {
arena.push_arc(Counted(Rc::clone(&drops)));
}
arena.commit_arcs();
}
assert_eq!(drops.get(), 18, "arena teardown must drop the live run");
}
}