What's wrong
RingBuffer<T>.GetEnumerator() (Containers/RingBuffer.cs, ~line 288) is a plain for (i < Count) yield return At(i) loop with no version check. #70 / PR #84 added fail-fast, versioned enumerators to the other array-backed containers (Contiguous*, OrderedMap, InsertionOrderMap). The List-backed containers were already fail-fast, so RingBuffer is now the only container whose enumerator is not.
Failure scenario
On a full buffer, PushBack advances the front index, which shifts every logical index by one:
var rb = new RingBuffer<int>([1, 2, 3, 4], 4);
foreach (var x in rb) { seen.Add(x); if (x == 1) rb.PushBack(5); }
// seen = 1, 3, 4, 5: element 2 is skipped, and no exception is thrown
Calling rb.Resample(8) inside the same loop gives 1,1,2,2,3,3,4,4, again with no exception. Walking a sliding window while a producer pushes is a common way to use a ring buffer, so it is easy to read corrupted data this way without noticing.
Both cases were confirmed with a temporary MSTest against main (84bb8c4).
Suggested fix
Follow ContiguousCollection:
- add a
version field and increment it in PushBack, Clear, and AllocateBuffer, which covers Resize and Resample
- change
GetEnumerator to => Enumerate(version) and call Enumeration.ThrowIfModified(expected, version) before each yield and after the loop
Acceptance: PushBack, Clear, Resize or Resample called during a foreach throws InvalidOperationException, and a test covers each case.
What's wrong
RingBuffer<T>.GetEnumerator()(Containers/RingBuffer.cs, ~line 288) is a plainfor (i < Count) yield return At(i)loop with no version check. #70 / PR #84 added fail-fast, versioned enumerators to the other array-backed containers (Contiguous*, OrderedMap, InsertionOrderMap). The List-backed containers were already fail-fast, so RingBuffer is now the only container whose enumerator is not.Failure scenario
On a full buffer,
PushBackadvances the front index, which shifts every logical index by one:Calling
rb.Resample(8)inside the same loop gives1,1,2,2,3,3,4,4, again with no exception. Walking a sliding window while a producer pushes is a common way to use a ring buffer, so it is easy to read corrupted data this way without noticing.Both cases were confirmed with a temporary MSTest against
main(84bb8c4).Suggested fix
Follow
ContiguousCollection:versionfield and increment it inPushBack,Clear, andAllocateBuffer, which coversResizeandResampleGetEnumeratorto=> Enumerate(version)and callEnumeration.ThrowIfModified(expected, version)before each yield and after the loopAcceptance:
PushBack,Clear,ResizeorResamplecalled during aforeachthrowsInvalidOperationException, and a test covers each case.