fix(marktree): fix edge case bug regarding changing intersections

fix #37867

This bug happens when only one end is moved between different nodes,
but the other end is also moved but within the same node.
When this happens we need the correct previous position even for the
internal move. This code shall be refactored to make the intent clearer,
(and avoid some unnecessary processing) but this is a fix for the observable
bug.

Thanks to KevinGoodsell for providing a deterministic
reproduce using fuzzing techniques.
This commit is contained in:
bfredl
2026-02-24 11:12:56 +01:00
parent 3a4a7a7efb
commit 37c0efb21c
2 changed files with 34 additions and 9 deletions

View File

@@ -1860,16 +1860,17 @@ bool marktree_itr_step_overlap(MarkTree *b, MarkTreeIter *itr, MTPair *pair)
static void swap_keys(MarkTree *b, MarkTreeIter *itr1, MarkTreeIter *itr2, DamageList *damage)
{
if (itr1->x != itr2->x) {
if (mt_paired(rawkey(itr1))) {
kvi_push(*damage, ((Damage){ mt_lookup_key(rawkey(itr1)), itr1->x, itr2->x,
itr1->i, itr2->i }));
}
if (mt_paired(rawkey(itr2))) {
kvi_push(*damage, ((Damage){ mt_lookup_key(rawkey(itr2)), itr2->x, itr1->x,
itr2->i, itr1->i }));
}
// TODO(bfredl): redactor is planned, see TODO comment next to qsort in marktree_splice
if (mt_paired(rawkey(itr1))) {
kvi_push(*damage, ((Damage){ mt_lookup_key(rawkey(itr1)), itr1->x, itr2->x,
itr1->i, itr2->i }));
}
if (mt_paired(rawkey(itr2))) {
kvi_push(*damage, ((Damage){ mt_lookup_key(rawkey(itr2)), itr2->x, itr1->x,
itr2->i, itr1->i }));
}
if (itr1->x != itr2->x) {
uint32_t meta_inc_1[kMTMetaCount];
meta_describe_key(meta_inc_1, rawkey(itr1));
uint32_t meta_inc_2[kMTMetaCount];

View File

@@ -645,4 +645,28 @@ describe('marktree', function()
until not lib.marktree_itr_next_filter(tree, iter, 101, 0, filter)
eq(tablelength(seen), tablelength(shadow))
end)
itp('works with edge case splicing overlapping ranges #37867', function()
local tree = ffi.new('MarkTree[1]') -- zero initialized by luajit
put(tree, 190, 48, false)
put(tree, 48, 48, true)
put(tree, 190, 48, false)
put(tree, 166, 48, false, 166, 48, false)
put(tree, 48, 48, true, 48, 48, false)
put(tree, 48, 48, true, 48, 48, false)
put(tree, 48, 48, true, 255, 48, false)
put(tree, 131, 48, false, 48, 48, false)
put(tree, 131, 48, false, 48, 48, false)
put(tree, 48, 48, true, 131, 48, false)
put(tree, 48, 48, false, 216, 48, false)
put(tree, 172, 48, false, 51, 48, false)
put(tree, 131, 48, false, 131, 48, false)
put(tree, 156, 48, false, 131, 48, false)
put(tree, 135, 48, false, 166, 48, false)
put(tree, 172, 48, false, 250, 48, false)
put(tree, 48, 48, false, 143, 48, false)
ok(lib.marktree_check_intersections(tree))
lib.marktree_splice(tree, 48, 0, 139, 0, 0, 0)
ok(lib.marktree_check_intersections(tree))
end)
end)