use std::sync::Arc;
use rustc_hash::{FxHashMap, FxHashSet};
use super::pyramid::{Leveling, Places, Placing, Pyramid, Wanted, answer, reaches};
use super::sight::{Domain, Sight};
use super::{Jobs, stamp_of};
use crate::data::{Data, Field, Name, RowIds, Table, UNWIRED, Version};
use crate::expr::cpu::Rows;
use crate::expr::{Expr, Node};
use crate::host::Job;
use crate::{Pending, Row};
const PLACE_COST: f64 = 1.0;
const SAMPLE: u32 = 1 << 16;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum Draw {
Whole,
Tiles { rows: u32 },
First { rows: u32 },
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Place {
Nowhere,
Points([Expr; 2]),
Geometry(Name),
Ends(Vec<Name>),
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct Planned {
pub(crate) draw: Draw,
pub(crate) priority: Option<Expr>,
pub(crate) place: Place,
pub(crate) reads: Option<FxHashSet<Name>>,
}
impl Planned {
pub(crate) fn as_is(&self) -> bool {
self.draw == Draw::Whole && self.priority.is_none()
}
}
pub(crate) type Plans = FxHashMap<(Name, Name), Planned>;
pub(crate) struct Ask<'a> {
pub(crate) sources: &'a [Data],
pub(crate) plans: &'a Plans,
pub(crate) sight: Option<&'a Sight<'a>>,
pub(crate) earth: bool,
pub(crate) pinned: &'a [Row],
pub(crate) at: f64,
}
#[derive(Default)]
pub(crate) struct Tiled {
sets: Vec<Set>,
}
impl Tiled {
pub(crate) fn update(&mut self, ask: &Ask<'_>, jobs: &mut Jobs<'_>) -> (Vec<Data>, Vec<Name>) {
let mut wanted: Vec<&Name> = Vec::new();
for ((data, _), planned) in ask.plans {
if !planned.as_is() && !wanted.contains(&data) {
wanted.push(data);
}
}
let mut gone = Vec::new();
self.sets.retain(|set| {
let keep = wanted.contains(&&set.name) && ask.sources.iter().any(|d| *d.name() == set.name);
if !keep {
gone.push(set.name.clone());
}
keep
});
let mut changed = Vec::new();
for name in wanted {
let Some(source) = ask.sources.iter().find(|d| d.name() == name) else { continue };
let i = self.sets.iter().position(|s| s.name == *name).unwrap_or_else(|| {
self.sets.push(Set::new(name.clone()));
self.sets.len() - 1
});
if let Some(drawn) = self.sets[i].update(source, ask, jobs) {
changed.push(drawn);
}
}
(changed, gone)
}
pub(crate) fn complete(&self) -> bool {
self.sets.iter().all(|s| s.working.is_empty() && !s.deferred && !s.sampling && s.settled())
}
pub(crate) fn due(&self) -> bool {
self.sets.iter().any(|s| s.deferred || !s.settled())
}
pub(crate) fn draws(&self, name: &str) -> bool {
self.sets.iter().any(|s| *s.name == *name)
}
pub(crate) fn bounds(&self, data: &Name, table: &Name) -> Option<[f64; 4]> {
let set = self.sets.iter().find(|s| s.name == *data)?;
set.placed.get(table).and_then(|placed| placed.bounds)
}
}
struct Placed {
made: Version,
rows: (Version, Version),
domain: Domain,
pyramid: Pyramid,
bounds: Option<[f64; 4]>,
}
struct Working {
table: Name,
pending: Pending<Vec<u8>>,
answered: u32,
placed: Placed,
}
#[derive(Default)]
struct Gathered {
rows: Option<Arc<[u32]>>,
slots: Slots,
writes: Option<Vec<(usize, usize)>>,
versions: Option<(Version, Version)>,
ids: Option<RowIds>,
fields: FxHashMap<Name, Kept>,
references: FxHashMap<Name, (u64, u64, Field)>,
targets: FxHashMap<Name, (Version, Arc<[u32]>)>,
}
struct Kept {
stamp: u64,
source: Field,
drawn: Field,
}
struct Set {
name: Name,
stamp: u64,
placed: FxHashMap<Name, Arc<Placed>>,
working: Vec<Working>,
deferred: bool,
laid: u64,
held: u64,
quiet: u32,
tables: FxHashMap<Name, Gathered>,
rows: FxHashMap<Name, Arc<[u32]>>,
versions: FxHashMap<Name, (Version, Version)>,
sampling: bool,
}
impl Set {
fn new(name: Name) -> Set {
Set {
name,
stamp: 0,
placed: FxHashMap::default(),
working: Vec::new(),
deferred: false,
laid: 0,
held: 0,
quiet: 0,
tables: FxHashMap::default(),
rows: FxHashMap::default(),
versions: FxHashMap::default(),
sampling: false,
}
}
fn settled(&self) -> bool {
self.tables.values().all(|g| g.slots.ordered || g.slots.rows.is_empty())
}
fn update(&mut self, source: &Data, ask: &Ask<'_>, jobs: &mut Jobs<'_>) -> Option<Data> {
let placing = placed(source, ask);
let mut i = 0;
while i < self.working.len() {
let Some(landed) = self.working[i].pending.take() else {
i += 1;
continue;
};
let working = self.working.swap_remove(i);
if let Ok(bytes) = landed
&& let Some(pyramid) = Pyramid::read(&bytes).remove(&working.answered)
{
self.placed
.insert(working.table, Arc::new(Placed { pyramid, made: Version::next(), ..working.placed }));
}
}
let stamp = stamp(source, ask, &placing);
let due = !placing.is_empty() && stamp != self.stamp;
let cost: f64 = placing
.iter()
.map(|(t, p)| {
t.len() as f64 * if matches!(p.draw, Draw::Tiles { .. }) { PLACE_COST } else { PLACE_COST / 20.0 }
})
.sum();
let at_once = cost <= jobs.left() as f64;
if due && at_once {
self.level(&placing, ask, stamp, jobs);
}
let drawn = self.gather(source, ask);
self.deferred = due && !at_once && drawn.is_some();
if due && !at_once && drawn.is_none() {
self.level(&placing, ask, stamp, jobs);
}
drawn
}
fn level(&mut self, placing: &[(&Table, &Planned)], ask: &Ask<'_>, stamp: u64, jobs: &mut Jobs<'_>) {
{
self.stamp = stamp;
self.working.clear();
let (leveling, bounds) = leveling(ask, placing);
let domain = leveling.domain;
for (i, table) in leveling.tables.iter().enumerate().filter(|(_, t)| t.answered) {
let name = placing[i].0.name.clone();
let mut alone = Leveling { domain, tables: Vec::with_capacity(2) };
let mut table = table.clone();
if let Some((target, _)) = table.ends.as_mut() {
let at = &leveling.tables[*target as usize];
alone.tables.push(Placing { rows: at.rows, places: at.places.clone(), ..Placing::default() });
*target = 0;
}
let answered = alone.tables.len() as u32;
let rows = (placing[i].0.structure, placing[i].0.order);
alone.tables.push(table);
let per_row = if alone.tables[answered as usize].leveled { PLACE_COST } else { PLACE_COST / 20.0 };
let cost = per_row * alone.tables.iter().map(|t| f64::from(t.rows)).sum::<f64>();
let made = Version::next();
let placed =
Placed { made, rows, domain, pyramid: Pyramid::default(), bounds: bounds.get(&name).copied() };
if jobs.fits(cost as u64) {
let pyramid = Pyramid::answered(answer(&alone)).remove(&answered).unwrap_or_default();
self.placed.insert(name, Arc::new(Placed { pyramid, ..placed }));
} else {
let job = Job::pyramid(&alone);
let pending = jobs.spawn(job);
self.working.push(Working { table: name, pending, answered, placed });
}
}
}
}
fn pyramid(&self, source: &Data, name: &Name) -> Option<Arc<Placed>> {
let table = source.table(name)?;
self.placed.get(name).filter(|p| p.rows == (table.structure, table.order)).cloned()
}
fn gather(&mut self, source: &Data, ask: &Ask<'_>) -> Option<Data> {
let mut chosen: Vec<(Name, Choice)> = Vec::new();
let mut serving: Vec<(Name, Arc<Placed>)> = Vec::new();
for table in source.tables() {
let Some(planned) = ask.plans.get(&(source.name().clone(), table.name.clone())) else { continue };
if planned.as_is() {
continue;
}
let placed = self.pyramid(source, &table.name);
if let Some(placed) = &placed {
serving.push((table.name.clone(), placed.clone()));
}
let pyramid = placed.as_deref().map(|placed| (&placed.pyramid, &placed.domain));
let choice = match (pyramid, planned.draw) {
(None, Draw::Whole) => Choice::AsIs,
(None, Draw::Tiles { rows } | Draw::First { rows }) => {
Choice::Sample((table.len() as u32).div_ceil(rows.clamp(1, SAMPLE)).max(1))
}
(Some(_), Draw::Whole) => Choice::Tiles(Wanted { tiles: vec![(0, true)], cover: None }),
(Some((pyramid, _)), Draw::First { rows }) => Choice::Last(rows.min(pyramid.order.len() as u32)),
(Some((pyramid, domain)), Draw::Tiles { rows }) => {
Choice::Tiles(ask.sight.map(|sight| pyramid.wanted(sight, domain, rows)).unwrap_or_default())
}
};
chosen.push((table.name.clone(), choice));
}
self.sampling = chosen.iter().any(|(_, choice)| matches!(choice, Choice::Sample(_)));
let pinned: Vec<&Row> = ask.pinned.iter().filter(|r| r.data == self.name).collect();
let frames: Vec<Option<u64>> = sampled_frames(source, ask);
let pyramids: Vec<Version> = serving.iter().map(|(_, p)| p.made).collect();
let laid = stamp_of((pyramids, &chosen, &pinned, rows_of(source)));
let held = stamp_of((source.0.version, frames, reads_of(source, ask)));
if laid == self.laid {
self.quiet = self.quiet.saturating_add(1);
if held == self.held && (self.settled() || self.quiet < SETTLE) {
return None;
}
} else {
self.quiet = 0;
}
let afresh = self.quiet >= SETTLE && !self.settled();
let relaid = laid != self.laid || afresh;
(self.laid, self.held) = (laid, held);
if !relaid {
return Some(self.rewrite(source, ask));
}
let serving: FxHashMap<Name, Arc<Placed>> = serving.into_iter().collect();
Some(self.build(source, ask, &serving, &chosen, &pinned, afresh))
}
fn rewrite(&mut self, source: &Data, ask: &Ask<'_>) -> Data {
let (rows, versions) = (std::mem::take(&mut self.rows), std::mem::take(&mut self.versions));
for name in rows.keys() {
if let Some(gathered) = self.tables.get_mut(name) {
gathered.writes = Some(Vec::new());
}
}
let tables: Vec<Table> =
source.tables().map(|table| self.drawn(source, table, ask, &rows, &versions)).collect();
(self.rows, self.versions) = (rows, versions);
source.drawn(tables)
}
fn build(
&mut self,
source: &Data,
ask: &Ask<'_>,
serving: &FxHashMap<Name, Arc<Placed>>,
chosen: &[(Name, Choice)],
pinned: &[&Row],
afresh: bool,
) -> Data {
let mut wanted: FxHashMap<Name, Vec<u32>> = chosen
.iter()
.filter_map(|(name, choice)| {
let len = source.table(name).map_or(0, Table::len) as u32;
Some((name.clone(), choice.rows(serving.get(name).map(Arc::as_ref), len)?))
})
.collect();
let mut drawn: FxHashMap<Name, Bitset> = wanted
.iter()
.map(|(name, list)| (name.clone(), Bitset::of(list, source.table(name).map_or(0, Table::len))))
.collect();
for row in pinned {
let (Some(list), Some(table)) = (wanted.get_mut(&row.table), source.table(&row.table)) else { continue };
if let Some(r) = table.row(row.id.as_ref())
&& drawn.get_mut(&row.table).is_some_and(|set| set.insert(r as u32))
{
list.push(r as u32);
}
}
let mut carried: Vec<(Name, Vec<u32>)> = Vec::new();
for table in source.tables() {
let Some(Place::Ends(ends)) = ask.plans.get(&(source.name().clone(), table.name.clone())).map(|p| &p.place)
else {
continue;
};
for (name, field) in &table.fields {
let Field::Reference(reference) = field else { continue };
if !wanted.contains_key(&reference.to) || !ends.contains(name) {
continue;
}
let targets = self.targets(&table.name, name, field);
let ends: Vec<u32> = match wanted.get(&table.name) {
Some(list) => list.iter().map(|&r| targets[r as usize]).collect(),
None => targets.to_vec(),
};
carried.push((reference.to.clone(), ends));
}
}
for (to, ends) in carried {
let (Some(list), Some(set)) = (wanted.get_mut(&to), drawn.get_mut(&to)) else { continue };
for t in ends {
if t != UNWIRED && set.insert(t) {
list.push(t);
}
}
}
let mut rows: FxHashMap<Name, Arc<[u32]>> = FxHashMap::default();
let mut versions: FxHashMap<Name, (Version, Version)> = FxHashMap::default();
for (name, list) in &wanted {
let gathered = self.tables.entry(name.clone()).or_default();
let len = source.table(name).map_or(0, Table::len);
let writes = gathered.slots.lay(list, len, afresh);
let laid: Arc<[u32]> = match (&writes, &gathered.rows) {
(Some(writes), Some(before)) if writes.is_empty() => before.clone(),
_ => gathered.slots.rows.as_slice().into(),
};
let held = match (&writes, gathered.versions) {
(Some(writes), Some(held)) if writes.is_empty() => held,
(Some(_), Some((order, _))) => (order, Version::next()),
_ => {
let v = Version::next();
(v, v)
}
};
gathered.versions = Some(held);
versions.insert(name.clone(), held);
if writes.is_none() {
gathered.ids = None;
gathered.fields.clear();
gathered.references.clear();
}
gathered.writes = writes.map(|w| {
w.iter().map(|&(at, r)| (at as usize, if r == HOLE { crate::data::HOLE } else { r as usize })).collect()
});
gathered.rows = Some(laid.clone());
rows.insert(name.clone(), laid);
}
let mut tables = Vec::with_capacity(source.tables().count());
for table in source.tables() {
tables.push(self.drawn(source, table, ask, &rows, &versions));
}
(self.rows, self.versions) = (rows, versions);
source.drawn(tables)
}
fn targets(&mut self, table: &Name, name: &Name, field: &Field) -> Arc<[u32]> {
let Field::Reference(reference) = field else { return Arc::from([]) };
let gathered = self.tables.entry(table.clone()).or_default();
if let Some((version, targets)) = gathered.targets.get(name)
&& *version == reference.version
{
return targets.clone();
}
let targets: Arc<[u32]> = reference.targets().into();
gathered.targets.insert(name.clone(), (reference.version, targets.clone()));
targets
}
fn remapped(&mut self, table: &Table, versions: &FxHashMap<Name, (Version, Version)>) -> Table {
let mut out = table.clone();
for (name, field) in &table.fields {
let Field::Reference(reference) = field else { continue };
let Some(&(order, structure)) = versions.get(&reference.to) else { continue };
let whole = stamp_of((reference.version, order, structure));
let kept = self.tables.get(&table.name).and_then(|g| g.references.get(name));
if let Some((_, _, field)) = kept.filter(|(_, w, _)| *w == whole) {
out.insert(name.clone(), field.clone());
continue;
}
let targets = self.targets(&table.name, name, field);
let map = &self.tables[&reference.to].slots.at;
let mapped: Vec<u32> = targets.iter().map(|&t| remap(map, t)).collect();
let kept = self.tables.get(&table.name).and_then(|g| g.references.get(name)).map(|(_, _, f)| f.clone());
let kept = kept.as_ref().and_then(|f| if let Field::Reference(r) = f { Some(r) } else { None });
let remapped = reference.resolved_to(reference.ids.clone(), &mapped, order, structure, kept);
let field = Field::Reference(remapped);
let gathered = self.tables.entry(table.name.clone()).or_default();
gathered.references.insert(name.clone(), (stamp_of(reference.version), whole, field.clone()));
out.insert(name.clone(), field);
}
out
}
fn drawn(
&mut self,
source: &Data,
table: &Table,
ask: &Ask<'_>,
rows: &FxHashMap<Name, Arc<[u32]>>,
versions: &FxHashMap<Name, (Version, Version)>,
) -> Table {
let planned = ask.plans.contains_key(&(source.name().clone(), table.name.clone()));
let remaps = table.fields.iter().any(|(_, f)| matches!(f, Field::Reference(r) if rows.contains_key(&r.to)));
let Some(list) = rows.get(&table.name) else {
return if remaps && planned { self.remapped(table, versions) } else { table.clone() };
};
let reads = ask.plans.get(&(source.name().clone(), table.name.clone())).and_then(|p| p.reads.as_ref());
let picks = std::cell::OnceCell::new();
let picked = || {
picks.get_or_init(|| {
list.iter().map(|&r| if r == HOLE { crate::data::HOLE } else { r as usize }).collect::<Vec<usize>>()
})
};
let (order, structure) = versions[&table.name];
let gathered = self.tables.get_mut(&table.name).expect("made with its rows");
let writes = gathered.writes.take();
let len = list.len();
let ids = match (&gathered.ids, writes.as_deref()) {
(Some(ids), Some([])) => ids.clone(),
(Some(ids), Some(writes)) => ids.write(len, writes, &table.ids).expect("ids of one kind"),
_ => table.ids.gather(picked()),
};
gathered.ids = Some(ids.clone());
let mut out =
Table { name: table.name.clone(), ids, index: Arc::default(), fields: Vec::new(), structure, order };
if writes.as_ref().is_some_and(|w| !w.is_empty()) {
gathered.fields.retain(|name, _| reads.is_none_or(|r| r.contains(name)));
}
let frames = |samples: &crate::data::Samples| samples.frame_at(ask.at);
for (name, field) in &table.fields {
let read = reads.is_none_or(|r| r.contains(name));
let gathered = self.tables.get_mut(&table.name).expect("made with its rows");
let kept = gathered.fields.get(name);
let (stamp, made) = match field {
Field::Values(column, version) if read => {
let stamp = stamp_of(version);
let made = match (kept, writes.as_deref()) {
(Some(kept), Some([])) if kept.stamp == stamp => kept.drawn.clone(),
(
Some(Kept { stamp: was, source: Field::Values(before, _), drawn: Field::Values(drawn, _) }),
Some(laid),
) if before.kind() == column.kind() => {
let changed = if *was == stamp {
Vec::new()
} else {
column.rows_since(before).unwrap_or_else(|| column.differing(before))
};
let at = &gathered.slots.at;
let mut written: Vec<(usize, usize)> = changed
.into_iter()
.filter_map(|r| at.get(r).filter(|&&a| a != UNWIRED).map(|&a| (a as usize, r)))
.collect();
written.extend_from_slice(laid);
written.sort_unstable_by_key(|w| w.0);
written.dedup_by_key(|w| w.0);
Field::Values(drawn.write(len, &written, column).expect("one storage"), Version::next())
}
_ => Field::Values(column.gather(picked()), Version::next()),
};
(stamp, made)
}
Field::Sampled(samples, version) if read => {
let frame = frames(samples);
let stamp = stamp_of((version, frame));
let made = match (kept, writes.as_deref()) {
(Some(kept), Some([])) if kept.stamp == stamp => kept.drawn.clone(),
_ => {
let span = frame.map_or(0..0, |f| f..f + 2);
Field::Sampled(samples.gather_frames(picked(), span), Version::next())
}
};
(stamp, made)
}
Field::Reference(reference) => {
let held = versions.get(&reference.to).copied();
let named = held.unwrap_or_else(|| {
source.table(&reference.to).map_or((order, structure), |t| (t.order, t.structure))
});
let resolved =
self.reference(&table.name, name, field, list, writes.as_deref(), (held.is_some(), named));
out.insert(name.clone(), resolved);
continue;
}
_ => continue,
};
let gathered = self.tables.get_mut(&table.name).expect("made with its rows");
gathered.fields.insert(name.clone(), Kept { stamp, source: field.clone(), drawn: made.clone() });
out.insert(name.clone(), made);
}
out
}
fn reference(
&mut self,
table: &Name,
name: &Name,
field: &Field,
list: &[u32],
writes: Option<&[(usize, usize)]>,
(in_part, (order, structure)): (bool, (Version, Version)),
) -> Field {
let Field::Reference(reference) = field else { return field.clone() };
let stamp = stamp_of(reference.version);
let whole = stamp_of((reference.version, order, structure));
let gathered = self.tables.entry(table.clone()).or_default();
if let Some((_, _, field)) = gathered.references.get(name).filter(|(_, w, _)| *w == whole)
&& writes.is_some_and(<[_]>::is_empty)
{
return field.clone();
}
let targets = self.targets(table, name, field);
let gathered = self.tables.entry(table.clone()).or_default();
let kept = gathered.references.get(name).filter(|(s, _, _)| *s == stamp).map(|(_, _, f)| f.clone());
let kept = kept.as_ref().and_then(|f| if let Field::Reference(r) = f { Some(r) } else { None });
let len = list.len();
let ids = match (kept, writes) {
(Some(kept), Some(writes)) => kept.ids.write(len, writes, &reference.ids).expect("ids of one kind"),
(Some(kept), None) if kept.ids.len() == len => kept.ids.clone(),
_ => {
let picked: Vec<usize> =
list.iter().map(|&r| if r == HOLE { crate::data::HOLE } else { r as usize }).collect();
reference.ids.gather(&picked)
}
};
let target = self.tables.get(&reference.to).filter(|_| in_part);
let mapped: Vec<u32> = list
.iter()
.map(|&r| match (r, target) {
(HOLE, _) => UNWIRED,
(r, Some(target)) => remap(&target.slots.at, targets[r as usize]),
(r, None) => targets[r as usize],
})
.collect();
let resolved = Field::Reference(reference.resolved_to(ids, &mapped, order, structure, kept));
let gathered = self.tables.entry(table.clone()).or_default();
gathered.references.insert(name.clone(), (stamp, whole, resolved.clone()));
resolved
}
}
struct Bitset(Vec<u64>);
impl Bitset {
fn of<'a>(list: impl IntoIterator<Item = &'a u32>, len: usize) -> Bitset {
let mut set = Bitset(vec![0; len.div_ceil(64)]);
for &r in list {
set.insert(r);
}
set
}
fn contains(&self, r: u32) -> bool {
self.0.get(r as usize / 64).is_some_and(|word| word >> (r % 64) & 1 == 1)
}
fn insert(&mut self, r: u32) -> bool {
let Some(word) = self.0.get_mut(r as usize / 64) else { return false };
let bit = 1 << (r % 64);
let new = *word & bit == 0;
*word |= bit;
new
}
}
#[derive(Clone, Debug, PartialEq, Hash)]
enum Choice {
AsIs,
Sample(u32),
Tiles(Wanted),
Last(u32),
}
impl Choice {
fn rows(&self, placed: Option<&Placed>, len: u32) -> Option<Vec<u32>> {
Some(match (self, placed) {
(Choice::AsIs, _) => return None,
(Choice::Tiles(wanted), Some(placed)) => {
let pyramid = &placed.pyramid;
let mut tiles = wanted.tiles.clone();
tiles.sort_by_key(|&(t, _)| std::cmp::Reverse((pyramid.tiles[t as usize].key.z, u32::MAX - t)));
let mut list = Vec::new();
for (t, finest) in tiles {
let range = pyramid.tiles[t as usize].rows.clone();
let rows = &pyramid.order[range.start as usize..range.end as usize];
match wanted.cover.filter(|_| !finest && !pyramid.anchors.is_empty()) {
Some(cover) => list.extend(rows.iter().copied().filter(|&r| {
let extent = pyramid.extents.get(r as usize).copied().unwrap_or(0);
reaches(&cover, pyramid.anchors[r as usize], extent)
})),
None => list.extend_from_slice(rows),
}
}
list
}
(Choice::Last(n), Some(placed)) => {
let order = &placed.pyramid.order;
order[order.len() - *n as usize..].to_vec()
}
(Choice::Sample(step), _) => (0..len).step_by(*step as usize).collect(),
(_, None) => Vec::new(),
})
}
}
const HOLE: u32 = u32::MAX;
const SETTLE: u32 = 8;
#[derive(Default)]
struct Slots {
rows: Vec<u32>,
at: Vec<u32>,
holes: Vec<u32>,
ordered: bool,
}
impl Slots {
fn lay(&mut self, wanted: &[u32], len: usize, afresh: bool) -> Option<Vec<(u32, u32)>> {
self.at.resize(len, UNWIRED);
if afresh || self.rows.is_empty() {
return self.afresh(wanted);
}
let keep = Bitset::of(wanted, len);
let mut writes: Vec<(u32, u32)> = Vec::new();
let mut freed: Vec<u32> = Vec::new();
for (i, slot) in self.rows.iter_mut().enumerate() {
if *slot != HOLE && !keep.contains(*slot) {
if let Some(at) = self.at.get_mut(*slot as usize) {
*at = UNWIRED;
}
*slot = HOLE;
freed.push(i as u32);
writes.push((i as u32, HOLE));
}
}
if !freed.is_empty() {
self.holes.extend(freed);
self.holes.sort_unstable();
}
let mut taken = 0;
for &source in wanted {
if self.at.get(source as usize).is_none_or(|&at| at != UNWIRED) {
continue;
}
let row = if let Some(&hole) = self.holes.get(taken) {
taken += 1;
hole
} else {
self.rows.push(HOLE);
self.rows.len() as u32 - 1
};
self.rows[row as usize] = source;
self.at[source as usize] = row;
writes.push((row, source));
}
self.holes.drain(..taken);
if 4 * self.holes.len() > self.rows.len() {
return self.afresh(wanted);
}
writes.sort_by_key(|w| w.0);
writes.dedup_by(|later, earlier| {
let same = later.0 == earlier.0;
if same {
earlier.1 = later.1;
}
same
});
self.ordered = self.holes.is_empty() && self.rows == wanted;
Some(writes)
}
fn afresh(&mut self, wanted: &[u32]) -> Option<Vec<(u32, u32)>> {
for &old in self.rows.iter().filter(|&&r| r != HOLE) {
if let Some(at) = self.at.get_mut(old as usize) {
*at = UNWIRED;
}
}
for (row, &source) in wanted.iter().enumerate() {
if let Some(at) = self.at.get_mut(source as usize) {
*at = row as u32;
}
}
self.rows = wanted.to_vec();
self.holes.clear();
self.ordered = true;
None
}
}
fn remap(map: &[u32], t: u32) -> u32 {
map.get(t as usize).copied().unwrap_or(UNWIRED)
}
fn placed<'a>(source: &'a Data, ask: &'a Ask<'_>) -> Vec<(&'a Table, &'a Planned)> {
let plan = |table: &Table| ask.plans.get(&(source.name().clone(), table.name.clone()));
let mut out: Vec<(&Table, &Planned)> = Vec::new();
for table in source.tables() {
let Some(planned) = plan(table) else { continue };
let named = source.tables().any(|other| {
plan(other).is_some_and(|p| matches!(p.draw, Draw::Tiles { .. }) && ends_of(other, p) == Some(&table.name))
});
if !planned.as_is() || named {
out.push((table, planned));
}
}
out.sort_by_key(|(_, p)| matches!(p.place, Place::Ends(_)));
out
}
fn ends_of<'t>(table: &'t Table, planned: &Planned) -> Option<&'t Name> {
let Place::Ends(fields) = &planned.place else { return None };
match table.field_named(fields.first()?)? {
Field::Reference(reference) => Some(&reference.to),
_ => None,
}
}
fn stamp(source: &Data, ask: &Ask<'_>, placed: &[(&Table, &Planned)]) -> u64 {
let rows: Vec<_> = placed.iter().map(|(t, _)| (t.name.clone(), t.structure, t.order)).collect();
let mut inputs = Vec::new();
for (table, planned) in placed {
let mut fields = Vec::new();
if let Some(priority) = &planned.priority {
fields_of(priority, &mut fields);
}
match &planned.place {
Place::Points(xy) => xy.iter().for_each(|e| fields_of(e, &mut fields)),
Place::Geometry(field) => fields.push(field.clone()),
Place::Ends(ends) => fields.extend(ends.iter().cloned()),
Place::Nowhere => {}
}
let versions: Vec<_> = fields.iter().map(|f| table.field_named(f).map(Field::version)).collect();
let tiles = matches!(planned.draw, Draw::Tiles { .. });
inputs.push((format!("{:?}{:?}", planned.priority, planned.place), versions, tiles, planned.as_is()));
}
stamp_of((source.name(), rows, inputs, ask.earth))
}
fn fields_of(expr: &Expr, out: &mut Vec<Name>) {
match &*expr.0 {
Node::Field(f) if f.of.is_none() => out.push(f.name.clone()),
Node::Unary(_, a) | Node::Is(a, _) => fields_of(a, out),
Node::Binary(_, a, b) => {
fields_of(a, out);
fields_of(b, out);
}
Node::When(c, a, b) => {
fields_of(c, out);
fields_of(a, out);
fields_of(b, out);
}
Node::Scale { input, .. } | Node::Colormap { input, .. } => fields_of(input, out),
_ => {}
}
}
fn sampled_frames(source: &Data, ask: &Ask<'_>) -> Vec<Option<u64>> {
let mut frames = Vec::new();
for table in source.tables() {
let Some(planned) = ask.plans.get(&(source.name().clone(), table.name.clone())) else { continue };
if planned.as_is() {
continue;
}
for (name, field) in &table.fields {
if let Field::Sampled(samples, _) = field
&& planned.reads.as_ref().is_none_or(|r| r.contains(name))
{
frames.push(samples.frame_at(ask.at));
}
}
}
frames
}
fn rows_of(source: &Data) -> Vec<(Version, Version, Vec<Version>)> {
source
.tables()
.map(|table| {
let references = table.fields.iter().filter(|(_, f)| matches!(f, Field::Reference(_)));
(table.structure, table.order, references.map(|(_, f)| f.version()).collect())
})
.collect()
}
fn reads_of(source: &Data, ask: &Ask<'_>) -> Vec<Option<Vec<Name>>> {
source
.tables()
.filter_map(|t| ask.plans.get(&(source.name().clone(), t.name.clone())))
.map(|p| {
p.reads.as_ref().map(|reads| {
let mut reads: Vec<Name> = reads.iter().cloned().collect();
reads.sort();
reads
})
})
.collect()
}
fn bounds_of(places: &Places) -> [f64; 4] {
let mut b = [f64::INFINITY, f64::INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY];
let mut take = |r: [f64; 4]| {
if r.iter().all(|v| v.is_finite()) {
b = [b[0].min(r[0]), b[1].min(r[1]), b[2].max(r[2]), b[3].max(r[3])];
}
};
match places {
Places::None => {}
Places::Points(points) => points.iter().for_each(|p| take([p[0], p[1], p[0], p[1]].map(f64::from))),
Places::Boxes(boxes) => boxes.iter().for_each(|r| take(r.map(f64::from))),
}
b
}
fn locate(table: &Table, place: &Place, all: &[Data]) -> Places {
match place {
Place::Points([x, y]) => {
let rows = Rows::new(table, all);
let (xs, ys) = (rows.numbers(x, |v| v as f32), rows.numbers(y, |v| v as f32));
Places::Points(xs.into_iter().zip(ys).map(|(x, y)| [x, y]).collect())
}
Place::Geometry(field) => {
let Some(Field::Values(column, _)) = table.field_named(field) else { return Places::None };
let mut boxes = Vec::with_capacity(table.len());
for page in column.pages() {
let geometry = page.values.geometry();
for i in 0..page.values.len() {
let mut b = [f64::INFINITY, f64::INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY];
if let Some(g) = geometry.filter(|_| page.values.is_valid(i)) {
for p in g.row_coords(i) {
if p[0].is_finite() && p[1].is_finite() {
b = [b[0].min(p[0]), b[1].min(p[1]), b[2].max(p[0]), b[3].max(p[1])];
}
}
}
boxes.push(b);
}
}
Places::Boxes(boxes.into_iter().map(|b: [f64; 4]| b.map(|v| v as f32)).collect())
}
Place::Ends(_) | Place::Nowhere => Places::None,
}
}
fn leveling(ask: &Ask<'_>, placed: &[(&Table, &Planned)]) -> (Leveling, FxHashMap<Name, [f64; 4]>) {
let names: Vec<Name> = placed.iter().map(|(t, _)| t.name.clone()).collect();
let mut tables: Vec<Placing> = placed
.iter()
.map(|(table, planned)| {
let anchors = matches!(planned.draw, Draw::Tiles { .. }) || planned.as_is();
let places = if anchors { locate(table, &planned.place, ask.sources) } else { Places::None };
Placing { rows: table.len() as u32, answered: !planned.as_is(), places, ..Placing::default() }
})
.collect();
let mut bounds: FxHashMap<Name, [f64; 4]> = FxHashMap::default();
let mut all = [f64::INFINITY, f64::INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY];
for (name, placing) in names.iter().zip(&tables) {
let b = bounds_of(&placing.places);
if b[0] <= b[2] {
all = [all[0].min(b[0]), all[1].min(b[1]), all[2].max(b[2]), all[3].max(b[3])];
bounds.insert(name.clone(), b);
}
}
let domain = if ask.earth { Domain::Earth } else { Domain::plane(all) };
for (i, (table, planned)) in placed.iter().enumerate() {
let placing = &mut tables[i];
if let (Place::Ends(fields), Some(to)) = (&planned.place, ends_of(table, planned))
&& let Some(target) = names.iter().position(|n| n == to)
&& !placed[target].1.place.eq(&Place::Nowhere)
{
let ends: Option<Vec<Arc<[u32]>>> = fields
.iter()
.map(|f| match table.field_named(f) {
Some(Field::Reference(r)) if r.to == *to => Some(r.targets().into()),
_ => None,
})
.collect();
placing.ends = ends.map(|ends| (target as u32, ends));
}
placing.leveled = matches!(planned.draw, Draw::Tiles { .. })
&& (!matches!(placing.places, Places::None) || placing.ends.is_some());
if let Some(priority) = &planned.priority {
placing.priority = Rows::new(table, ask.sources).numbers(priority, |p| p as f32).into();
}
}
(Leveling { domain, tables }, bounds)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rows_still_drawn_keep_their_places_and_arriving_ones_fill_holes() {
let mut slots = Slots::default();
let first: Vec<u32> = (0..20).collect();
assert_eq!(slots.lay(&first, 100, false), None, "laid out afresh");
assert!(slots.ordered);
let kept: Vec<u32> = (0..4).chain(8..20).collect();
let writes = slots.lay(&kept, 100, false).expect("in place");
assert_eq!(writes, (4..8).map(|r| (r, HOLE)).collect::<Vec<_>>());
assert_eq!((slots.rows[3], slots.rows[8], slots.at[9]), (3, 8, 9));
assert!(!slots.ordered, "holes lie among the rows");
let more: Vec<u32> = kept.iter().copied().chain(50..55).collect();
let writes = slots.lay(&more, 100, false).expect("in place");
assert_eq!(writes, [(4, 50), (5, 51), (6, 52), (7, 53), (20, 54)]);
assert_eq!(slots.lay(&more, 100, false), Some(vec![]), "nothing changed, nothing written");
let swapped: Vec<u32> = more.iter().copied().filter(|&r| r != 10).chain([60]).collect();
assert_eq!(slots.lay(&swapped, 100, false), Some(vec![(10, 60)]));
assert_eq!(slots.lay(&[60, 61], 100, false), None);
assert_eq!((slots.rows.as_slice(), slots.ordered, slots.at[10]), (&[60, 61][..], true, UNWIRED));
assert_eq!(slots.lay(&[60, 61], 100, true), None, "and so does asking for it");
}
}