// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! A utility class for implementing "snapshottable" things; a snapshottable data structure permits //! you to take a snapshot (via `start_snapshot`) and then, after making some changes, elect either //! to rollback to the start of the snapshot or commit those changes. //! //! This vector is intended to be used as part of an abstraction, not serve as a complete //! abstraction on its own. As such, while it will roll back most changes on its own, it also //! supports a `get_mut` operation that gives you an abitrary mutable pointer into the vector. To //! ensure that any changes you make this with this pointer are rolled back, you must invoke //! `record` to record any changes you make and also supplying a delegate capable of reversing //! those changes. use self::UndoLog::*; use std::mem; #[deriving(PartialEq)] pub enum UndoLog { /// Indicates where a snapshot started. OpenSnapshot, /// Indicates a snapshot that has been committed. CommittedSnapshot, /// New variable with given index was created. NewElem(uint), /// Variable with given index was changed *from* the given value. SetElem(uint, T), /// Extensible set of actions Other(U) } pub struct SnapshotVec { values: Vec, undo_log: Vec>, delegate: D } // Snapshots are tokens that should be created/consumed linearly. #[allow(missing_copy_implementations)] pub struct Snapshot { // Length of the undo log at the time the snapshot was taken. length: uint, } pub trait SnapshotVecDelegate { fn reverse(&mut self, values: &mut Vec, action: U); } impl> SnapshotVec { pub fn new(delegate: D) -> SnapshotVec { SnapshotVec { values: Vec::new(), undo_log: Vec::new(), delegate: delegate } } fn in_snapshot(&self) -> bool { !self.undo_log.is_empty() } pub fn record(&mut self, action: U) { if self.in_snapshot() { self.undo_log.push(Other(action)); } } pub fn push(&mut self, elem: T) -> uint { let len = self.values.len(); self.values.push(elem); if self.in_snapshot() { self.undo_log.push(NewElem(len)); } len } pub fn get<'a>(&'a self, index: uint) -> &'a T { &self.values[index] } /// Returns a mutable pointer into the vec; whatever changes you make here cannot be undone /// automatically, so you should be sure call `record()` with some sort of suitable undo /// action. pub fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut T { &mut self.values[index] } /// Updates the element at the given index. The old value will saved (and perhaps restored) if /// a snapshot is active. pub fn set(&mut self, index: uint, new_elem: T) { let old_elem = mem::replace(&mut self.values[index], new_elem); if self.in_snapshot() { self.undo_log.push(SetElem(index, old_elem)); } } pub fn start_snapshot(&mut self) -> Snapshot { let length = self.undo_log.len(); self.undo_log.push(OpenSnapshot); Snapshot { length: length } } pub fn actions_since_snapshot(&self, snapshot: &Snapshot) -> &[UndoLog] { self.undo_log[snapshot.length..] } fn assert_open_snapshot(&self, snapshot: &Snapshot) { // Or else there was a failure to follow a stack discipline: assert!(self.undo_log.len() > snapshot.length); // Invariant established by start_snapshot(): assert!( match self.undo_log[snapshot.length] { OpenSnapshot => true, _ => false }); } pub fn rollback_to(&mut self, snapshot: Snapshot) { debug!("rollback_to({})", snapshot.length); self.assert_open_snapshot(&snapshot); while self.undo_log.len() > snapshot.length + 1 { match self.undo_log.pop().unwrap() { OpenSnapshot => { // This indicates a failure to obey the stack discipline. panic!("Cannot rollback an uncommitted snapshot"); } CommittedSnapshot => { // This occurs when there are nested snapshots and // the inner is committed but outer is rolled back. } NewElem(i) => { self.values.pop(); assert!(self.values.len() == i); } SetElem(i, v) => { self.values[i] = v; } Other(u) => { self.delegate.reverse(&mut self.values, u); } } } let v = self.undo_log.pop().unwrap(); assert!(match v { OpenSnapshot => true, _ => false }); assert!(self.undo_log.len() == snapshot.length); } /// Commits all changes since the last snapshot. Of course, they /// can still be undone if there is a snapshot further out. pub fn commit(&mut self, snapshot: Snapshot) { debug!("commit({})", snapshot.length); self.assert_open_snapshot(&snapshot); if snapshot.length == 0 { // The root snapshot. self.undo_log.truncate(0); } else { self.undo_log[snapshot.length] = CommittedSnapshot; } } }