1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! A utility class for implementing "snapshottable" things; a snapshottable data structure permits
12 //! you to take a snapshot (via `start_snapshot`) and then, after making some changes, elect either
13 //! to rollback to the start of the snapshot or commit those changes.
15 //! This vector is intended to be used as part of an abstraction, not serve as a complete
16 //! abstraction on its own. As such, while it will roll back most changes on its own, it also
17 //! supports a `get_mut` operation that gives you an arbitrary mutable pointer into the vector. To
18 //! ensure that any changes you make this with this pointer are rolled back, you must invoke
19 //! `record` to record any changes you make and also supplying a delegate capable of reversing
25 pub enum UndoLog
<D
:SnapshotVecDelegate
> {
26 /// Indicates where a snapshot started.
29 /// Indicates a snapshot that has been committed.
32 /// New variable with given index was created.
35 /// Variable with given index was changed *from* the given value.
36 SetElem(usize, D
::Value
),
38 /// Extensible set of actions
42 pub struct SnapshotVec
<D
:SnapshotVecDelegate
> {
43 values
: Vec
<D
::Value
>,
44 undo_log
: Vec
<UndoLog
<D
>>,
48 // Snapshots are tokens that should be created/consumed linearly.
50 // Length of the undo log at the time the snapshot was taken.
54 pub trait SnapshotVecDelegate
{
58 fn reverse(&mut self, values
: &mut Vec
<Self::Value
>, action
: Self::Undo
);
61 impl<D
:SnapshotVecDelegate
> SnapshotVec
<D
> {
62 pub fn new(delegate
: D
) -> SnapshotVec
<D
> {
70 fn in_snapshot(&self) -> bool
{
71 !self.undo_log
.is_empty()
74 pub fn record(&mut self, action
: D
::Undo
) {
75 if self.in_snapshot() {
76 self.undo_log
.push(Other(action
));
80 pub fn push(&mut self, elem
: D
::Value
) -> usize {
81 let len
= self.values
.len();
82 self.values
.push(elem
);
84 if self.in_snapshot() {
85 self.undo_log
.push(NewElem(len
));
91 pub fn get
<'a
>(&'a
self, index
: usize) -> &'a D
::Value
{
95 /// Returns a mutable pointer into the vec; whatever changes you make here cannot be undone
96 /// automatically, so you should be sure call `record()` with some sort of suitable undo
98 pub fn get_mut
<'a
>(&'a
mut self, index
: usize) -> &'a
mut D
::Value
{
99 &mut self.values
[index
]
102 /// Updates the element at the given index. The old value will saved (and perhaps restored) if
103 /// a snapshot is active.
104 pub fn set(&mut self, index
: usize, new_elem
: D
::Value
) {
105 let old_elem
= mem
::replace(&mut self.values
[index
], new_elem
);
106 if self.in_snapshot() {
107 self.undo_log
.push(SetElem(index
, old_elem
));
111 pub fn start_snapshot(&mut self) -> Snapshot
{
112 let length
= self.undo_log
.len();
113 self.undo_log
.push(OpenSnapshot
);
114 Snapshot { length: length }
117 pub fn actions_since_snapshot(&self,
120 &self.undo_log
[snapshot
.length
..]
123 fn assert_open_snapshot(&self, snapshot
: &Snapshot
) {
124 // Or else there was a failure to follow a stack discipline:
125 assert
!(self.undo_log
.len() > snapshot
.length
);
127 // Invariant established by start_snapshot():
129 match self.undo_log
[snapshot
.length
] {
130 OpenSnapshot
=> true,
135 pub fn rollback_to(&mut self, snapshot
: Snapshot
) {
136 debug
!("rollback_to({})", snapshot
.length
);
138 self.assert_open_snapshot(&snapshot
);
140 while self.undo_log
.len() > snapshot
.length
+ 1 {
141 match self.undo_log
.pop().unwrap() {
143 // This indicates a failure to obey the stack discipline.
144 panic
!("Cannot rollback an uncommitted snapshot");
147 CommittedSnapshot
=> {
148 // This occurs when there are nested snapshots and
149 // the inner is committed but outer is rolled back.
154 assert
!(self.values
.len() == i
);
162 self.delegate
.reverse(&mut self.values
, u
);
167 let v
= self.undo_log
.pop().unwrap();
168 assert
!(match v { OpenSnapshot => true, _ => false }
);
169 assert
!(self.undo_log
.len() == snapshot
.length
);
172 /// Commits all changes since the last snapshot. Of course, they
173 /// can still be undone if there is a snapshot further out.
174 pub fn commit(&mut self, snapshot
: Snapshot
) {
175 debug
!("commit({})", snapshot
.length
);
177 self.assert_open_snapshot(&snapshot
);
179 if snapshot
.length
== 0 {
180 // The root snapshot.
181 self.undo_log
.truncate(0);
183 self.undo_log
[snapshot
.length
] = CommittedSnapshot
;