use crate::numeric_id::{DenseIdMap, define_id};
use crate::{Subset, Value};
use super::{AtomId, Variable};
define_id!(pub SubsetId, u32, "An offset into a buffer of subsets");
enum UpdateCell {
PushBinding(Variable, Value),
RefineAtom(AtomId, SubsetId),
EndFrame,
}
pub(super) enum UpdateInstr {
PushBinding(Variable, Value),
RefineAtom(AtomId, Subset),
EndFrame,
}
#[derive(Default)]
pub(super) struct FrameUpdates {
subsets: DenseIdMap<SubsetId, Subset>,
updates: Vec<UpdateCell>,
frames: usize,
last_start: usize,
}
impl FrameUpdates {
pub(super) fn with_capacity(capacity: usize) -> FrameUpdates {
FrameUpdates {
subsets: DenseIdMap::with_capacity(capacity),
updates: Vec::with_capacity(capacity * 2),
frames: 0,
last_start: 0,
}
}
pub(super) fn push_binding(&mut self, var: Variable, val: Value) {
self.updates.push(UpdateCell::PushBinding(var, val));
}
pub(super) fn refine_atom(&mut self, atom: AtomId, subset: Subset) {
let subset = self.subsets.push(subset);
self.updates.push(UpdateCell::RefineAtom(atom, subset));
}
pub(super) fn rollback(&mut self) {
self.updates.truncate(self.last_start);
}
pub(super) fn finish_frame(&mut self) {
self.updates.push(UpdateCell::EndFrame);
self.last_start = self.updates.len();
self.frames += 1;
}
pub(super) fn frames(&self) -> usize {
self.frames
}
pub(super) fn clear(&mut self) {
self.subsets.clear();
self.updates.clear();
}
pub(super) fn drain(&mut self, f: impl FnMut(UpdateInstr)) {
let start = if matches!(self.updates.first(), Some(UpdateCell::EndFrame)) {
1 } else {
0
};
self.updates
.drain(start..)
.map(|cell| match cell {
UpdateCell::PushBinding(var, val) => UpdateInstr::PushBinding(var, val),
UpdateCell::RefineAtom(atom, subset) => {
UpdateInstr::RefineAtom(atom, self.subsets.take(subset).unwrap())
}
UpdateCell::EndFrame => UpdateInstr::EndFrame,
})
.for_each(f);
self.subsets.clear();
self.frames = 0;
self.last_start = 0;
}
}