[Bug]: ChangeAwareList merges an insert one slot before a pending AddRange, corrupting list order downstream
I maintainer di solito rispondono entro 1 giorno
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- Mezza giornata
- Idoneità per principianti
- 63/100
Direzione di ricerca
The bug is in the AddRange branch of InsertItem in src/DynamicData/List/ChangeAwareList.cs, where the minimum bound uses range.Index - 1 instead of range.Index. Start by reading that branch and the clamping code after it, then write a regression test from the issue's repro (source.Items should match the AsObservableList mirror). Done means that test passes and the existing list-change tests still pass. Leave the RangeChange list-aliasing problem for a separate issue.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the bug 🐞
When a ChangeAwareList<T> has a pending AddRange change and an item is inserted one slot before that range, InsertItem folds the item into the range and moves the range start back by one. The two are not contiguous (the element that was at range.Index - 1 now sits between them), so the emitted AddRange places the range items at the wrong positions. Anything that replays indexes (Clone, AsObservableList, Transform, Bind) ends up with a list whose order differs from the source. There is no exception: the downstream data is silently wrong.
Sort is affected independently of the source: it builds its output with its own ChangeAwareList, so a source changeset of [AddRange, Add] whose sorted positions put the single item one slot before the freshly sorted range produces a corrupted sorted output, even when the source changeset itself is correct.
Step to reproduce
using var source = new SourceList<string>();
source.Add("x");
using var mirror = source.Connect().AsObservableList();
source.Edit(list =>
{
list.AddRange(new[] { "b", "c" }); // appended at index 1
list.Insert(0, "a"); // index 0 == range.Index - 1
});
// source.Items: a, x, b, c
// emitted: AddRange index 0 [a, b, c]
// mirror.Items: a, b, c, x <-- wrong
Reproduction repository
N/A: the repro above is self-contained, and the linked PR adds regression tests.
Expected behavior
mirror.Items equals source.Items (a, x, b, c). The changeset should be [AddRange index 1 [b, c], Add index 0 a], or any other sequence that replays to the same list.
Screenshots 🖼️
N/A
IDE
N/A
Operating system
Any
Version
N/A
Device
N/A
DynamicData Version
9.5.0 and main. The merge window has been unchanged since at least 2019.
Additional information ℹ️
Root cause. src/DynamicData/List/ChangeAwareList.cs, InsertItem, AddRange branch:
var minimum = Math.Max(range.Index - 1, 0);
var maximum = range.Index + range.Count;
var isPartOfRange = index >= minimum && index <= maximum;
A pending range occupies [range.Index, range.Index + range.Count). An inserted item extends it only when it lands inside that span or directly after it, so the valid window is [range.Index, range.Index + range.Count]. The lower bound of range.Index - 1 is off by one, and the clamping and SetStartingIndex code that follows exists only to support that case.
Second case in the same branch. The seeded constructor new ChangeAwareList<T>(items) records an AddRange with index -1. Inserting into that list before CaptureChanges() merges the item one position too far to the right (insertPosition = index - (-1)). The library's own callers (ToObservableChangeSet for ObservableCollection and BindingList) capture immediately after construction, so this is only reachable by custom operators using the public type.
Scope. The other merge branches (Add + insert, Remove + remove, RemoveRange + remove) were checked with exhaustive position tests and a seeded randomized replay test (100k random edit batches) and are correct.
Separate known problem (not addressed by the fix for this issue). RangeChange<T> keeps a reference to the caller's List<T> instead of copying it (items.AsList()). A later single insert that merges into that pending range mutates the caller's list:
var mine = new List<int> { 1, 2 };
source.Edit(list =>
{
list.AddRange(mine);
list.Add(3);
});
// mine is now { 1, 2, 3 }
Fixing it means copying on every AddRange, which has a performance cost, so it should be decided and tracked separately.
- Lingua principale
- C#
- Stelle
- 1.9k
- Fork
- 194
- Merge medio
- 12m
- PR unite (30g)
- 1
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di reactivemarbles/DynamicData
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
reactivemarbles/DynamicData#1229 ·
I maintainer di solito rispondono entro 1 giorno
-
infrastructure
Difficoltà 2/5 1-3 ore Idoneità per principianti 64/100
reactivemarbles/DynamicData#1089 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
[Feature]: Make Avg, StdDev, Maximum and Minimum refresh-aware, consistent with SumForse già presa @dwcullop l’ha presa 14 giorni fa. Apertabreaking change enhancement
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
reactivemarbles/DynamicData#1214 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
reactivemarbles/DynamicData#1196 ·
I maintainer di solito rispondono entro 1 giorno
-
[Bug]: RemoveKey loses entry identity for equal values and produces invalid list indexesForse già presa @dwcullop l’ha presa 51 giorni fa. Apertabug
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
reactivemarbles/DynamicData#1182 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di reactivemarbles/DynamicData
Issue simili
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
immichFrame/ImmichFrame#735 ·
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dimitar-radenkov/Pointframe#318 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
DotHarness/dotcraft#546 ·
I maintainer di solito rispondono entro 1 giorno
-
a-corriger bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
christian-raj/S-Aloha#303 · 1 commento ·
-
Input editor says "Apply Changes" even when no changes have been made.Forse già presa @hcooper l’ha presa oggi. Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 72/100
MobiFlight/MobiFlight-Connector#3526 ·
I maintainer di solito rispondono entro 1 giorno