forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodified_set.rs
133 lines (117 loc) · 3.69 KB
/
modified_set.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::marker::PhantomData;
use rustc_index::vec::Idx;
use ena::undo_log::{Rollback, UndoLogs};
#[derive(Copy, Clone, Debug)]
enum UndoInner {
Add,
Drain { index: usize, offset: usize },
}
#[derive(Copy, Clone, Debug)]
pub struct Undo<I>(UndoInner, PhantomData<I>);
/// Tracks which indices have been modified and allows watchers to registered and notified of these
/// changes.
#[derive(Clone, Debug)]
pub struct ModifiedSet<T: Idx> {
modified: Vec<T>,
offsets: Vec<usize>,
}
impl<T: Idx> Default for ModifiedSet<T> {
fn default() -> Self {
Self { modified: Default::default(), offsets: Vec::new() }
}
}
impl<T: Idx> ModifiedSet<T> {
/// Creates a new `ModifiedSet`
pub fn new() -> Self {
Self::default()
}
/// Marks `index` as "modified". A subsequent call to `drain` will notify the callback with
/// `index`
pub fn set(&mut self, undo_log: &mut impl UndoLogs<Undo<T>>, index: T) {
self.modified.push(index);
undo_log.push(Undo(UndoInner::Add, PhantomData));
}
/// Calls `f` with all the indices that have been modified since the last call to
/// `notify_watcher`
pub fn notify_watcher(
&mut self,
undo_log: &mut impl UndoLogs<Undo<T>>,
watcher_offset: &Offset<T>,
mut f: impl FnMut(T),
) {
let offset = &mut self.offsets[watcher_offset.index];
if *offset < self.modified.len() {
for &index in &self.modified[*offset..] {
f(index);
}
undo_log.push(Undo(
UndoInner::Drain { index: watcher_offset.index, offset: *offset },
PhantomData,
));
*offset = self.modified.len();
}
}
/// Clears the set of all modifications that have been drained by all watchers
pub fn clear(&mut self) {
let min = self.offsets.iter().copied().min().unwrap_or_else(|| self.modified.len());
self.modified.drain(..min);
for offset in &mut self.offsets {
*offset -= min;
}
}
/// Registers a new watcher on this set.
///
/// NOTE: Watchers must be removed in the reverse order that they were registered
pub fn register(&mut self) -> Offset<T> {
let index = self.offsets.len();
self.offsets.push(self.modified.len());
Offset { index, _marker: PhantomData }
}
/// De-registers a watcher on this set.
///
/// NOTE: Watchers must be removed in the reverse order that they were registered
pub fn deregister(&mut self, offset: Offset<T>) {
assert_eq!(
offset.index,
self.offsets.len() - 1,
"Watchers must be removed in the reverse order that they were registered"
);
self.offsets.pop();
std::mem::forget(offset);
}
}
impl<I: Idx> Rollback<Undo<I>> for ModifiedSet<I> {
fn reverse(&mut self, undo: Undo<I>) {
match undo.0 {
UndoInner::Add => {
self.modified.pop();
}
UndoInner::Drain { index, offset } => {
if let Some(o) = self.offsets.get_mut(index) {
*o = offset;
}
}
}
}
}
/// A registered offset into a `ModifiedSet`. Tracks how much a watcher has seen so far to avoid
/// being notified of the same event twice.
#[must_use]
pub struct Offset<T> {
index: usize,
_marker: PhantomData<T>,
}
impl<T> Drop for Offset<T> {
fn drop(&mut self) {
if !std::thread::panicking() {
panic!("Offsets should be deregistered")
}
}
}
#[must_use]
#[derive(Debug)]
pub struct Snapshot<T> {
modified_len: usize,
undo_log_len: usize,
_marker: PhantomData<T>,
}