What's wrong
All three power-of-two buffers round their size up with the same NextPower2 helper (RingBuffer.cs ~L214, SpscRingBuffer.cs ~L202, DelayLine.cs ~L180). For any input above 2^30 the result wraps to int.MinValue, and new T[int.MinValue] throws OverflowException. The constructors only validate the lower bound, and their docs promise ArgumentOutOfRangeException for bad sizes.
RingBuffer<T>(length) and Resize(length) with length > 2^30: NextPower2(length) overflows (~L139).
SpscRingBuffer<T>(capacity) with capacity >= 2^30: NextPower2(capacity + 1) overflows (~L90). With int.MaxValue, the + 1 itself also wraps.
DelayLine(maxDelaySamples) with maxDelaySamples >= 2^30: the same as SpscRingBuffer (~L80).
RingBuffer.Resize is worse. AllocateBuffer (~L134-144) assigns Length and Capacity before allocating. When the allocation throws, the old Buffer stays in place but Capacity is int.MinValue, so the index mask becomes int.MaxValue. Subsequent PushBack calls index past the end of the array.
Repro (verified on net10.0)
new RingBuffer<byte>((1 << 30) + 1); // OverflowException
new SpscRingBuffer<byte>(1 << 30); // OverflowException
new DelayLine(1 << 30); // OverflowException
RingBuffer<int> r = new(4); r.PushBack(1); r.PushBack(2);
try { r.Resize((1 << 30) + 1); } catch (OverflowException) { }
r.PushBack(0); r.PushBack(1); /* ... */ // IndexOutOfRangeException within a few pushes
Suggested fix
- Validate the upper bound up front with
ArgumentOutOfRangeException.ThrowIfGreaterThan, and document the limits:
RingBuffer: length ≤ 1 << 30.
SpscRingBuffer and DelayLine: capacity ≤ (1 << 30) - 1, because they need the extra slot.
- In
RingBuffer.AllocateBuffer, compute and allocate the new array before assigning any field, so a failure leaves the buffer unchanged.
Acceptance criteria
- Oversized constructor and
Resize calls throw ArgumentOutOfRangeException.
- After a rejected
Resize, the buffer still holds its previous contents and PushBack still works.
Same family as the closed #73 (the Resample index overflow) and #50 (the lower-bound check).
What's wrong
All three power-of-two buffers round their size up with the same
NextPower2helper (RingBuffer.cs~L214,SpscRingBuffer.cs~L202,DelayLine.cs~L180). For any input above 2^30 the result wraps toint.MinValue, andnew T[int.MinValue]throwsOverflowException. The constructors only validate the lower bound, and their docs promiseArgumentOutOfRangeExceptionfor bad sizes.RingBuffer<T>(length)andResize(length)withlength > 2^30:NextPower2(length)overflows (~L139).SpscRingBuffer<T>(capacity)withcapacity >= 2^30:NextPower2(capacity + 1)overflows (~L90). Withint.MaxValue, the+ 1itself also wraps.DelayLine(maxDelaySamples)withmaxDelaySamples >= 2^30: the same asSpscRingBuffer(~L80).RingBuffer.Resizeis worse.AllocateBuffer(~L134-144) assignsLengthandCapacitybefore allocating. When the allocation throws, the oldBufferstays in place butCapacityisint.MinValue, so the index mask becomesint.MaxValue. SubsequentPushBackcalls index past the end of the array.Repro (verified on net10.0)
Suggested fix
ArgumentOutOfRangeException.ThrowIfGreaterThan, and document the limits:RingBuffer:length ≤ 1 << 30.SpscRingBufferandDelayLine:capacity ≤ (1 << 30) - 1, because they need the extra slot.RingBuffer.AllocateBuffer, compute and allocate the new array before assigning any field, so a failure leaves the buffer unchanged.Acceptance criteria
Resizecalls throwArgumentOutOfRangeException.Resize, the buffer still holds its previous contents andPushBackstill works.Same family as the closed #73 (the
Resampleindex overflow) and #50 (the lower-bound check).