Skip to content

Commit f9c0061

Browse files
authored
Merge pull request #14 from nnethercote/rm-OpenSnapshot-CommittedSnapshot
Remove `OpenSnapshot` and `CommittedSnapshot` markers.
2 parents 31c5efb + 2c162ce commit f9c0061

File tree

1 file changed

+88
-40
lines changed

1 file changed

+88
-40
lines changed

src/snapshot_vec.rs

+88-40
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@ use std::ops;
2727

2828
#[derive(Debug)]
2929
pub enum UndoLog<D: SnapshotVecDelegate> {
30-
/// Indicates where a snapshot started.
31-
OpenSnapshot,
32-
33-
/// Indicates a snapshot that has been committed.
34-
CommittedSnapshot,
35-
3630
/// New variable with given index was created.
3731
NewElem(usize),
3832

@@ -46,6 +40,7 @@ pub enum UndoLog<D: SnapshotVecDelegate> {
4640
pub struct SnapshotVec<D: SnapshotVecDelegate> {
4741
values: Vec<D::Value>,
4842
undo_log: Vec<UndoLog<D>>,
43+
num_open_snapshots: usize,
4944
}
5045

5146
impl<D> fmt::Debug for SnapshotVec<D>
@@ -58,6 +53,7 @@ impl<D> fmt::Debug for SnapshotVec<D>
5853
fmt.debug_struct("SnapshotVec")
5954
.field("values", &self.values)
6055
.field("undo_log", &self.undo_log)
56+
.field("num_open_snapshots", &self.num_open_snapshots)
6157
.finish()
6258
}
6359
}
@@ -81,6 +77,7 @@ impl<D: SnapshotVecDelegate> Default for SnapshotVec<D> {
8177
SnapshotVec {
8278
values: Vec::new(),
8379
undo_log: Vec::new(),
80+
num_open_snapshots: 0,
8481
}
8582
}
8683
}
@@ -94,11 +91,12 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
9491
SnapshotVec {
9592
values: Vec::with_capacity(c),
9693
undo_log: Vec::new(),
94+
num_open_snapshots: 0,
9795
}
9896
}
9997

10098
fn in_snapshot(&self) -> bool {
101-
!self.undo_log.is_empty()
99+
self.num_open_snapshots > 0
102100
}
103101

104102
pub fn record(&mut self, action: D::Undo) {
@@ -176,7 +174,7 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
176174

177175
pub fn start_snapshot(&mut self) -> Snapshot {
178176
let length = self.undo_log.len();
179-
self.undo_log.push(OpenSnapshot);
177+
self.num_open_snapshots += 1;
180178
Snapshot { length: length }
181179
}
182180

@@ -185,33 +183,18 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
185183
}
186184

187185
fn assert_open_snapshot(&self, snapshot: &Snapshot) {
188-
// Or else there was a failure to follow a stack discipline:
189-
assert!(self.undo_log.len() > snapshot.length);
190-
191-
// Invariant established by start_snapshot():
192-
assert!(match self.undo_log[snapshot.length] {
193-
OpenSnapshot => true,
194-
_ => false,
195-
});
186+
// Failures here may indicate a failure to follow a stack discipline.
187+
assert!(self.undo_log.len() >= snapshot.length);
188+
assert!(self.num_open_snapshots > 0);
196189
}
197190

198191
pub fn rollback_to(&mut self, snapshot: Snapshot) {
199192
debug!("rollback_to({})", snapshot.length);
200193

201194
self.assert_open_snapshot(&snapshot);
202195

203-
while self.undo_log.len() > snapshot.length + 1 {
196+
while self.undo_log.len() > snapshot.length {
204197
match self.undo_log.pop().unwrap() {
205-
OpenSnapshot => {
206-
// This indicates a failure to obey the stack discipline.
207-
panic!("Cannot rollback an uncommitted snapshot");
208-
}
209-
210-
CommittedSnapshot => {
211-
// This occurs when there are nested snapshots and
212-
// the inner is committed but outer is rolled back.
213-
}
214-
215198
NewElem(i) => {
216199
self.values.pop();
217200
assert!(self.values.len() == i);
@@ -227,12 +210,7 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
227210
}
228211
}
229212

230-
let v = self.undo_log.pop().unwrap();
231-
assert!(match v {
232-
OpenSnapshot => true,
233-
_ => false,
234-
});
235-
assert!(self.undo_log.len() == snapshot.length);
213+
self.num_open_snapshots -= 1;
236214
}
237215

238216
/// Commits all changes since the last snapshot. Of course, they
@@ -242,12 +220,15 @@ impl<D: SnapshotVecDelegate> SnapshotVec<D> {
242220

243221
self.assert_open_snapshot(&snapshot);
244222

245-
if snapshot.length == 0 {
246-
// The root snapshot.
247-
self.undo_log.truncate(0);
248-
} else {
249-
self.undo_log[snapshot.length] = CommittedSnapshot;
223+
if self.num_open_snapshots == 1 {
224+
// The root snapshot. It's safe to clear the undo log because
225+
// there's no snapshot further out that we might need to roll back
226+
// to.
227+
assert!(snapshot.length == 0);
228+
self.undo_log.clear();
250229
}
230+
231+
self.num_open_snapshots -= 1;
251232
}
252233
}
253234

@@ -301,6 +282,7 @@ where
301282
SnapshotVec {
302283
values: self.values.clone(),
303284
undo_log: self.undo_log.clone(),
285+
num_open_snapshots: self.num_open_snapshots,
304286
}
305287
}
306288
}
@@ -312,11 +294,77 @@ where
312294
{
313295
fn clone(&self) -> Self {
314296
match *self {
315-
OpenSnapshot => OpenSnapshot,
316-
CommittedSnapshot => CommittedSnapshot,
317297
NewElem(i) => NewElem(i),
318298
SetElem(i, ref v) => SetElem(i, v.clone()),
319299
Other(ref u) => Other(u.clone()),
320300
}
321301
}
322302
}
303+
304+
impl SnapshotVecDelegate for i32 {
305+
type Value = i32;
306+
type Undo = ();
307+
308+
fn reverse(_: &mut Vec<i32>, _: ()) {}
309+
}
310+
311+
#[test]
312+
fn basic() {
313+
let mut vec: SnapshotVec<i32> = SnapshotVec::default();
314+
assert!(!vec.in_snapshot());
315+
assert_eq!(vec.len(), 0);
316+
vec.push(22);
317+
vec.push(33);
318+
assert_eq!(vec.len(), 2);
319+
assert_eq!(*vec.get(0), 22);
320+
assert_eq!(*vec.get(1), 33);
321+
vec.set(1, 34);
322+
assert_eq!(vec.len(), 2);
323+
assert_eq!(*vec.get(0), 22);
324+
assert_eq!(*vec.get(1), 34);
325+
326+
let snapshot = vec.start_snapshot();
327+
assert!(vec.in_snapshot());
328+
329+
vec.push(44);
330+
vec.push(55);
331+
vec.set(1, 35);
332+
assert_eq!(vec.len(), 4);
333+
assert_eq!(*vec.get(0), 22);
334+
assert_eq!(*vec.get(1), 35);
335+
assert_eq!(*vec.get(2), 44);
336+
assert_eq!(*vec.get(3), 55);
337+
338+
vec.rollback_to(snapshot);
339+
assert!(!vec.in_snapshot());
340+
341+
assert_eq!(vec.len(), 2);
342+
assert_eq!(*vec.get(0), 22);
343+
assert_eq!(*vec.get(1), 34);
344+
}
345+
346+
#[test]
347+
#[should_panic]
348+
fn out_of_order() {
349+
let mut vec: SnapshotVec<i32> = SnapshotVec::default();
350+
vec.push(22);
351+
let snapshot1 = vec.start_snapshot();
352+
vec.push(33);
353+
let snapshot2 = vec.start_snapshot();
354+
vec.push(44);
355+
vec.rollback_to(snapshot1); // bogus, but accepted
356+
vec.rollback_to(snapshot2); // asserts
357+
}
358+
359+
#[test]
360+
fn nested_commit_then_rollback() {
361+
let mut vec: SnapshotVec<i32> = SnapshotVec::default();
362+
vec.push(22);
363+
let snapshot1 = vec.start_snapshot();
364+
let snapshot2 = vec.start_snapshot();
365+
vec.set(0, 23);
366+
vec.commit(snapshot2);
367+
assert_eq!(*vec.get(0), 23);
368+
vec.rollback_to(snapshot1);
369+
assert_eq!(*vec.get(0), 22);
370+
}

0 commit comments

Comments
 (0)