From af183ea2d3d8c471110a8b36263a4d893901550a Mon Sep 17 00:00:00 2001 From: youdie006 Date: Mon, 7 Sep 2026 10:26:29 +0900 Subject: [PATCH] Clamp the overwrite discard to what the buffer holds In overwrite mode write() advances the read pointer by len(p) - avail to make room. When len(p) is larger than the buffer, that count exceeds the data actually present and the % size wraps it to an arbitrary offset, leaving bytes readable that were never written. New(4).SetOverwrite(true).Write([]byte("abcdef")) returned 2 and left "\x00\x00ab" readable with Length() == 4. TestRingBuffer_OverwriteMode_TwiceFull already asserts the intended semantic - an oversize write keeps the first size bytes - but only exercises a length that is an exact multiple of the size, so the wrap cancels out. --- ring_buffer.go | 4 ++++ ring_buffer_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/ring_buffer.go b/ring_buffer.go index ce9af00..4a08dbc 100644 --- a/ring_buffer.go +++ b/ring_buffer.go @@ -664,6 +664,10 @@ func (r *RingBuffer) write(p []byte) (n int, err error) { if len(p) > avail { // Advance read pointer to make room needed := len(p) - avail + // Never discard more than the buffer actually holds. + if used := r.size - avail; needed > used { + needed = used + } r.r = (r.r + needed) % r.size // If buffer was full, it's no longer full after advancing read pointer r.isFull = false diff --git a/ring_buffer_test.go b/ring_buffer_test.go index 15fdc1a..cd62bdf 100644 --- a/ring_buffer_test.go +++ b/ring_buffer_test.go @@ -2193,3 +2193,33 @@ type result struct { data string err error } + +func TestRingBuffer_OverwriteMode_WriteLargerThanCapacity(t *testing.T) { + for _, tt := range []struct { + name string + prefill string + write string + want string + }{ + {"empty", "", "abcdef", "abcd"}, + {"empty_odd", "", "abcde", "abcd"}, + {"partial", "ab", "cdefgh", "cdef"}, + {"full", "abcd", "efghij", "efgh"}, + } { + t.Run(tt.name, func(t *testing.T) { + rb := New(4).SetOverwrite(true) + if tt.prefill != "" { + if _, err := rb.Write([]byte(tt.prefill)); err != nil { + t.Fatalf("prefill: %v", err) + } + } + n, err := rb.Write([]byte(tt.write)) + if got := string(rb.Bytes(nil)); got != tt.want { + t.Errorf("Bytes = %q, want %q (n=%d err=%v)", got, tt.want, n, err) + } + if rb.Length() != 4 { + t.Errorf("Length = %d, want 4", rb.Length()) + } + }) + } +}