@@ -38,25 +38,34 @@ pub struct ObligationForest<O> {
38
38
/// at a higher index than its parent. This is needed by the
39
39
/// backtrace iterator (which uses `split_at`).
40
40
nodes : Vec < Node < O > > ,
41
- snapshots : Vec < usize > ,
42
41
43
42
/// List of inversion actions that may be performed to undo mutations
44
43
/// while in snapshots.
45
- undo_log : Vec < UndoAction < O > > ,
44
+ undo_log : Vec < Action < O > > ,
46
45
}
47
46
48
47
/// A single inversion of an action on a node in an ObligationForest.
49
48
#[ derive( Debug ) ]
50
- enum UndoAction < O > {
49
+ enum Action < O > {
51
50
// FIXME potential optimization: store push undos as a count in the snapshots vec (because we
52
51
// never remove when in a snapshot, and because snapshots are sequenced in a specific way
53
52
// w.r.t. what's in the ObligationForest's undo list, it's possible to just keep track of push
54
53
// counts instead of sequencing them in the undo list with node state modifications)
55
- UndoPush ,
56
- UndoModify {
54
+ Push ,
55
+ Modify {
57
56
at : NodeIndex ,
58
57
undoer : UndoModify < O > ,
59
58
} ,
59
+
60
+ /// An indicator that a snapshot was opened at the actions position; does not have a meaningful
61
+ /// 'undo' operation on the tree, as any time we're interested in 'undoing' this we're already
62
+ /// doing so by a user having called `ObligationForest::rollback_snapshot`.
63
+ OpenSnapshot ,
64
+
65
+ /// An action that we've left sitting in the queue because it's somehow become a no-op but we
66
+ /// don't want to deal with O(N) data movement in the log. At present this is always a
67
+ /// committed snapshot.
68
+ NoOp ,
60
69
}
61
70
62
71
/// A single inversion of a node modification action in an ObligationForest.
@@ -146,7 +155,6 @@ impl<O: Clone + Debug> ObligationForest<O> {
146
155
pub fn new ( ) -> ObligationForest < O > {
147
156
ObligationForest {
148
157
nodes : vec ! [ ] ,
149
- snapshots : vec ! [ ] ,
150
158
undo_log : vec ! [ ] ,
151
159
}
152
160
}
@@ -158,34 +166,65 @@ impl<O: Clone + Debug> ObligationForest<O> {
158
166
}
159
167
160
168
pub fn start_snapshot ( & mut self ) -> Snapshot {
161
- self . snapshots . push ( self . undo_log . len ( ) ) ;
162
- Snapshot { len : self . snapshots . len ( ) }
169
+ let result = Snapshot { len : self . undo_log . len ( ) } ;
170
+ self . undo_log . push ( Action :: OpenSnapshot ) ;
171
+ result
163
172
}
164
173
165
174
pub fn commit_snapshot ( & mut self , snapshot : Snapshot ) {
166
- // Check that we are obeying stack discipline.
167
- assert_eq ! ( snapshot. len, self . snapshots. len( ) ) ;
168
- self . snapshots . pop ( ) . unwrap ( ) ;
175
+ // Look for the first open snapshot (which MUST be the snapshot passed to us, else
176
+ // something went wrong).
177
+ let mut commit_at_index = None ;
178
+ for ( index, action) in self . undo_log . iter_mut ( ) . enumerate ( ) . rev ( ) {
179
+ commit_at_index = Some ( match action {
180
+ & mut Action :: OpenSnapshot => index,
181
+ _ => continue ,
182
+ } ) ;
183
+ break ;
184
+ }
185
+ // We should always have a snapshot to commit unless we've broken stack discipline at the
186
+ // base.
187
+ let commit_at_index = commit_at_index. unwrap ( ) ;
188
+ // Check that we are obeying stack discipline for anywhere that isn't the base.
189
+ assert_eq ! ( snapshot. len, commit_at_index) ;
190
+ mem:: replace ( & mut self . undo_log [ commit_at_index] , Action :: NoOp ) ;
169
191
if !self . in_snapshot ( ) {
170
192
self . undo_log . clear ( ) ;
171
193
}
172
194
}
173
195
174
196
pub fn rollback_snapshot ( & mut self , snapshot : Snapshot ) {
175
- // Check that we are obeying stack discipline.
176
- assert_eq ! ( snapshot. len, self . snapshots. len( ) ) ;
177
- let undo_len = self . snapshots . pop ( ) . unwrap ( ) ;
197
+ // Look for the first open snapshot (which MUST be the snapshot passed to us, else
198
+ // something went wrong).
199
+ let mut rollback_at_index = None ;
200
+ for ( index, action) in self . undo_log . iter_mut ( ) . enumerate ( ) . rev ( ) {
201
+ rollback_at_index = Some ( match action {
202
+ & mut Action :: OpenSnapshot => index,
203
+ _ => continue ,
204
+ } ) ;
205
+ break ;
206
+ }
207
+ // We should always have a snapshot to commit unless we've broken stack discipline at the
208
+ // base.
209
+ let rollback_at_index = rollback_at_index. unwrap ( ) ;
210
+ // Check that we are obeying stack discipline for anywhere that isn't the base.
211
+ assert_eq ! ( snapshot. len, rollback_at_index) ;
212
+
213
+ let undo_len = rollback_at_index;
178
214
179
- let undoers : Vec < _ > = ( undo_len..self . undo_log . len ( ) )
215
+ let actions : Vec < _ > = ( undo_len..self . undo_log . len ( ) )
180
216
. map ( |_| self . undo_log . pop ( ) . unwrap ( ) )
181
217
. collect ( ) ;
182
- for undoer in undoers {
183
- undoer. undo ( self ) ;
218
+ for action in actions {
219
+ action. undo ( self ) ;
220
+ }
221
+ if !self . in_snapshot ( ) {
222
+ self . compress ( ) ;
184
223
}
185
224
}
186
225
187
226
pub fn in_snapshot ( & self ) -> bool {
188
- !self . snapshots . is_empty ( )
227
+ !self . undo_log . is_empty ( )
189
228
}
190
229
191
230
/// Adds a new tree to the forest.
@@ -195,7 +234,7 @@ impl<O: Clone + Debug> ObligationForest<O> {
195
234
let index = NodeIndex :: new ( self . nodes . len ( ) ) ;
196
235
self . nodes . push ( Node :: new ( index, None , obligation) ) ;
197
236
if self . in_snapshot ( ) {
198
- self . undo_log . push ( UndoAction :: UndoPush ) ;
237
+ self . undo_log . push ( Action :: Push ) ;
199
238
}
200
239
}
201
240
@@ -339,8 +378,8 @@ impl<O: Clone + Debug> ObligationForest<O> {
339
378
}
340
379
341
380
if self . in_snapshot ( ) {
342
- self . undo_log . extend ( ( 0 ..num_incomplete_children) . map ( |_| UndoAction :: UndoPush ) ) ;
343
- self . undo_log . push ( UndoAction :: UndoModify {
381
+ self . undo_log . extend ( ( 0 ..num_incomplete_children) . map ( |_| Action :: Push ) ) ;
382
+ self . undo_log . push ( Action :: Modify {
344
383
at : NodeIndex :: new ( index) ,
345
384
undoer : UndoModify :: UndoPendingIntoSuccess ,
346
385
} ) ;
@@ -381,7 +420,7 @@ impl<O: Clone + Debug> ObligationForest<O> {
381
420
if let NodeState :: Error = self . nodes [ root] . state {
382
421
let old_state = mem:: replace ( & mut self . nodes [ child] . state , NodeState :: Error ) ;
383
422
if self . in_snapshot ( ) {
384
- self . undo_log . push ( UndoAction :: UndoModify {
423
+ self . undo_log . push ( Action :: Modify {
385
424
at : NodeIndex :: new ( child) ,
386
425
undoer : match old_state {
387
426
NodeState :: Pending { obligation } =>
@@ -410,7 +449,7 @@ impl<O: Clone + Debug> ObligationForest<O> {
410
449
let obligation = match state {
411
450
NodeState :: Pending { obligation } => {
412
451
if self . in_snapshot ( ) {
413
- self . undo_log . push ( UndoAction :: UndoModify {
452
+ self . undo_log . push ( Action :: Modify {
414
453
at : NodeIndex :: new ( p) ,
415
454
undoer : UndoModify :: UndoPendingIntoError {
416
455
obligation : obligation. clone ( )
@@ -421,7 +460,7 @@ impl<O: Clone + Debug> ObligationForest<O> {
421
460
} ,
422
461
NodeState :: Success { obligation, num_incomplete_children } => {
423
462
if self . in_snapshot ( ) {
424
- self . undo_log . push ( UndoAction :: UndoModify {
463
+ self . undo_log . push ( Action :: Modify {
425
464
at : NodeIndex :: new ( p) ,
426
465
undoer : UndoModify :: UndoSuccessIntoError {
427
466
obligation : obligation. clone ( ) ,
@@ -557,13 +596,15 @@ impl<'b, O> Iterator for Backtrace<'b, O> {
557
596
}
558
597
}
559
598
560
- impl < O > UndoAction < O > {
599
+ impl < O > Action < O > {
561
600
fn undo ( self , forest : & mut ObligationForest < O > ) {
562
601
match self {
563
- UndoAction :: UndoPush => {
602
+ Action :: Push => {
564
603
forest. nodes . pop ( ) . unwrap ( ) ;
565
604
} ,
566
- UndoAction :: UndoModify { at, undoer } => undoer. undo ( forest, at) ,
605
+ Action :: Modify { at, undoer } => undoer. undo ( forest, at) ,
606
+ Action :: OpenSnapshot => { } ,
607
+ Action :: NoOp => { } ,
567
608
}
568
609
}
569
610
}
0 commit comments