Skip to content

Commit aada136

Browse files
committed
fix: skip reversal when list lengths match in unkeyed reconciliation
The common case (same-length lists) now takes the original back-to-back zip path with zero overhead. The front-to-front reversal only fires when lengths actually differ.
1 parent fcaf483 commit aada136

1 file changed

Lines changed: 41 additions & 34 deletions

File tree

packages/yew/src/dom_bundle/blist.rs

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl BList {
145145

146146
/// Diff and patch unkeyed child lists
147147
///
148-
/// Pairs children by render-order position from the front, so that leading
148+
/// Pairs children front-to-front by render-order position so that leading
149149
/// nodes are always reconciled with themselves when the list grows or
150150
/// shrinks at the tail.
151151
fn apply_unkeyed(
@@ -163,47 +163,54 @@ impl BList {
163163
slot,
164164
};
165165

166-
// `rights` is stored in reverse render order. Flip to render order so
167-
// that simple index-based pairing matches children front-to-front.
168-
rights.reverse();
166+
if lefts.len() == rights.len() {
167+
// Fast path: when lengths match, back-to-back pairing (the natural
168+
// order for rev_children) is identical to front-to-front. No
169+
// reversal needed.
170+
for (l, r) in lefts.into_iter().rev().zip(rights.iter_mut()) {
171+
writer = writer.patch(l, r);
172+
}
173+
} else {
174+
// Lengths differ: flip to render order so index-based pairing
175+
// matches children front-to-front.
176+
rights.reverse();
169177

170-
let left_len = lefts.len();
178+
let left_len = lefts.len();
171179

172-
// Remove excess old nodes from the END of render order (now the tail).
173-
if left_len < rights.len() {
174-
for r in rights.drain(left_len..) {
175-
test_log!("removing: {:?}", r);
176-
r.detach(root, parent, false);
180+
// Remove excess old nodes from the end of render order (the tail).
181+
if left_len < rights.len() {
182+
for r in rights.drain(left_len..) {
183+
test_log!("removing: {:?}", r);
184+
r.detach(root, parent, false);
185+
}
177186
}
178-
}
179-
180-
let paired_count = rights.len(); // min(left_len, old_len)
181187

182-
// The NodeWriter must process children right-to-left in render order.
183-
let mut lefts_rev = lefts.into_iter().rev();
188+
let paired_count = rights.len(); // min(left_len, old_len)
189+
let mut lefts_rev = lefts.into_iter().rev();
190+
191+
// Add excess new nodes at the tail of render order (rightmost
192+
// first for the NodeWriter).
193+
let excess_start = rights.len();
194+
for l in lefts_rev
195+
.by_ref()
196+
.take(left_len.saturating_sub(paired_count))
197+
{
198+
let (next_writer, el) = writer.add(l);
199+
rights.push(el);
200+
writer = next_writer;
201+
}
202+
// Items were pushed right-to-left; flip to render order.
203+
rights[excess_start..].reverse();
184204

185-
// 1. Add excess new nodes at the tail of render order (rightmost first).
186-
let excess_start = rights.len();
187-
for l in lefts_rev
188-
.by_ref()
189-
.take(left_len.saturating_sub(paired_count))
190-
{
191-
let (next_writer, el) = writer.add(l);
192-
rights.push(el);
193-
writer = next_writer;
194-
}
195-
// Items were pushed right-to-left; flip them to render order.
196-
rights[excess_start..].reverse();
205+
// Patch paired nodes right-to-left.
206+
for (l, r) in lefts_rev.zip(rights[..paired_count].iter_mut().rev()) {
207+
writer = writer.patch(l, r);
208+
}
197209

198-
// 2. Patch paired nodes right-to-left. lefts_rev now yields positions [paired_count-1 ..
199-
// 0]. rights[..paired_count] is in render order, .rev() walks right-to-left.
200-
for (l, r) in lefts_rev.zip(rights[..paired_count].iter_mut().rev()) {
201-
writer = writer.patch(l, r);
210+
// Flip back to reverse render order.
211+
rights.reverse();
202212
}
203213

204-
// Flip back to reverse render order.
205-
rights.reverse();
206-
207214
writer.slot
208215
}
209216

0 commit comments

Comments
 (0)