From f8e1575f2c29d73606c6d188188ce087d8b52bf6 Mon Sep 17 00:00:00 2001 From: Zeal Jagannatha Date: Sat, 27 Sep 2014 13:47:21 -0700 Subject: [PATCH] Initial commit --- README.md | 72 ++++++++++++++++++++++++++ ring.go | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++ ring_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+) create mode 100644 README.md create mode 100644 ring.go create mode 100644 ring_test.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..faca557 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# ring +-- + import "git.sky/shenzhen/ring" + + +## Usage + +```go +var DefaultCapacity int = 10 +``` +The DefaultCapacity of an uninitialized Ring buffer. + +Changing this value only affects ring buffers created after it is changed. + +#### type Ring + +```go +type Ring struct { +} +``` + +Type Ring implements a Circular Buffer. The default value of the Ring struct is +a valid (empty) Ring buffer with capacity DefaultCapacify. + +#### func (Ring) Capacity + +```go +func (r Ring) Capacity() int +``` +Capacity returns the current capacity of the ring buffer. + +#### func (*Ring) Dequeue + +```go +func (r *Ring) Dequeue() interface{} +``` +Dequeue a value from the Ring buffer. + +Returns nil if the ring buffer is empty. + +#### func (*Ring) Enqueue + +```go +func (r *Ring) Enqueue(i interface{}) +``` +Enqueue a value into the Ring buffer. + +#### func (*Ring) Peek + +```go +func (r *Ring) Peek() interface{} +``` +Read the value that Dequeue would have dequeued without actually dequeuing it. + +Returns nil if the ring buffer is empty. + +#### func (*Ring) SetCapacity + +```go +func (r *Ring) SetCapacity(size int) +``` +Set the maximum size of the ring buffer. + +#### func (*Ring) Values + +```go +func (r *Ring) Values() []interface{} +``` +Values returns a slice of all the values in the circular buffer without +modifying them at all. The returned slice can be modified independently of the +circular buffer. However, the values inside the slice are shared between the +slice and circular buffer. diff --git a/ring.go b/ring.go new file mode 100644 index 0000000..12cde93 --- /dev/null +++ b/ring.go @@ -0,0 +1,141 @@ +package ring + +/* +The DefaultCapacity of an uninitialized Ring buffer. + +Changing this value only affects ring buffers created after it is changed. +*/ +var DefaultCapacity int = 10 + +/* +Type Ring implements a Circular Buffer. +The default value of the Ring struct is a valid (empty) Ring buffer with capacity DefaultCapacify. +*/ +type Ring struct { + head int // the most recent value written + tail int // the least recent value written + buff []interface{} +} + +/* +Set the maximum size of the ring buffer. +*/ +func (r *Ring) SetCapacity(size int) { + r.checkInit() + r.extend(size) +} + +/* +Capacity returns the current capacity of the ring buffer. +*/ +func (r Ring) Capacity() int { + return len(r.buff) +} + +/* +Enqueue a value into the Ring buffer. +*/ +func (r *Ring) Enqueue(i interface{}) { + r.checkInit() + r.set(r.head+1, i) + old := r.head + r.head = r.mod(r.head+1) + if old != -1 && r.head == r.tail { + r.tail = r.mod(r.tail + 1) + } +} + +/* +Dequeue a value from the Ring buffer. + +Returns nil if the ring buffer is empty. +*/ +func (r *Ring) Dequeue() interface{} { + r.checkInit() + if r.head == -1 { + return nil + } + v := r.get(r.tail) + if r.tail == r.head { + r.head = -1 + r.tail = 0 + } else { + r.tail = r.mod(r.tail + 1) + } + return v +} + +/* +Read the value that Dequeue would have dequeued without actually dequeuing it. + +Returns nil if the ring buffer is empty. +*/ +func (r *Ring) Peek() interface{} { + r.checkInit() + if r.head == -1 { + return nil + } + return r.get(r.tail) +} + +/* +Values returns a slice of all the values in the circular buffer without modifying them at all. +The returned slice can be modified independently of the circular buffer. However, the values inside the slice +are shared between the slice and circular buffer. +*/ +func (r *Ring) Values() []interface{} { + if r.head == -1 { + return []interface{}{} + } + arr := make([]interface{}, 0, r.Capacity()) + for i := 0; i < r.Capacity(); i++ { + idx := r.mod(i+r.tail) + arr = append(arr, r.get(idx)) + if idx == r.head { + break + } + } + return arr +} + +/** +*** Unexported methods beyond this point. +**/ + +// sets a value at the given unmodified index and returns the modified index of the value +func (r *Ring) set(p int, v interface{}) { + r.buff[r.mod(p)] = v +} + +// gets a value based at a given unmodified index +func (r *Ring) get(p int) interface{} { + return r.buff[r.mod(p)] +} + +// returns the modified index of an unmodified index +func (r *Ring) mod(p int) int { + return p % len(r.buff) +} + +func (r *Ring) checkInit() { + if r.buff == nil { + r.buff = make([]interface{}, DefaultCapacity) + for i := range r.buff { + r.buff[i] = nil + } + r.head, r.tail = -1, 0 + } +} + +func (r *Ring) extend(size int) { + if size == len(r.buff) { + return + } else if size < len(r.buff) { + r.buff = r.buff[0:size] + } + newb := make([]interface{}, size-len(r.buff)) + for i := range newb { + newb[i] = nil + } + r.buff = append(r.buff, newb...) +} diff --git a/ring_test.go b/ring_test.go new file mode 100644 index 0000000..49037f8 --- /dev/null +++ b/ring_test.go @@ -0,0 +1,116 @@ +package ring + +import ( + // "fmt" + "testing" +) + +func TestSetsSize(t *testing.T) { + r := &Ring{} + r.SetCapacity(10) + if r.Capacity() != 10 { + t.Fatal("Size of ring was not 10", r.Capacity()) + } +} + +func TestSavesSomeData(t *testing.T) { + r := Ring{} + r.SetCapacity(10) + for i := 0; i < 7; i++ { + r.Enqueue(i) + } + for i := 0; i < 7; i++ { + x := r.Dequeue() + if x != i { + t.Fatal("Unexpected response", x, "wanted", i) + } + } +} + +func TestReusesBuffer(t *testing.T) { + r := Ring{} + r.SetCapacity(10) + for i := 0; i < 7; i++ { + r.Enqueue(i) + } + for i := 0; i < 7; i++ { + r.Dequeue() + } + for i := 7; i < 14; i++ { + r.Enqueue(i) + } + for i := 7; i < 14; i++ { + x := r.Dequeue() + if x != i { + t.Fatal("Unexpected response", x, "wanted", i) + } + } +} + +func TestOverflowsBuffer(t *testing.T) { + r := Ring{} + r.SetCapacity(10) + for i := 0; i < 20; i++ { + r.Enqueue(i) + } + for i := 10; i < 20; i++ { + x := r.Dequeue() + if x != i { + t.Fatal("Unexpected response", x, "wanted", i) + } + } +} + +func TestPartiallyOverflows(t *testing.T) { + r := Ring{} + r.SetCapacity(10) + for i := 0; i < 15; i++ { + r.Enqueue(i) + } + for i := 5; i < 15; i++ { + x := r.Dequeue() + if x != i { + t.Fatal("Unexpected response", x, "wanted", i) + } + } +} + +func TestPeeks(t *testing.T) { + r := Ring{} + r.SetCapacity(10) + for i := 0; i < 10; i++ { + r.Enqueue(i) + } + for i := 0; i < 10; i++ { + r.Peek() + r.Peek() + x1 := r.Peek() + x := r.Dequeue() + if x != i { + t.Fatal("Unexpected response", x, "wanted", i) + } + if x1 != x { + t.Fatal("Unexpected response", x1, "wanted", x) + } + } +} + +func TestConstructsArr(t *testing.T) { + r := Ring{} + r.SetCapacity(10) + v := r.Values() + if len(v) != 0 { + t.Fatal("Unexpected values", v, "wanted len of", 0) + } + for i := 1; i < 21; i++ { + r.Enqueue(i) + l := i + if l > 10 { + l = 10 + } + v = r.Values() + if len(v) != l { + t.Fatal("Unexpected values", v, "wanted len of", l, "index", i) + } + } +}