1- use super :: chunk:: { Chunk , Markers } ;
2- use super :: AliasId ;
1+ use super :: chunk:: LaneSlot ;
2+ use super :: CommitAlias ;
33use std:: collections:: BTreeMap ;
44
55/// A single mutation of the lane state, recorded while processing one
66/// commit.
77#[ derive( Clone , Debug ) ]
88pub enum DeltaOp {
9- Insert { index : usize , item : Option < Chunk > } ,
10- Remove { index : usize } ,
11- Replace { index : usize , new : Option < Chunk > } ,
9+ Insert {
10+ index : usize ,
11+ item : Option < LaneSlot > ,
12+ } ,
13+ Remove {
14+ index : usize ,
15+ } ,
16+ Replace {
17+ index : usize ,
18+ new : Option < LaneSlot > ,
19+ } ,
1220}
1321
1422/// All lane-state mutations caused by processing a single commit.
@@ -22,25 +30,25 @@ const CHECKPOINT_INTERVAL: usize = 100;
2230/// Delta-compressed history of the graph's lane state.
2331///
2432/// While walking the log top-down, every commit mutates the set of
25- /// active lanes (a `Vec<Option<Chunk >>`, one slot per lane). So, storign a
33+ /// active lanes (a `Vec<Option<LaneSlot >>`, one slot per lane). So, storign a
2634/// full copy of that state for each commit is a waste. This
2735/// buffer preserves ONLY the latest state PLUS the list of [`Delta`]s that
2836/// produced it.
2937/// Use [`Buffer::decompress`] to get the complete version.
3038pub struct Buffer {
3139 /// Lane state after the most recently processed commit.
32- pub current : Vec < Option < Chunk > > ,
40+ pub current : Vec < Option < LaneSlot > > ,
3341
3442 /// One [`Delta`] per processed commit, in the order of the walk.
3543 pub deltas : Vec < Delta > ,
3644
3745 /// Full lane-state snapshots taken every `CHECKPOINT_INTERVAL`
3846 /// commits, keyed by delta index, for reducing decompression cost.
39- pub checkpoints : BTreeMap < usize , Vec < Option < Chunk > > > ,
47+ pub checkpoints : BTreeMap < usize , Vec < Option < LaneSlot > > > ,
4048
4149 /// Aliases of merge commits whose second parent still needs a new
4250 /// lane.
43- merge_commits : Vec < AliasId > ,
51+ merge_commits : Vec < CommitAlias > ,
4452
4553 /// Scratch list of the [`DeltaOp`]s recorded for processing commit
4654 pending_delta : Vec < DeltaOp > ,
@@ -65,16 +73,16 @@ impl Buffer {
6573
6674 /// Remember `alias` as a merge commit whose second parent must be
6775 /// given its own lane.
68- pub fn track_merge_commit ( & mut self , alias : AliasId ) {
76+ pub fn track_merge_commit ( & mut self , alias : CommitAlias ) {
6977 self . merge_commits . push ( alias) ;
7078 }
7179
72- pub fn update ( & mut self , new_chunk : & Chunk ) {
80+ pub fn update ( & mut self , new_chunk : & LaneSlot ) {
7381 // Phase 1: place the new chunk into the lane array.
7482 let placement_index = self . place_chunk ( new_chunk) ;
7583
7684 // Phase 2: consume the alias in all other live chunks.
77- if let Some ( alias) = new_chunk. alias {
85+ if let Some ( alias) = new_chunk. alias ( ) {
7886 self . consume_alias_in_other_chunks (
7987 alias,
8088 placement_index,
@@ -88,10 +96,11 @@ impl Buffer {
8896 self . commit_delta ( ) ;
8997 }
9098
91- fn place_chunk ( & mut self , new_chunk : & Chunk ) -> usize {
92- // Prefer a lane whose current occupant is waiting for this chunk as parent_a.
99+ fn place_chunk ( & mut self , new_chunk : & LaneSlot ) -> usize {
100+ // Prefer a lane whose current occupant awaits this chunk's
101+ // commit as its primary parent.
93102 let target = self
94- . find_lane_awaiting_parent ( new_chunk. alias )
103+ . find_lane_awaiting_parent ( new_chunk. alias ( ) )
95104 . or_else ( || self . first_empty_lane ( ) )
96105 . unwrap_or ( self . current . len ( ) ) ;
97106
@@ -105,12 +114,14 @@ impl Buffer {
105114
106115 fn find_lane_awaiting_parent (
107116 & self ,
108- alias : Option < AliasId > ,
117+ alias : Option < CommitAlias > ,
109118 ) -> Option < usize > {
110- let alias = alias?;
111- self . current . iter ( ) . position ( |slot| {
112- slot. as_ref ( )
113- . is_some_and ( |chunk| chunk. parent_a == Some ( alias) )
119+ alias. and_then ( |alias| {
120+ self . current . iter ( ) . position ( |slot| {
121+ slot. as_ref ( ) . is_some_and ( |chunk| {
122+ chunk. awaits ( ) == Some ( alias)
123+ } )
124+ } )
114125 } )
115126 }
116127
@@ -120,70 +131,86 @@ impl Buffer {
120131
121132 fn consume_alias_in_other_chunks (
122133 & mut self ,
123- alias : AliasId ,
134+ alias : CommitAlias ,
124135 skip_index : usize ,
125136 ) {
126- for index in 0 ..self . current . len ( ) {
127- let mut chunk = match self . current [ index] . clone ( ) {
137+ let current = self . current . clone ( ) ;
138+ for ( index, slot) in current. into_iter ( ) . enumerate ( ) {
139+ let chunk = match slot {
128140 Some ( chunk) if index != skip_index => chunk,
129141 _ => continue ,
130142 } ;
131143
132- let changed_a = chunk. parent_a == Some ( alias ) ;
133- let changed_b = chunk . parent_b == Some ( alias ) ;
134-
135- if changed_a || changed_b {
136- if changed_a {
137- chunk . parent_a = None ;
138- }
139- if changed_b {
140- chunk . parent_b = None ;
144+ let new = match chunk {
145+ // The awaited parent was JUST placed. Close the lane.
146+ // The pending second parent is dropped with it.
147+ LaneSlot :: Flowing { parent , .. }
148+ | LaneSlot :: FlowingMerge { parent , .. }
149+ | LaneSlot :: Reserved { parent }
150+ if parent . get ( ) == alias =>
151+ {
152+ None
141153 }
154+ // The second parent was just placed
155+ // bridge resolves and the lane flows
156+ LaneSlot :: FlowingMerge {
157+ alias : merge_alias,
158+ parent,
159+ second,
160+ } if second. get ( ) == alias => Some ( LaneSlot :: Flowing {
161+ alias : merge_alias,
162+ parent,
163+ } ) ,
164+ _ => continue ,
165+ } ;
142166
143- self . record_replace (
144- index,
145- chunk. parent_a . is_some ( ) . then_some ( chunk) ,
146- ) ;
147- }
167+ self . record_replace ( index, new) ;
148168 }
149169 }
150170
151171 fn flush_merge_commits ( & mut self ) {
152172 while let Some ( alias) = self . merge_commits . pop ( ) {
153173 // Search for an occupied slot that matches the target alias.
154- // If found, extract its index and a mutable clone of the chunk.
155- let Some ( ( index, mut chunk) ) =
174+ // If found, extract its index and a clone of the chunk.
175+ let Some ( ( index, chunk) ) =
156176 self . current . iter ( ) . enumerate ( ) . find_map (
157177 |( index, slot) | {
158178 let chunk = slot. as_ref ( ) ?;
159- ( chunk. alias == Some ( alias) )
179+ ( chunk. alias ( ) == Some ( alias) )
160180 . then ( || ( index, chunk. clone ( ) ) )
161181 } ,
162182 )
163183 else {
164184 continue ;
165185 } ;
166186
167- let detached_parent = chunk. parent_b . take ( ) ;
168- self . record_replace ( index, Some ( chunk) ) ;
169-
170- if let Some ( parent) = detached_parent {
171- let new_lane = Chunk {
172- alias : None ,
173- parent_a : Some ( parent) ,
174- parent_b : None ,
175- marker : Markers :: Commit ,
176- } ;
177-
178- // Always append the merge's second-parent lane to
179- // the end instead of reusing an existing empty slot,
180- // so the new visual column does not collapse
181- // spatial ordering of lanes already in existence.
182- self . record_insert (
183- self . current . len ( ) ,
184- Some ( new_lane) ,
185- ) ;
186- }
187+ // Only a merge still owing its second parent needs a
188+ // lane split off; anything else is a no-op.
189+ let LaneSlot :: FlowingMerge {
190+ alias : merge_alias,
191+ parent,
192+ second,
193+ } = chunk
194+ else {
195+ continue ;
196+ } ;
197+
198+ self . record_replace (
199+ index,
200+ Some ( LaneSlot :: Flowing {
201+ alias : merge_alias,
202+ parent,
203+ } ) ,
204+ ) ;
205+
206+ // Always append the merge's second-parent lane to
207+ // the end instead of reusing an existing empty slot,
208+ // so the new visual column does not collapse
209+ // spatial ordering of lanes already in existence.
210+ self . record_insert (
211+ self . current . len ( ) ,
212+ Some ( LaneSlot :: Reserved { parent : second } ) ,
213+ ) ;
187214 }
188215 }
189216
@@ -202,15 +229,23 @@ impl Buffer {
202229 }
203230 }
204231
205- fn record_replace ( & mut self , index : usize , new : Option < Chunk > ) {
232+ fn record_replace (
233+ & mut self ,
234+ index : usize ,
235+ new : Option < LaneSlot > ,
236+ ) {
206237 self . pending_delta . push ( DeltaOp :: Replace {
207238 index,
208239 new : new. clone ( ) ,
209240 } ) ;
210241 self . current [ index] = new;
211242 }
212243
213- fn record_insert ( & mut self , index : usize , item : Option < Chunk > ) {
244+ fn record_insert (
245+ & mut self ,
246+ index : usize ,
247+ item : Option < LaneSlot > ,
248+ ) {
214249 self . pending_delta . push ( DeltaOp :: Insert {
215250 index,
216251 item : item. clone ( ) ,
@@ -227,7 +262,7 @@ impl Buffer {
227262 & self ,
228263 start : usize ,
229264 end : usize ,
230- ) -> Vec < Vec < Option < Chunk > > > {
265+ ) -> Vec < Vec < Option < LaneSlot > > > {
231266 let ( current_index, mut state) =
232267 self . checkpoints . range ( ..=start) . next_back ( ) . map_or_else (
233268 || ( None , Vec :: new ( ) ) ,
@@ -261,7 +296,7 @@ impl Buffer {
261296 }
262297
263298 fn apply_delta_to_state (
264- state : & mut Vec < Option < Chunk > > ,
299+ state : & mut Vec < Option < LaneSlot > > ,
265300 delta : & Delta ,
266301 ) {
267302 for op in & delta. 0 {
0 commit comments