Skip to content

Commit 17cf91d

Browse files
committed
refactor buffer for LaneSlot
1 parent 2d0d19b commit 17cf91d

1 file changed

Lines changed: 99 additions & 64 deletions

File tree

asyncgit/src/graph/buffer.rs

Lines changed: 99 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1-
use super::chunk::{Chunk, Markers};
2-
use super::AliasId;
1+
use super::chunk::LaneSlot;
2+
use super::CommitAlias;
33
use std::collections::BTreeMap;
44

55
/// A single mutation of the lane state, recorded while processing one
66
/// commit.
77
#[derive(Clone, Debug)]
88
pub 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.
3038
pub 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

Comments
 (0)