// 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 arbitrary 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::fmt; use std::marker::PhantomData; use std::mem; use std::ops; use undo_log::{Rollback, Snapshots, UndoLogs, VecLog}; #[derive(Debug)] pub enum UndoLog { /// New variable with given index was created. NewElem(usize), /// Variable with given index was changed *from* the given value. SetElem(usize, D::Value), /// Extensible set of actions Other(D::Undo), } impl Rollback> for SnapshotVecStorage { fn reverse(&mut self, undo: UndoLog) { self.values.reverse(undo) } } impl Rollback> for Vec { fn reverse(&mut self, undo: UndoLog) { match undo { NewElem(i) => { self.pop(); assert!(Vec::len(self) == i); } SetElem(i, v) => { self[i] = v; } Other(u) => { D::reverse(self, u); } } } } pub trait VecLike: AsRef<[D::Value]> + AsMut<[D::Value]> + Rollback> where D: SnapshotVecDelegate, { fn push(&mut self, item: D::Value); fn len(&self) -> usize; fn reserve(&mut self, size: usize); } impl VecLike for Vec where D: SnapshotVecDelegate, { fn push(&mut self, item: D::Value) { Vec::push(self, item) } fn len(&self) -> usize { Vec::len(self) } fn reserve(&mut self, size: usize) { Vec::reserve(self, size) } } impl VecLike for &'_ mut Vec where D: SnapshotVecDelegate, { fn push(&mut self, item: D::Value) { Vec::push(self, item) } fn len(&self) -> usize { Vec::len(self) } fn reserve(&mut self, size: usize) { Vec::reserve(self, size) } } #[allow(type_alias_bounds)] pub type SnapshotVecStorage = SnapshotVec::Value>, ()>; pub struct SnapshotVec< D: SnapshotVecDelegate, V: VecLike = Vec<::Value>, L = VecLog>, > { values: V, undo_log: L, _marker: PhantomData, } impl fmt::Debug for SnapshotVec where D: SnapshotVecDelegate, V: VecLike + fmt::Debug, L: fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("SnapshotVec") .field("values", &self.values) .field("undo_log", &self.undo_log) .finish() } } // Snapshots are tokens that should be created/consumed linearly. pub struct Snapshot { pub(crate) value_count: usize, snapshot: S, } pub trait SnapshotVecDelegate { type Value; type Undo; fn reverse(values: &mut Vec, action: Self::Undo); } // HACK(eddyb) manual impl avoids `Default` bound on `D`. impl + Default, L: Default> Default for SnapshotVec { fn default() -> Self { SnapshotVec { values: V::default(), undo_log: Default::default(), _marker: PhantomData, } } } impl + Default, L: Default> SnapshotVec { /// Creates a new `SnapshotVec`. If `L` is set to `()` then most mutating functions will not /// be accessible without calling `with_log` and supplying a compatibly `UndoLogs` instance. pub fn new() -> Self { Self::default() } } impl SnapshotVecStorage { /// Creates a `SnapshotVec` using the `undo_log`, allowing mutating methods to be called pub fn with_log<'a, L>( &'a mut self, undo_log: L, ) -> SnapshotVec::Value>, L> where L: UndoLogs>, { SnapshotVec { values: &mut self.values, undo_log, _marker: PhantomData, } } } impl SnapshotVec, L> { pub fn with_capacity(c: usize) -> Self { SnapshotVec { values: Vec::with_capacity(c), undo_log: Default::default(), _marker: PhantomData, } } } impl, D: SnapshotVecDelegate, U> SnapshotVec { pub fn len(&self) -> usize { self.values.len() } pub fn get(&self, index: usize) -> &D::Value { &self.values.as_ref()[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(&mut self, index: usize) -> &mut D::Value { &mut self.values.as_mut()[index] } /// Reserve space for new values, just like an ordinary vec. pub fn reserve(&mut self, additional: usize) { // This is not affected by snapshots or anything. self.values.reserve(additional); } } impl, D: SnapshotVecDelegate, L: UndoLogs>> SnapshotVec { fn in_snapshot(&self) -> bool { self.undo_log.in_snapshot() } pub fn record(&mut self, action: D::Undo) { if self.in_snapshot() { self.undo_log.push(Other(action)); } } pub fn push(&mut self, elem: D::Value) -> usize { let len = self.values.len(); self.values.push(elem); if self.in_snapshot() { self.undo_log.push(NewElem(len)); } len } /// 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: usize, new_elem: D::Value) { let old_elem = mem::replace(&mut self.values.as_mut()[index], new_elem); if self.undo_log.in_snapshot() { self.undo_log.push(SetElem(index, old_elem)); } } /// Updates all elements. Potentially more efficient -- but /// otherwise equivalent to -- invoking `set` for each element. pub fn set_all(&mut self, mut new_elems: impl FnMut(usize) -> D::Value) { if !self.undo_log.in_snapshot() { for (index, slot) in self.values.as_mut().iter_mut().enumerate() { *slot = new_elems(index); } } else { for i in 0..self.values.len() { self.set(i, new_elems(i)); } } } pub fn update(&mut self, index: usize, op: OP) where OP: FnOnce(&mut D::Value), D::Value: Clone, { if self.undo_log.in_snapshot() { let old_elem = self.values.as_mut()[index].clone(); self.undo_log.push(SetElem(index, old_elem)); } op(&mut self.values.as_mut()[index]); } } impl SnapshotVec where D: SnapshotVecDelegate, V: VecLike + Rollback>, L: Snapshots>, { pub fn start_snapshot(&mut self) -> Snapshot { Snapshot { value_count: self.values.len(), snapshot: self.undo_log.start_snapshot(), } } pub fn actions_since_snapshot(&self, snapshot: &Snapshot) -> &[UndoLog] { self.undo_log.actions_since_snapshot(&snapshot.snapshot) } pub fn rollback_to(&mut self, snapshot: Snapshot) { let values = &mut self.values; self.undo_log.rollback_to(|| values, snapshot.snapshot); } /// 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) { self.undo_log.commit(snapshot.snapshot); } } impl, L> ops::Deref for SnapshotVec { type Target = [D::Value]; fn deref(&self) -> &[D::Value] { self.values.as_ref() } } impl, L> ops::DerefMut for SnapshotVec { fn deref_mut(&mut self) -> &mut [D::Value] { self.values.as_mut() } } impl, L> ops::Index for SnapshotVec { type Output = D::Value; fn index(&self, index: usize) -> &D::Value { self.get(index) } } impl, L> ops::IndexMut for SnapshotVec { fn index_mut(&mut self, index: usize) -> &mut D::Value { self.get_mut(index) } } impl + Extend> Extend for SnapshotVec { fn extend(&mut self, iterable: T) where T: IntoIterator, { let initial_len = self.values.len(); self.values.extend(iterable); let final_len = self.values.len(); if self.in_snapshot() { self.undo_log .extend((initial_len..final_len).map(|len| NewElem(len))); } } } impl Clone for SnapshotVec where V: VecLike + Clone, L: Clone, { fn clone(&self) -> Self { SnapshotVec { values: self.values.clone(), undo_log: self.undo_log.clone(), _marker: PhantomData, } } } impl Clone for UndoLog where D::Value: Clone, D::Undo: Clone, { fn clone(&self) -> Self { match *self { NewElem(i) => NewElem(i), SetElem(i, ref v) => SetElem(i, v.clone()), Other(ref u) => Other(u.clone()), } } } impl SnapshotVecDelegate for i32 { type Value = i32; type Undo = (); fn reverse(_: &mut Vec, _: ()) {} } #[test] fn basic() { let mut vec: SnapshotVec = SnapshotVec::default(); assert!(!vec.in_snapshot()); assert_eq!(vec.len(), 0); vec.push(22); vec.push(33); assert_eq!(vec.len(), 2); assert_eq!(*vec.get(0), 22); assert_eq!(*vec.get(1), 33); vec.set(1, 34); assert_eq!(vec.len(), 2); assert_eq!(*vec.get(0), 22); assert_eq!(*vec.get(1), 34); let snapshot = vec.start_snapshot(); assert!(vec.in_snapshot()); vec.push(44); vec.push(55); vec.set(1, 35); assert_eq!(vec.len(), 4); assert_eq!(*vec.get(0), 22); assert_eq!(*vec.get(1), 35); assert_eq!(*vec.get(2), 44); assert_eq!(*vec.get(3), 55); vec.rollback_to(snapshot); assert!(!vec.in_snapshot()); assert_eq!(vec.len(), 2); assert_eq!(*vec.get(0), 22); assert_eq!(*vec.get(1), 34); } #[test] #[should_panic] fn out_of_order() { let mut vec: SnapshotVec = SnapshotVec::default(); vec.push(22); let snapshot1 = vec.start_snapshot(); vec.push(33); let snapshot2 = vec.start_snapshot(); vec.push(44); vec.rollback_to(snapshot1); // bogus, but accepted vec.rollback_to(snapshot2); // asserts } #[test] fn nested_commit_then_rollback() { let mut vec: SnapshotVec = SnapshotVec::default(); vec.push(22); let snapshot1 = vec.start_snapshot(); let snapshot2 = vec.start_snapshot(); vec.set(0, 23); vec.commit(snapshot2); assert_eq!(*vec.get(0), 23); vec.rollback_to(snapshot1); assert_eq!(*vec.get(0), 22); }