2019-02-08 16:36:22 +00:00
|
|
|
use crate::fx::FxHashMap;
|
2016-05-21 12:18:09 +00:00
|
|
|
use std::hash::Hash;
|
2016-10-19 01:32:31 +00:00
|
|
|
use std::mem;
|
2019-12-22 22:42:04 +00:00
|
|
|
use std::ops;
|
2016-05-21 12:18:09 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
2019-08-01 20:57:23 +00:00
|
|
|
mod tests;
|
2016-05-21 12:18:09 +00:00
|
|
|
|
|
|
|
pub struct SnapshotMap<K, V>
|
2019-12-22 22:42:04 +00:00
|
|
|
where
|
|
|
|
K: Clone + Eq,
|
2016-05-21 12:18:09 +00:00
|
|
|
{
|
2016-11-08 03:02:55 +00:00
|
|
|
map: FxHashMap<K, V>,
|
2016-05-21 12:18:09 +00:00
|
|
|
undo_log: Vec<UndoLog<K, V>>,
|
2018-11-04 23:59:33 +00:00
|
|
|
num_open_snapshots: usize,
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
|
2018-07-25 12:44:06 +00:00
|
|
|
// HACK(eddyb) manual impl avoids `Default` bounds on `K` and `V`.
|
|
|
|
impl<K, V> Default for SnapshotMap<K, V>
|
2019-12-22 22:42:04 +00:00
|
|
|
where
|
|
|
|
K: Hash + Clone + Eq,
|
2018-07-25 12:44:06 +00:00
|
|
|
{
|
|
|
|
fn default() -> Self {
|
2019-12-22 22:42:04 +00:00
|
|
|
SnapshotMap { map: Default::default(), undo_log: Default::default(), num_open_snapshots: 0 }
|
2018-07-25 12:44:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-21 12:18:09 +00:00
|
|
|
pub struct Snapshot {
|
2016-10-19 19:10:05 +00:00
|
|
|
len: usize,
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
enum UndoLog<K, V> {
|
|
|
|
Inserted(K),
|
|
|
|
Overwrite(K, V),
|
2018-11-04 22:28:12 +00:00
|
|
|
Purged,
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
|
2018-10-16 14:57:53 +00:00
|
|
|
impl<K, V> SnapshotMap<K, V>
|
2019-12-22 22:42:04 +00:00
|
|
|
where
|
|
|
|
K: Hash + Clone + Eq,
|
2018-10-16 14:57:53 +00:00
|
|
|
{
|
Generate documentation for auto-trait impls
A new section is added to both both struct and trait doc pages.
On struct/enum pages, a new 'Auto Trait Implementations' section displays any
synthetic implementations for auto traits. Currently, this is only done
for Send and Sync.
On trait pages, a new 'Auto Implementors' section displays all types
which automatically implement the trait. Effectively, this is a list of
all public types in the standard library.
Synthesized impls for a particular auto trait ('synthetic impls') take
into account generic bounds. For example, a type 'struct Foo<T>(T)' will
have 'impl<T> Send for Foo<T> where T: Send' generated for it.
Manual implementations of auto traits are also taken into account. If we have
the following types:
'struct Foo<T>(T)'
'struct Wrapper<T>(Foo<T>)'
'unsafe impl<T> Send for Wrapper<T>' // pretend that Wrapper<T> makes
this sound somehow
Then Wrapper will have the following impl generated:
'impl<T> Send for Wrapper<T>'
reflecting the fact that 'T: Send' need not hold for 'Wrapper<T>: Send'
to hold
Lifetimes, HRTBS, and projections (e.g. '<T as Iterator>::Item') are
taken into account by synthetic impls
However, if a type can *never* implement a particular auto trait
(e.g. 'struct MyStruct<T>(*const T)'), then a negative impl will be
generated (in this case, 'impl<T> !Send for MyStruct<T>')
All of this means that a user should be able to copy-paste a synthetic
impl into their code, without any observable changes in behavior
(assuming the rest of the program remains unchanged).
2017-11-22 21:16:55 +00:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.map.clear();
|
|
|
|
self.undo_log.clear();
|
2018-11-04 23:59:33 +00:00
|
|
|
self.num_open_snapshots = 0;
|
Generate documentation for auto-trait impls
A new section is added to both both struct and trait doc pages.
On struct/enum pages, a new 'Auto Trait Implementations' section displays any
synthetic implementations for auto traits. Currently, this is only done
for Send and Sync.
On trait pages, a new 'Auto Implementors' section displays all types
which automatically implement the trait. Effectively, this is a list of
all public types in the standard library.
Synthesized impls for a particular auto trait ('synthetic impls') take
into account generic bounds. For example, a type 'struct Foo<T>(T)' will
have 'impl<T> Send for Foo<T> where T: Send' generated for it.
Manual implementations of auto traits are also taken into account. If we have
the following types:
'struct Foo<T>(T)'
'struct Wrapper<T>(Foo<T>)'
'unsafe impl<T> Send for Wrapper<T>' // pretend that Wrapper<T> makes
this sound somehow
Then Wrapper will have the following impl generated:
'impl<T> Send for Wrapper<T>'
reflecting the fact that 'T: Send' need not hold for 'Wrapper<T>: Send'
to hold
Lifetimes, HRTBS, and projections (e.g. '<T as Iterator>::Item') are
taken into account by synthetic impls
However, if a type can *never* implement a particular auto trait
(e.g. 'struct MyStruct<T>(*const T)'), then a negative impl will be
generated (in this case, 'impl<T> !Send for MyStruct<T>')
All of this means that a user should be able to copy-paste a synthetic
impl into their code, without any observable changes in behavior
(assuming the rest of the program remains unchanged).
2017-11-22 21:16:55 +00:00
|
|
|
}
|
|
|
|
|
2018-11-04 23:44:52 +00:00
|
|
|
fn in_snapshot(&self) -> bool {
|
2018-11-04 23:59:33 +00:00
|
|
|
self.num_open_snapshots > 0
|
2018-11-04 23:44:52 +00:00
|
|
|
}
|
|
|
|
|
2016-05-21 12:18:09 +00:00
|
|
|
pub fn insert(&mut self, key: K, value: V) -> bool {
|
|
|
|
match self.map.insert(key.clone(), value) {
|
|
|
|
None => {
|
2018-11-04 23:44:52 +00:00
|
|
|
if self.in_snapshot() {
|
2016-05-21 12:18:09 +00:00
|
|
|
self.undo_log.push(UndoLog::Inserted(key));
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
Some(old_value) => {
|
2018-11-04 23:44:52 +00:00
|
|
|
if self.in_snapshot() {
|
2016-05-21 12:18:09 +00:00
|
|
|
self.undo_log.push(UndoLog::Overwrite(key, old_value));
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove(&mut self, key: K) -> bool {
|
|
|
|
match self.map.remove(&key) {
|
|
|
|
Some(old_value) => {
|
2018-11-04 23:44:52 +00:00
|
|
|
if self.in_snapshot() {
|
2016-05-21 12:18:09 +00:00
|
|
|
self.undo_log.push(UndoLog::Overwrite(key, old_value));
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
2016-10-19 19:10:05 +00:00
|
|
|
None => false,
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get(&self, key: &K) -> Option<&V> {
|
|
|
|
self.map.get(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn snapshot(&mut self) -> Snapshot {
|
2018-11-04 23:59:33 +00:00
|
|
|
let len = self.undo_log.len();
|
|
|
|
self.num_open_snapshots += 1;
|
2018-08-09 14:57:55 +00:00
|
|
|
Snapshot { len }
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn assert_open_snapshot(&self, snapshot: &Snapshot) {
|
2018-11-04 23:59:33 +00:00
|
|
|
assert!(self.undo_log.len() >= snapshot.len);
|
|
|
|
assert!(self.num_open_snapshots > 0);
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
|
2018-11-04 23:37:23 +00:00
|
|
|
pub fn commit(&mut self, snapshot: Snapshot) {
|
|
|
|
self.assert_open_snapshot(&snapshot);
|
2018-11-04 23:59:33 +00:00
|
|
|
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);
|
2018-11-04 22:35:28 +00:00
|
|
|
self.undo_log.clear();
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
2018-11-04 23:59:33 +00:00
|
|
|
|
|
|
|
self.num_open_snapshots -= 1;
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn partial_rollback<F>(&mut self, snapshot: &Snapshot, should_revert_key: &F)
|
|
|
|
where
|
|
|
|
F: Fn(&K) -> bool,
|
2016-10-19 22:39:49 +00:00
|
|
|
{
|
2016-10-19 01:32:31 +00:00
|
|
|
self.assert_open_snapshot(snapshot);
|
2019-12-22 22:42:04 +00:00
|
|
|
for i in (snapshot.len..self.undo_log.len()).rev() {
|
2016-10-19 01:32:31 +00:00
|
|
|
let reverse = match self.undo_log[i] {
|
2018-11-04 22:28:12 +00:00
|
|
|
UndoLog::Purged => false,
|
2016-10-19 22:39:49 +00:00
|
|
|
UndoLog::Inserted(ref k) => should_revert_key(k),
|
|
|
|
UndoLog::Overwrite(ref k, _) => should_revert_key(k),
|
2016-10-19 01:32:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if reverse {
|
2018-11-04 22:28:12 +00:00
|
|
|
let entry = mem::replace(&mut self.undo_log[i], UndoLog::Purged);
|
2016-10-19 01:32:31 +00:00
|
|
|
self.reverse(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-04 23:37:23 +00:00
|
|
|
pub fn rollback_to(&mut self, snapshot: Snapshot) {
|
|
|
|
self.assert_open_snapshot(&snapshot);
|
2018-11-04 23:59:33 +00:00
|
|
|
while self.undo_log.len() > snapshot.len {
|
2016-10-19 01:32:31 +00:00
|
|
|
let entry = self.undo_log.pop().unwrap();
|
|
|
|
self.reverse(entry);
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
|
2018-11-04 23:59:33 +00:00
|
|
|
self.num_open_snapshots -= 1;
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
2016-10-19 01:32:31 +00:00
|
|
|
|
|
|
|
fn reverse(&mut self, entry: UndoLog<K, V>) {
|
|
|
|
match entry {
|
|
|
|
UndoLog::Inserted(key) => {
|
|
|
|
self.map.remove(&key);
|
|
|
|
}
|
|
|
|
|
|
|
|
UndoLog::Overwrite(key, old_value) => {
|
|
|
|
self.map.insert(key, old_value);
|
|
|
|
}
|
|
|
|
|
2018-11-04 22:28:12 +00:00
|
|
|
UndoLog::Purged => {}
|
2016-10-19 01:32:31 +00:00
|
|
|
}
|
|
|
|
}
|
2016-05-21 12:18:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'k, K, V> ops::Index<&'k K> for SnapshotMap<K, V>
|
2019-12-22 22:42:04 +00:00
|
|
|
where
|
|
|
|
K: Hash + Clone + Eq,
|
2016-05-21 12:18:09 +00:00
|
|
|
{
|
|
|
|
type Output = V;
|
|
|
|
fn index(&self, key: &'k K) -> &V {
|
|
|
|
&self.map[key]
|
|
|
|
}
|
|
|
|
}
|