use super::geom::Rect;
use super::store::{AuthorityError, EdgeKey, Store};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct RefinementWork {
pub index: u64,
pub discovery: u64,
pub sort: u64,
pub events: u64,
pub pieces: u64,
pub references: u64,
}
impl RefinementWork {
pub fn total(&self) -> u64 {
self.index + self.discovery + self.sort + self.events + self.pieces + self.references
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RefinedPiece {
pub owner: u32,
pub sheet: u16,
pub domain: Rect,
pub edge_start: usize,
pub edge_end: usize,
}
#[derive(Debug)]
pub struct RefinedSlice {
pub pieces: Vec<RefinedPiece>,
pub edges: Vec<EdgeKey>,
pub work: RefinementWork,
pub peak_heap_bytes: u64,
pub cell_references: u64,
}
impl RefinedSlice {
pub(crate) fn retained_heap_bytes(&self) -> u64 {
(self.pieces.capacity() * size_of::<RefinedPiece>()
+ self.edges.capacity() * size_of::<EdgeKey>()) as u64
}
}
#[derive(Clone, Copy, Default)]
struct Event {
row: u32,
edge: usize,
start: bool,
}
fn bytes<T>(n: usize) -> Result<u64, AuthorityError> {
n.checked_mul(size_of::<T>())
.and_then(|n| u64::try_from(n).ok())
.ok_or(AuthorityError::Alloc)
}
fn add(a: u64, b: u64) -> Result<u64, AuthorityError> {
a.checked_add(b).ok_or(AuthorityError::Alloc)
}
fn admit(needed: u64, limit: Option<u64>) -> Result<(), AuthorityError> {
if let Some(limit) = limit
&& needed > limit
{
return Err(AuthorityError::Admission {
resource: "scratch",
needed,
limit,
});
}
Ok(())
}
fn reserved<T>(n: usize) -> Result<Vec<T>, AuthorityError> {
let mut v = Vec::new();
v.try_reserve_exact(n).map_err(|_| AuthorityError::Alloc)?;
Ok(v)
}
fn sort_events(events: &mut [Event], temp: &mut [Event], work: &mut RefinementWork) {
const RADIX_MIN: usize = 64;
if events.len() < RADIX_MIN {
let n = events.len() as u64;
work.sort += n * u64::from(64 - n.leading_zeros()).max(1);
events.sort_unstable_by_key(|e| e.row);
return;
}
for shift in [0, 8, 16] {
let mut counts = [0usize; 256];
work.sort += 256; for e in events.iter() {
work.sort += 1;
counts[((e.row >> shift) & 255) as usize] += 1;
}
let mut pos = 0;
for n in &mut counts {
work.sort += 1;
let count = *n;
*n = pos;
pos += count;
}
for &e in events.iter() {
work.sort += 1;
let bucket = ((e.row >> shift) & 255) as usize;
temp[counts[bucket]] = e;
counts[bucket] += 1;
}
for (dst, src) in events.iter_mut().zip(temp.iter()) {
work.sort += 1;
*dst = *src;
}
}
}
const NONE: usize = usize::MAX;
fn sweep(
domain: Rect,
events: &[Event],
prev: &mut [usize],
next: &mut [usize],
work: &mut RefinementWork,
mut emit_piece: impl FnMut(Rect),
mut emit_edge: impl FnMut(usize),
) {
let mut head = NONE;
let mut i = 0;
let mut row = domain.r0;
while row <= domain.r1 {
while i < events.len() && events[i].row == row {
work.events += 1;
let e = events[i];
if e.start {
prev[e.edge] = NONE;
next[e.edge] = head;
if head != NONE {
prev[head] = e.edge;
}
head = e.edge;
} else {
let (p, n) = (prev[e.edge], next[e.edge]);
if p == NONE {
head = n;
} else {
next[p] = n;
}
if n != NONE {
prev[n] = p;
}
}
i += 1;
}
let end = events.get(i).map_or(domain.r1 + 1, |e| e.row);
debug_assert!(row < end);
work.pieces += 1;
emit_piece(Rect::new(row, domain.c0, end - 1, domain.c0));
let mut active = head;
while active != NONE {
work.references += 1;
emit_edge(active);
active = next[active];
}
row = end;
}
}
impl Store {
pub(crate) fn refine_owner_column(
&self,
owner: u32,
col: u32,
r0: u32,
r1: u32,
scratch_limit: Option<u64>,
) -> Result<RefinedSlice, AuthorityError> {
let (sheet, owner_domain, _) = self.owner_dom(owner);
let mut out = RefinedSlice {
pieces: Vec::new(),
edges: Vec::new(),
work: RefinementWork::default(),
peak_heap_bytes: 0,
cell_references: 0,
};
if r0 > r1 || col < owner_domain.c0 || col > owner_domain.c1 {
return Ok(out);
}
let Some(domain) = owner_domain.intersect(&Rect::new(r0, col, r1, col)) else {
return Ok(out);
};
const INLINE_EDGES: usize = 32;
let mut inline: smallvec::SmallVec<[(EdgeKey, Rect); INLINE_EDGES]> =
smallvec::SmallVec::new();
let mut count = 0usize;
out.work.index += self.visit_plan_edges(sheet, &domain, &mut |key, dep| {
out.work.discovery += 1;
if count < INLINE_EDGES {
inline.push((key, dep));
}
count += 1;
});
let event_count = count.checked_mul(2).ok_or(AuthorityError::Alloc)?;
let event_bytes = bytes::<Event>(event_count)?;
let link_bytes = bytes::<usize>(count)?;
let temporary = add(
add(bytes::<EdgeKey>(count)?, add(event_bytes, event_bytes)?)?,
add(link_bytes, link_bytes)?,
)?;
admit(temporary, scratch_limit)?;
let mut keys = reserved::<EdgeKey>(count)?;
let mut events = reserved::<Event>(event_count)?;
let mut temp = reserved::<Event>(event_count)?;
let mut prev = reserved::<usize>(count)?;
let mut next = reserved::<usize>(count)?;
for _ in 0..event_count {
out.work.events += 1;
temp.push(Event::default());
}
for _ in 0..count {
out.work.events += 2;
prev.push(NONE);
next.push(NONE);
}
let mut add_edge = |key: EdgeKey, dep: Rect| {
let dep = dep
.intersect(&domain)
.expect("index returned a disjoint edge");
let edge = keys.len();
keys.push(key);
events.push(Event {
row: dep.r0,
edge,
start: true,
});
events.push(Event {
row: dep.r1 + 1,
edge,
start: false,
});
out.cell_references += dep.area();
};
if count <= INLINE_EDGES {
for (key, dep) in inline {
add_edge(key, dep);
}
} else {
out.work.index += self.visit_plan_edges(sheet, &domain, &mut |key, dep| {
out.work.discovery += 1;
add_edge(key, dep);
});
}
debug_assert_eq!(keys.len(), count);
sort_events(&mut events, &mut temp, &mut out.work);
let (mut pieces, mut references) = (0usize, 0usize);
sweep(
domain,
&events,
&mut prev,
&mut next,
&mut out.work,
|_| pieces += 1,
|_| references += 1,
);
let peak = add(
temporary,
add(
bytes::<RefinedPiece>(pieces)?,
bytes::<EdgeKey>(references)?,
)?,
)?;
admit(peak, scratch_limit)?;
out.pieces = reserved::<RefinedPiece>(pieces)?;
out.edges = reserved::<EdgeKey>(references)?;
let edge_cursor = std::cell::Cell::new(0usize);
sweep(
domain,
&events,
&mut prev,
&mut next,
&mut out.work,
|domain| {
let start = edge_cursor.get();
if let Some(p) = out.pieces.last_mut() {
p.edge_end = start;
}
out.pieces.push(RefinedPiece {
owner,
sheet,
domain,
edge_start: start,
edge_end: start,
});
},
|edge| {
out.edges.push(keys[edge]);
edge_cursor.set(edge_cursor.get() + 1);
},
);
if let Some(p) = out.pieces.last_mut() {
p.edge_end = out.edges.len();
}
debug_assert_eq!(out.pieces.len(), pieces);
debug_assert_eq!(out.edges.len(), references);
out.peak_heap_bytes = peak;
Ok(out)
}
}