{
map: FxHashMap<K, V>,
undo_log: Vec<UndoLog<K, V>>,
+ num_open_snapshots: usize,
}
// HACK(eddyb) manual impl avoids `Default` bounds on `K` and `V`.
SnapshotMap {
map: Default::default(),
undo_log: Default::default(),
+ num_open_snapshots: 0,
}
}
}
}
enum UndoLog<K, V> {
- OpenSnapshot,
- CommittedSnapshot,
Inserted(K),
Overwrite(K, V),
Purged,
pub fn clear(&mut self) {
self.map.clear();
self.undo_log.clear();
+ self.num_open_snapshots = 0;
}
fn in_snapshot(&self) -> bool {
- !self.undo_log.is_empty()
+ self.num_open_snapshots > 0
}
pub fn insert(&mut self, key: K, value: V) -> bool {
}
pub fn snapshot(&mut self) -> Snapshot {
- self.undo_log.push(UndoLog::OpenSnapshot);
- let len = self.undo_log.len() - 1;
+ let len = self.undo_log.len();
+ self.num_open_snapshots += 1;
Snapshot { len }
}
fn assert_open_snapshot(&self, snapshot: &Snapshot) {
- assert!(snapshot.len < self.undo_log.len());
- assert!(match self.undo_log[snapshot.len] {
- UndoLog::OpenSnapshot => true,
- _ => false,
- });
+ assert!(self.undo_log.len() >= snapshot.len);
+ assert!(self.num_open_snapshots > 0);
}
pub fn commit(&mut self, snapshot: Snapshot) {
self.assert_open_snapshot(&snapshot);
- if snapshot.len == 0 {
- // The root snapshot.
+ 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.len == 0);
self.undo_log.clear();
- } else {
- self.undo_log[snapshot.len] = UndoLog::CommittedSnapshot;
}
+
+ self.num_open_snapshots -= 1;
}
pub fn partial_rollback<F>(&mut self,
where F: Fn(&K) -> bool
{
self.assert_open_snapshot(snapshot);
- for i in (snapshot.len + 1..self.undo_log.len()).rev() {
+ for i in (snapshot.len .. self.undo_log.len()).rev() {
let reverse = match self.undo_log[i] {
- UndoLog::OpenSnapshot => false,
- UndoLog::CommittedSnapshot => false,
UndoLog::Purged => false,
UndoLog::Inserted(ref k) => should_revert_key(k),
UndoLog::Overwrite(ref k, _) => should_revert_key(k),
pub fn rollback_to(&mut self, snapshot: Snapshot) {
self.assert_open_snapshot(&snapshot);
- while self.undo_log.len() > snapshot.len + 1 {
+ while self.undo_log.len() > snapshot.len {
let entry = self.undo_log.pop().unwrap();
self.reverse(entry);
}
- let v = self.undo_log.pop().unwrap();
- assert!(match v {
- UndoLog::OpenSnapshot => true,
- _ => false,
- });
- assert!(self.undo_log.len() == snapshot.len);
+ self.num_open_snapshots -= 1;
}
fn reverse(&mut self, entry: UndoLog<K, V>) {
match entry {
- UndoLog::OpenSnapshot => {
- panic!("cannot rollback an uncommitted snapshot");
- }
-
- UndoLog::CommittedSnapshot => {}
-
UndoLog::Inserted(key) => {
self.map.remove(&key);
}
let snapshot = map.snapshot();
map.insert(22, "thirty-three");
assert_eq!(map[&22], "thirty-three");
- map.insert(44, "fourty-four");
- assert_eq!(map[&44], "fourty-four");
+ map.insert(44, "forty-four");
+ assert_eq!(map[&44], "forty-four");
assert_eq!(map.get(&33), None);
map.rollback_to(snapshot);
assert_eq!(map[&22], "twenty-two");
let mut map = SnapshotMap::default();
map.insert(22, "twenty-two");
let snapshot1 = map.snapshot();
- let _snapshot2 = map.snapshot();
- map.rollback_to(snapshot1);
+ map.insert(33, "thirty-three");
+ let snapshot2 = map.snapshot();
+ map.insert(44, "forty-four");
+ map.rollback_to(snapshot1); // bogus, but accepted
+ map.rollback_to(snapshot2); // asserts
}
#[test]