forked from neuvector/neuvector
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'upstream/main' into main
- Loading branch information
Showing
4 changed files
with
95 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,113 +1,54 @@ | ||
// package ringbuffer implements a sequential compact FIFO and LILO. Also called a Queue. | ||
// To Use: | ||
// | ||
// type myThing ringbuffer.RingElement | ||
// var whatever == myThing("whatever") // Assuming a conversion from string. | ||
// rb := RingBuffer.New(40) | ||
// rb.Write(myThing) // Et cetera | ||
// aThing := rb.Read() | ||
// for 0 < rb.Size() { | ||
// doSomethingWith(rb.Read()) | ||
// } | ||
// | ||
// THIS IS NOT CONCURRENT —— ONE GOROUTINE ONLY. | ||
// THIS IS NOT CONCURRENT —— ONE GOROUTINE ONLY. | ||
package ringbuffer | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// A ring buffer is stored in an array of ringbuffer.RingElement, of the size requested. | ||
type RingElement interface{} | ||
|
||
type Element interface{} | ||
type RingBuffer struct { | ||
data []RingElement | ||
in, out int // Place of next in (Write). Place of next out (Read). These are subscripts. | ||
size int // Number of items currenly in the ring buffer. | ||
} | ||
|
||
type RingBufferError struct { | ||
What string | ||
} | ||
|
||
// "Convert" ringbuffer.RingBufferError into a string. | ||
func (e *RingBufferError) Error() string { | ||
return e.What | ||
} | ||
|
||
// /// Inspect the internal state of the ring buffer and complain if not ok. //// | ||
var invNum int // invNum is an error code. | ||
|
||
// New() allocates and initializes a new ring buffer of specified size | ||
func New(n int) *RingBuffer { | ||
b := &RingBuffer{data: make([]RingElement, n), // Contents | ||
in: 0, out: 0, size: 0} | ||
return b | ||
} | ||
|
||
// next() does a 'wrapping increment' of a subscript to point to the next element. | ||
func (b *RingBuffer) next(subscript int) int { | ||
subscript++ | ||
if subscript >= cap(b.data) { // I suspect this is quicker than a modulus calculation. | ||
subscript = 0 | ||
data []Element | ||
head int | ||
tail int | ||
size int | ||
} | ||
|
||
func New(capacity int) *RingBuffer { | ||
return &RingBuffer{ | ||
data: make([]Element, capacity+1), // +1 | ||
head: 0, | ||
tail: 0, // empty | ||
size: capacity + 1, | ||
} | ||
return subscript | ||
} | ||
|
||
// Write inserts an element into the ring buffer. | ||
func (b *RingBuffer) Write(datum RingElement) error { | ||
if b.size >= cap(b.data) { | ||
//fmt.Printf("!Full b %p, size %d, cap %d\n", b, b.size, cap(b.data)) | ||
b.Read() // dump the oldest element | ||
} | ||
|
||
b.data[b.in] = datum | ||
b.in = b.next(b.in) | ||
b.size++ | ||
if 0 >= b.size { | ||
fmt.Printf("\n\tError: b.size %d, b %p, cap(b.data) %d\n", b.size, b, cap(b.data)) | ||
func (r *RingBuffer) Write(value Element) { | ||
r.data[r.tail] = value | ||
r.tail = (r.tail + 1) % r.size | ||
if r.tail == r.head { | ||
r.head = (r.head + 1) % r.size // next | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Read fetches the next element from the ring buffer. | ||
func (b *RingBuffer) Read() RingElement { | ||
if 0 >= b.size { | ||
return nil // Nil is our EOF. Could use an error return, too. | ||
//return &RingBufferError{"RingBuffer is empty\n"} | ||
func (r *RingBuffer) Read(i int) Element { | ||
if r.head == r.tail { | ||
return nil // Buffer is empty | ||
} | ||
b.size-- | ||
tmp := b.data[b.out] | ||
b.out = b.next(b.out) | ||
return tmp | ||
} | ||
|
||
// Number of slots currently in use. Total writes - Total reads. | ||
func (b *RingBuffer) Leng() int { | ||
return b.size | ||
return r.data[(r.head+i)%r.size] | ||
} | ||
|
||
// Is the buffer currently full? | ||
func (b *RingBuffer) Full() bool { | ||
if nil == b { | ||
return true // best we can do? Even Possible? | ||
func (r *RingBuffer) Length() int { | ||
if r.tail >= r.head { | ||
return r.tail - r.head | ||
} | ||
return (nil != b.data) && | ||
(b.size >= cap(b.data)) | ||
return r.tail + r.size - r.head | ||
} | ||
|
||
// Any left to read? | ||
func (b *RingBuffer) HasAny() bool { | ||
return b.size > 0 | ||
func (r *RingBuffer) Clear() { | ||
r.head, r.tail, r.size = 0, 0, 0 | ||
clear(r.data) | ||
} | ||
|
||
// Obliterate, Purge, and Remove the contents of the ring buffer. | ||
// Support your local Garbage Collector! | ||
func (b *RingBuffer) Clear() { | ||
b.in, b.out, b.size = 0, 0, 0 | ||
for i := 0; i < len(b.data); i++ { // Remove dangling references to avoid leaks. | ||
b.data[i] = nil | ||
func (r *RingBuffer) DumpExt() []Element { | ||
var elements []Element | ||
for i := 0; i < r.Length(); i++ { | ||
elements = append(elements, r.Read(i)) // starts from head | ||
} | ||
b.data = nil // Let GC collect the array. | ||
return elements | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package ringbuffer | ||
|
||
import ( | ||
"testing" | ||
// log "github.com/sirupsen/logrus" | ||
) | ||
|
||
func TestRingBuffer_empty(t *testing.T) { | ||
r := New(8) | ||
|
||
history := r.DumpExt() | ||
// log.WithFields(log.Fields{"len": len(history)}).Info("empty") | ||
if len(history) != 0 { | ||
t.Errorf("Error length[%d]: %d\n", 0, len(history)) | ||
} | ||
} | ||
|
||
func TestRingBuffer_in_range(t *testing.T) { | ||
r := New(8) | ||
for i := 0; i < 3; i++ { | ||
r.Write(i) | ||
} | ||
|
||
history := r.DumpExt() | ||
// log.WithFields(log.Fields{"len": len(history)}).Info("in range") | ||
if len(history) != 3 { | ||
t.Errorf("Error length[%d]: %d\n", 3, len(history)) | ||
} | ||
|
||
for i, item := range history { | ||
//log.WithFields(log.Fields{"i": i, "item": item}).Info() | ||
if item != i { | ||
t.Errorf("Error[%d]: [%v,%v]\n", i, i, item) | ||
} | ||
} | ||
} | ||
|
||
func TestRingBuffer_wrap_around(t *testing.T) { | ||
r := New(8) | ||
for i := 0; i < 128; i++ { | ||
r.Write(i) | ||
} | ||
|
||
history := r.DumpExt() | ||
// log.WithFields(log.Fields{"len": len(history)}).Info("wrap around") | ||
if len(history) != 8 { | ||
t.Errorf("Error length[%d]: %d\n", 3, len(history)) | ||
} | ||
|
||
for i, item := range history { | ||
// log.WithFields(log.Fields{"i": i, "item": item}).Info() | ||
if item != (i + 120) { | ||
t.Errorf("Error[%d]: [%v,%v]\n", i, i+120, item) | ||
} | ||
} | ||
} |