blob: 2537826c97e0fd02f8525c0146cbd52be68e0c88 [file] [log] [blame]
#[macro_use]
extern crate log;
extern crate ena;
use ena::{
snapshot_vec as sv,
undo_log::{Rollback, Snapshots, UndoLogs},
unify::{self as ut, EqUnifyValue, UnifyKey},
};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
struct IntKey(u32);
impl UnifyKey for IntKey {
type Value = Option<IntKey>;
fn index(&self) -> u32 {
self.0
}
fn from_index(u: u32) -> IntKey {
IntKey(u)
}
fn tag() -> &'static str {
"IntKey"
}
}
impl EqUnifyValue for IntKey {}
enum UndoLog {
EqRelation(sv::UndoLog<ut::Delegate<IntKey>>),
Values(sv::UndoLog<i32>),
}
impl From<sv::UndoLog<ut::Delegate<IntKey>>> for UndoLog {
fn from(l: sv::UndoLog<ut::Delegate<IntKey>>) -> Self {
UndoLog::EqRelation(l)
}
}
impl From<sv::UndoLog<i32>> for UndoLog {
fn from(l: sv::UndoLog<i32>) -> Self {
UndoLog::Values(l)
}
}
impl Rollback<UndoLog> for TypeVariableStorage {
fn reverse(&mut self, undo: UndoLog) {
match undo {
UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo),
UndoLog::Values(undo) => self.values.reverse(undo),
}
}
}
#[derive(Default)]
struct TypeVariableStorage {
values: sv::SnapshotVecStorage<i32>,
eq_relations: ut::UnificationTableStorage<IntKey>,
}
impl TypeVariableStorage {
fn with_log<'a>(&'a mut self, undo_log: &'a mut TypeVariableUndoLogs) -> TypeVariableTable<'a> {
TypeVariableTable {
storage: self,
undo_log,
}
}
fn len(&mut self) -> usize {
assert_eq!(self.values.len(), self.eq_relations.len());
self.values.len()
}
}
struct TypeVariableTable<'a> {
storage: &'a mut TypeVariableStorage,
undo_log: &'a mut TypeVariableUndoLogs,
}
impl TypeVariableTable<'_> {
fn new(&mut self, i: i32) -> IntKey {
self.storage.values.with_log(&mut self.undo_log).push(i);
self.storage
.eq_relations
.with_log(&mut self.undo_log)
.new_key(None)
}
}
struct Snapshot {
undo_len: usize,
}
struct TypeVariableUndoLogs {
logs: Vec<UndoLog>,
num_open_snapshots: usize,
}
impl Default for TypeVariableUndoLogs {
fn default() -> Self {
Self {
logs: Default::default(),
num_open_snapshots: Default::default(),
}
}
}
impl<T> UndoLogs<T> for TypeVariableUndoLogs
where
UndoLog: From<T>,
{
fn num_open_snapshots(&self) -> usize {
self.num_open_snapshots
}
fn push(&mut self, undo: T) {
if self.in_snapshot() {
self.logs.push(undo.into())
}
}
fn clear(&mut self) {
self.logs.clear();
self.num_open_snapshots = 0;
}
fn extend<J>(&mut self, undos: J)
where
Self: Sized,
J: IntoIterator<Item = T>,
{
if self.in_snapshot() {
self.logs.extend(undos.into_iter().map(UndoLog::from))
}
}
}
impl Snapshots<UndoLog> for TypeVariableUndoLogs {
type Snapshot = Snapshot;
fn actions_since_snapshot(&self, snapshot: &Self::Snapshot) -> &[UndoLog] {
&self.logs[snapshot.undo_len..]
}
fn start_snapshot(&mut self) -> Self::Snapshot {
self.num_open_snapshots += 1;
Snapshot {
undo_len: self.logs.len(),
}
}
fn rollback_to<R>(&mut self, values: impl FnOnce() -> R, snapshot: Self::Snapshot)
where
R: Rollback<UndoLog>,
{
debug!("rollback_to({})", snapshot.undo_len);
if self.logs.len() > snapshot.undo_len {
let mut values = values();
while self.logs.len() > snapshot.undo_len {
values.reverse(self.logs.pop().unwrap());
}
}
if self.num_open_snapshots == 1 {
// The root snapshot. It's safe to clear the undo log because
// there's no snapshot further out that we might need to roll back
// to.
assert!(snapshot.undo_len == 0);
self.logs.clear();
}
self.num_open_snapshots -= 1;
}
fn commit(&mut self, snapshot: Self::Snapshot) {
debug!("commit({})", snapshot.undo_len);
if self.num_open_snapshots == 1 {
// The root snapshot. It's safe to clear the undo log because
// there's no snapshot further out that we might need to roll back
// to.
assert!(snapshot.undo_len == 0);
self.logs.clear();
}
self.num_open_snapshots -= 1;
}
}
/// Tests that a undo log stored externally can be used with TypeVariableTable
#[test]
fn external_undo_log() {
let mut storage = TypeVariableStorage::default();
let mut undo_log = TypeVariableUndoLogs::default();
let snapshot = undo_log.start_snapshot();
storage.with_log(&mut undo_log).new(1);
storage.with_log(&mut undo_log).new(2);
assert_eq!(storage.len(), 2);
undo_log.rollback_to(|| &mut storage, snapshot);
assert_eq!(storage.len(), 0);
}