Skip to content

Commit 4a47d93

Browse files
committed
Port of a ring buffer that overwrites prior writes rather than ceases writing.
Tests ported to test overwrite behavior.
1 parent 0f0a02b commit 4a47d93

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

ringbuf/ringbuf.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// License: Apache License 2.0
2+
// Author: David Anderson
3+
// URL: https://code.google.com/p/curvecp/source/browse/ringbuf/ringbuf.go
4+
5+
// Package ringbuf implements a byte ring buffer. The interface is
6+
// close to that of an io.ReadWriter, but note that the semantics
7+
// differ in significant ways, because this ringbuf is an
8+
// implementation detail of the curvecp package, and it was more
9+
// convenient like this.
10+
package ringbuf
11+
12+
type Ringbuf struct {
13+
buf []byte
14+
start, size int
15+
}
16+
17+
// New creates a new ring buffer of the given size.
18+
func New(size int) *Ringbuf {
19+
return &Ringbuf{make([]byte, size), 0, 0}
20+
}
21+
22+
// Write appends all the bytes in b to the buffer, looping and overwriting
23+
// as needed, while incrementing the start to point to the start of the
24+
// buffer.
25+
func (r *Ringbuf) Write(b []byte) {
26+
for len(b) > 0 {
27+
start := (r.start + r.size) % len(r.buf)
28+
29+
// Copy as much as we can from where we are
30+
n := copy(r.buf[start:], b)
31+
b = b[n:]
32+
33+
// Are we already at capacity? Move the start an appropriate
34+
// distance forward depending on how much we copied.
35+
if r.size >= len(r.buf) {
36+
if n <= len(r.buf) {
37+
r.start += n
38+
if r.start >= len(r.buf) {
39+
r.start = 0
40+
}
41+
} else {
42+
r.start = 0
43+
}
44+
}
45+
r.size += n
46+
// Size can't exceed the capacity
47+
if r.size > cap(r.buf) {
48+
r.size = cap(r.buf)
49+
}
50+
}
51+
}
52+
53+
// Read reads as many bytes as possible from the ring buffer into
54+
// b. Returns the number of bytes read.
55+
func (r *Ringbuf) Read(b []byte) int {
56+
read := 0
57+
size := r.size
58+
start := r.start
59+
for len(b) > 0 && size > 0 {
60+
end := start + size
61+
if end > len(r.buf) {
62+
end = len(r.buf)
63+
}
64+
n := copy(b, r.buf[start:end])
65+
size -= n
66+
read += n
67+
b = b[n:]
68+
start = (start + n) % len(r.buf)
69+
}
70+
return read
71+
}
72+
73+
func (r *Ringbuf) Size() int {
74+
return r.size
75+
}

ringbuf/ringbuf_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package ringbuf
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
const (
9+
doRead = iota
10+
doWrite
11+
)
12+
13+
type rbtest struct {
14+
op int
15+
data string
16+
bufSize int
17+
ret int
18+
totalSize int
19+
}
20+
21+
func TestRingbuf(t *testing.T) {
22+
r := New(5)
23+
24+
tab := []rbtest{
25+
{doWrite, "abc", 0, 3, 3},
26+
{doWrite, "d", 0, 1, 4},
27+
{doRead, "abcd", 4, 4, 4},
28+
{doWrite, "ef", 0, 2, 5},
29+
{doRead, "bcdef", 5, 5, 5},
30+
{doWrite, "abcdefg", 0, 5, 5},
31+
{doWrite, "fg", 0, 2, 5},
32+
{doRead, "efgfg", 5, 5, 5},
33+
{doWrite, "abracadabra", 0, 11, 5},
34+
{doRead, "dabra", 5, 5, 5},
35+
}
36+
37+
for _, step := range tab {
38+
switch step.op {
39+
case doRead:
40+
b := make([]byte, step.bufSize)
41+
n := r.Read(b)
42+
t.Logf("r.Read(%d) = %d, %#v", step.bufSize, step.ret, string(b[:n]))
43+
if n != step.ret {
44+
t.Errorf("r.Read(%#v) = %d, want %d", b, n, step.ret)
45+
}
46+
if !bytes.Equal(b[:n], []byte(step.data)) {
47+
t.Errorf("b = %#v, want %#v", string(b[:n]), step.data)
48+
}
49+
50+
case doWrite:
51+
r.Write([]byte(step.data))
52+
t.Logf("r.Write(%#v) = %d", string(step.data), step.ret)
53+
}
54+
if r.Size() != step.totalSize {
55+
t.Errorf("r.Size() = %d, want %d", r.Size(), step.totalSize)
56+
}
57+
t.Logf("%#v", r)
58+
}
59+
}

0 commit comments

Comments
 (0)