Skip to content

Reject non-positive lengths in RingBuffer<T> - #53

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/nice-davinci-ip960j
Sep 17, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
claude/nice-davinci-ip960j

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #50

Problem

RingBuffer<T> passed length straight to AllocateBuffer with no guard, unlike DelayLine(int maxDelaySamples) and SpscRingBuffer<T>(int capacity) in the same directory, which both call ArgumentOutOfRangeException.ThrowIfNegativeOrZero up front.

  • new RingBuffer<int>(0) succeeded silently (Capacity 0, backing array T[0]), then the first PushBack threw an unhandled IndexOutOfRangeException instead of a clear ArgumentOutOfRangeException at construction time.
  • new RingBuffer<int>(-1) was likewise accepted — both NextPower2(0) and NextPower2(-1) evaluate to 0 — and crashed the same way, leaving Length at -1 internally.
  • RingBuffer(T value, int length) was worse: with a negative length the prefill loop never ran, so the object was constructed with a negative internal Length and no error at all.

Change

The guard goes in AllocateBuffer, so all three constructors reject length <= 0 immediately. That also closes the same hole in Resize(int) and Resample(int), which share the allocation path and accepted non-positive lengths with identical consequences. Every affected parameter is named length, so the thrown ParamName is correct at each public entry point.

XML docs on the three constructors, Resize and Resample now carry the matching <exception> element.

Tests

Six tests added to RingBufferTests.cs:

Test Covers
Constructor_NonPositiveLength_Throws RingBuffer<int>(0), RingBuffer<int>(-1)
Constructor_PrefillValue_NonPositiveLength_Throws RingBuffer<int>(42, 0 / -1)
Constructor_PrefillItems_NonPositiveLength_Throws RingBuffer<int>(items, 0 / -1)
Resize_NonPositiveLength_Throws Resize(0), Resize(-1)
Resample_NonPositiveLength_Throws Resample(0), Resample(-1)
Constructor_MinimumLength_IsUsable boundary guard that length == 1 still works

The five validation tests were confirmed to fail without the fix — reverting the guard and re-running gives failed: 5, succeeded: 358. With the guard restored: 363/363 pass, and no existing test or assertion was altered.

The library also builds clean in Release across all five target frameworks (net10.0, net9.0, net8.0, netstandard2.1, netstandard2.0) with 0 warnings — ThrowIfNegativeOrZero resolves via the same polyfill the sibling buffers already depend on.

🤖 Generated with Claude Code

https://claude.ai/code/session_019Z6jU64bBLVThqNZiXkL3c


Generated by Claude Code

RingBuffer<T> passed its length straight to AllocateBuffer with no guard,
unlike DelayLine and SpscRingBuffer in the same directory. new RingBuffer<int>(0)
succeeded silently and threw IndexOutOfRangeException on the first PushBack;
new RingBuffer<int>(-1) did the same while leaving a negative internal Length.
The RingBuffer(T value, int length) overload was worse still: a negative length
skipped the prefill loop entirely and reported no error at all.

Guard in AllocateBuffer so all three constructors, Resize and Resample reject
length <= 0 at the call site, matching the sibling buffers. Resize and Resample
share the same allocation path and had the identical hole.

Fixes #50

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Z6jU64bBLVThqNZiXkL3c
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit ed8af4d into main Sep 17, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the claude/nice-davinci-ip960j branch September 17, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RingBuffer&lt;T&gt; constructors don't validate length, unlike its sibling ring buffers

2 participants