@@ -12,15 +12,34 @@ enum Version<K> {
1212 Phi ( Vec < VersionId > ) ,
1313}
1414
15- #[ derive( Clone ) ]
16- pub ( super ) struct Snapshot < K > {
17- current : HashMap < K , VersionId > ,
15+ pub ( super ) struct Checkpoint {
16+ undo_start : usize ,
17+ depth : usize ,
18+ }
19+
20+ pub ( super ) struct BranchState < K > {
21+ bindings : HashMap < K , VersionId > ,
22+ }
23+
24+ impl < K > BranchState < K > {
25+ pub ( super ) fn unchanged ( ) -> Self {
26+ Self {
27+ bindings : HashMap :: default ( ) ,
28+ }
29+ }
30+ }
31+
32+ struct Undo < K > {
33+ key : K ,
34+ previous : Option < VersionId > ,
1835}
1936
2037pub ( super ) struct SsaStore < K > {
2138 versions : Vec < Version < K > > ,
2239 entries : HashMap < K , VersionId > ,
2340 current : HashMap < K , VersionId > ,
41+ undo : Vec < Undo < K > > ,
42+ checkpoints : Vec < usize > ,
2443}
2544
2645impl < K > Default for SsaStore < K > {
@@ -29,6 +48,8 @@ impl<K> Default for SsaStore<K> {
2948 versions : Vec :: new ( ) ,
3049 entries : HashMap :: default ( ) ,
3150 current : HashMap :: default ( ) ,
51+ undo : Vec :: new ( ) ,
52+ checkpoints : Vec :: new ( ) ,
3253 }
3354 }
3455}
5172 if let Some ( version) = self . current . get ( & key) {
5273 * version
5374 } else {
54- let version = self . entry ( key) ;
55- self . current . insert ( key, version) ;
56- version
75+ self . entry ( key)
5776 }
5877 }
5978
@@ -66,38 +85,65 @@ where
6685 }
6786
6887 pub ( super ) fn bind ( & mut self , key : K , version : VersionId ) {
69- self . current . insert ( key, version) ;
88+ let previous = self . current . insert ( key, version) ;
89+ if !self . checkpoints . is_empty ( ) {
90+ self . undo . push ( Undo { key, previous } ) ;
91+ }
7092 }
7193
72- pub ( super ) fn snapshot ( & self ) -> Snapshot < K > {
73- Snapshot {
74- current : self . current . clone ( ) ,
75- }
94+ pub ( super ) fn checkpoint ( & mut self ) -> Checkpoint {
95+ let checkpoint = Checkpoint {
96+ undo_start : self . undo . len ( ) ,
97+ depth : self . checkpoints . len ( ) ,
98+ } ;
99+ self . checkpoints . push ( checkpoint. undo_start ) ;
100+ checkpoint
76101 }
77102
78- pub ( super ) fn restore ( & mut self , snapshot : & Snapshot < K > ) {
79- self . current . clone_from ( & snapshot. current ) ;
103+ pub ( super ) fn capture_and_rollback ( & mut self , checkpoint : Checkpoint ) -> BranchState < K > {
104+ assert_eq ! ( checkpoint. depth + 1 , self . checkpoints. len( ) ) ;
105+ assert_eq ! ( self . checkpoints. pop( ) , Some ( checkpoint. undo_start) ) ;
106+
107+ let mut bindings = HashMap :: default ( ) ;
108+ for undo in & self . undo [ checkpoint. undo_start ..] {
109+ let version = self
110+ . current
111+ . get ( & undo. key )
112+ . copied ( )
113+ . expect ( "a branch binding must exist until rollback" ) ;
114+ bindings. insert ( undo. key , version) ;
115+ }
116+
117+ while self . undo . len ( ) > checkpoint. undo_start {
118+ let undo = self . undo . pop ( ) . expect ( "undo length checked above" ) ;
119+ if let Some ( previous) = undo. previous {
120+ self . current . insert ( undo. key , previous) ;
121+ } else {
122+ self . current . remove ( & undo. key ) ;
123+ }
124+ }
125+ bindings. retain ( |key, version| self . current . get ( key) . copied ( ) != Some ( * version) ) ;
126+ BranchState { bindings }
80127 }
81128
82- pub ( super ) fn merge ( & mut self , base : & Snapshot < K > , states : & [ Snapshot < K > ] ) {
83- let mut keys: HashSet < K > = base . current . keys ( ) . copied ( ) . collect ( ) ;
129+ pub ( super ) fn merge ( & mut self , states : & [ BranchState < K > ] ) {
130+ let mut keys = HashSet :: default ( ) ;
84131 for state in states {
85- keys. extend ( state. current . keys ( ) . copied ( ) ) ;
132+ keys. extend ( state. bindings . keys ( ) . copied ( ) ) ;
86133 }
87- let mut merged = HashMap :: default ( ) ;
88134 for key in keys {
89- let fallback = base
135+ let fallback = self
90136 . current
91137 . get ( & key)
92138 . copied ( )
93139 . unwrap_or_else ( || self . entry ( key) ) ;
94140 let inputs = states
95141 . iter ( )
96- . map ( |state| state. current . get ( & key) . copied ( ) . unwrap_or ( fallback) )
142+ . map ( |state| state. bindings . get ( & key) . copied ( ) . unwrap_or ( fallback) )
97143 . collect ( ) ;
98- merged. insert ( key, self . phi ( inputs) ) ;
144+ let version = self . phi ( inputs) ;
145+ self . bind ( key, version) ;
99146 }
100- self . current = merged;
101147 }
102148
103149 pub ( super ) fn root_sources ( & self , version : VersionId ) -> HashSet < K > {
@@ -179,32 +225,77 @@ mod tests {
179225 #[ test]
180226 fn retained_live_on_entry_is_not_a_combinational_read ( ) {
181227 let mut ssa = SsaStore :: default ( ) ;
182- let base = ssa. snapshot ( ) ;
183228 let retained = ssa. read ( "destination" ) ;
229+ let checkpoint = ssa. checkpoint ( ) ;
184230 let assigned = ssa. definition ( Vec :: new ( ) ) ;
185231 ssa. bind ( "destination" , assigned) ;
186- let branch = ssa. snapshot ( ) ;
232+ let branch = ssa. capture_and_rollback ( checkpoint ) ;
187233
188- ssa. merge ( & base , & [ base . clone ( ) , branch] ) ;
234+ ssa. merge ( & [ BranchState :: unchanged ( ) , branch] ) ;
189235
190236 let merged = ssa. read ( "destination" ) ;
191237 assert_ne ! ( merged, retained) ;
192238 assert ! ( ssa. root_sources( merged) . is_empty( ) ) ;
193239 }
194240
195241 #[ test]
196- fn restore_discards_current_bindings_without_discarding_versions ( ) {
242+ fn rollback_discards_current_bindings_without_discarding_versions ( ) {
197243 let mut ssa = SsaStore :: default ( ) ;
198- let base = ssa. snapshot ( ) ;
244+ let checkpoint = ssa. checkpoint ( ) ;
199245 let source = ssa. read ( "source" ) ;
200246 let definition = ssa. definition ( vec ! [ source] ) ;
201247 ssa. bind ( "destination" , definition) ;
202248
203- ssa. restore ( & base ) ;
249+ let _ = ssa. capture_and_rollback ( checkpoint ) ;
204250 let restored = ssa. read ( "destination" ) ;
205251
206252 let expected = [ "source" ] . into_iter ( ) . collect :: < HashSet < _ > > ( ) ;
207253 assert_eq ! ( ssa. root_sources( definition) , expected) ;
208254 assert ! ( ssa. root_sources( restored) . is_empty( ) ) ;
209255 }
256+
257+ #[ test]
258+ fn branch_state_contains_only_keys_changed_since_checkpoint ( ) {
259+ let mut ssa = SsaStore :: default ( ) ;
260+ for key in 0 ..1_000 {
261+ let version = ssa. definition ( Vec :: new ( ) ) ;
262+ ssa. bind ( key, version) ;
263+ }
264+
265+ let checkpoint = ssa. checkpoint ( ) ;
266+ let version = ssa. definition ( Vec :: new ( ) ) ;
267+ ssa. bind ( 500 , version) ;
268+ let branch = ssa. capture_and_rollback ( checkpoint) ;
269+
270+ assert_eq ! ( branch. bindings. len( ) , 1 ) ;
271+ assert_eq ! ( branch. bindings[ & 500 ] , version) ;
272+ }
273+
274+ #[ test]
275+ fn nested_rollback_preserves_the_outer_transaction ( ) {
276+ let mut ssa = SsaStore :: default ( ) ;
277+ let base = ssa. definition ( Vec :: new ( ) ) ;
278+ ssa. bind ( "outer" , base) ;
279+
280+ let outer_checkpoint = ssa. checkpoint ( ) ;
281+ let outer_definition = ssa. definition ( Vec :: new ( ) ) ;
282+ ssa. bind ( "outer" , outer_definition) ;
283+
284+ let inner_checkpoint = ssa. checkpoint ( ) ;
285+ let inner_definition = ssa. definition ( Vec :: new ( ) ) ;
286+ ssa. bind ( "inner" , inner_definition) ;
287+ let inner_state = ssa. capture_and_rollback ( inner_checkpoint) ;
288+
289+ assert_eq ! ( ssa. read( "outer" ) , outer_definition) ;
290+ assert_ne ! ( ssa. read( "inner" ) , inner_definition) ;
291+
292+ ssa. merge ( & [ BranchState :: unchanged ( ) , inner_state] ) ;
293+ let merged_inner = ssa. read ( "inner" ) ;
294+ let outer_state = ssa. capture_and_rollback ( outer_checkpoint) ;
295+
296+ assert_eq ! ( ssa. read( "outer" ) , base) ;
297+ assert_ne ! ( ssa. read( "inner" ) , merged_inner) ;
298+ assert_eq ! ( outer_state. bindings[ "outer" ] , outer_definition) ;
299+ assert_eq ! ( outer_state. bindings[ "inner" ] , merged_inner) ;
300+ }
210301}
0 commit comments