Implements CircularBuffer

pull/203/head
Emir Pasic 2 years ago
parent 5b2385446e
commit 9606c1a0e8

@ -39,6 +39,7 @@ Implementation of various data structures and algorithms in Go.
- [Queues](#queues)
- [LinkedListQueue](#linkedlistqueue)
- [ArrayQueue](#arrayqueue)
- [CircularBuffer](#circularbuffer)
- [Functions](#functions)
- [Comparator](#comparator)
- [Iterator](#iterator)
@ -67,40 +68,41 @@ type Container interface {
Size() int
Clear()
Values() []interface{}
String() string
String() string
}
```
Containers are either ordered or unordered. All ordered containers provide [stateful iterators](#iterator) and some of them allow [enumerable functions](#enumerable).
| **Data** | **Structure** | **Ordered** | **[Iterator](#iterator)** | **[Enumerable](#enumerable)** | **Referenced by** |
| :--- | :--- | :---: | :---: | :---: | :---: |
| **Data** | **Structure** | **Ordered** | **[Iterator](#iterator)** | **[Enumerable](#enumerable)** | **Referenced by** |
| :--- |:--------------------------------------| :---: | :---: | :---: | :---: |
| [Lists](#lists) |
| | [ArrayList](#arraylist) | yes | yes* | yes | index |
| | [ArrayList](#arraylist) | yes | yes* | yes | index |
| | [SinglyLinkedList](#singlylinkedlist) | yes | yes | yes | index |
| | [DoublyLinkedList](#doublylinkedlist) | yes | yes* | yes | index |
| [Sets](#sets) |
| | [HashSet](#hashset) | no | no | no | index |
| | [TreeSet](#treeset) | yes | yes* | yes | index |
| | [LinkedHashSet](#linkedhashset) | yes | yes* | yes | index |
| | [HashSet](#hashset) | no | no | no | index |
| | [TreeSet](#treeset) | yes | yes* | yes | index |
| | [LinkedHashSet](#linkedhashset) | yes | yes* | yes | index |
| [Stacks](#stacks) |
| | [LinkedListStack](#linkedliststack) | yes | yes | no | index |
| | [ArrayStack](#arraystack) | yes | yes* | no | index |
| | [LinkedListStack](#linkedliststack) | yes | yes | no | index |
| | [ArrayStack](#arraystack) | yes | yes* | no | index |
| [Maps](#maps) |
| | [HashMap](#hashmap) | no | no | no | key |
| | [TreeMap](#treemap) | yes | yes* | yes | key |
| | [LinkedHashMap](#linkedhashmap) | yes | yes* | yes | key |
| | [HashBidiMap](#hashbidimap) | no | no | no | key* |
| | [TreeBidiMap](#treebidimap) | yes | yes* | yes | key* |
| | [HashMap](#hashmap) | no | no | no | key |
| | [TreeMap](#treemap) | yes | yes* | yes | key |
| | [LinkedHashMap](#linkedhashmap) | yes | yes* | yes | key |
| | [HashBidiMap](#hashbidimap) | no | no | no | key* |
| | [TreeBidiMap](#treebidimap) | yes | yes* | yes | key* |
| [Trees](#trees) |
| | [RedBlackTree](#redblacktree) | yes | yes* | no | key |
| | [AVLTree](#avltree) | yes | yes* | no | key |
| | [BTree](#btree) | yes | yes* | no | key |
| | [BinaryHeap](#binaryheap) | yes | yes* | no | index |
| | [RedBlackTree](#redblacktree) | yes | yes* | no | key |
| | [AVLTree](#avltree) | yes | yes* | no | key |
| | [BTree](#btree) | yes | yes* | no | key |
| | [BinaryHeap](#binaryheap) | yes | yes* | no | index |
| [Queues](#queues) |
| | [LinkedListQueue](#linkedlistqueue) | yes | yes | no | index |
| | [ArrayQueue](#arrayqueue) | yes | yes* | no | index |
| | | | <sub><sup>*reversible</sup></sub> | | <sub><sup>*bidirectional</sup></sub> |
| | [LinkedListQueue](#linkedlistqueue) | yes | yes | no | index |
| | [ArrayQueue](#arrayqueue) | yes | yes* | no | index |
| | [CircularBuffer](#circularbuffer) | yes | yes* | no | index |
| | | | <sub><sup>*reversible</sup></sub> | | <sub><sup>*bidirectional</sup></sub> |
### Lists
@ -949,6 +951,39 @@ func main() {
}
```
#### CircularBuffer
A circular buffer, circular [queue](#queues), cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams.
<p align="center"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Circular_Buffer_Animation.gif/400px-Circular_Buffer_Animation.gif" width="300px" height="300px" /></p>
Implements [Queue](#queues), [ReverseIteratorWithIndex](#iteratorwithindex), [JSONSerializer](#jsonserializer) and [JSONDeserializer](#jsondeserializer) interfaces.
```go
package main
import cb "github.com/emirpasic/gods/queues/circularbuffer"
// CircularBufferExample to demonstrate basic usage of CircularBuffer
func main() {
queue := cb.New(3) // empty (max size is 3)
queue.Enqueue(1) // 1
queue.Enqueue(2) // 1, 2
queue.Enqueue(3) // 1, 2, 3
_ = queue.Values() // 1, 2, 3
queue.Enqueue(3) // 4, 2, 3
_, _ = queue.Peek() // 4,true
_, _ = queue.Dequeue() // 4, true
_, _ = queue.Dequeue() // 2, true
_, _ = queue.Dequeue() // 3, true
_, _ = queue.Dequeue() // nil, false (nothing to deque)
queue.Enqueue(1) // 1
queue.Clear() // empty
queue.Empty() // true
_ = queue.Size() // 0
}
```
## Functions
Various helper functions used throughout the library.

@ -0,0 +1,153 @@
// Copyright (c) 2021, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package circularbuffer implements the circular buffer.
//
// In computer science, a circular buffer, circular queue, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams.
//
// Structure is not thread safe.
//
// Reference: https://en.wikipedia.org/wiki/Circular_buffer
package circularbuffer
import (
"fmt"
"strings"
"github.com/emirpasic/gods/queues"
)
// Assert Queue implementation
var _ queues.Queue = (*Queue)(nil)
// Queue holds values in a slice.
type Queue struct {
values []interface{}
start int
end int
full bool
maxSize int
size int
}
// New instantiates a new empty queue with the specified size of maximum number of elements that it can hold.
// This max size of the buffer cannot be changed.
func New(maxSize int) *Queue {
if maxSize < 1 {
panic("Invalid maxSize, should be at least 1")
}
queue := &Queue{maxSize: maxSize}
queue.Clear()
return queue
}
// Enqueue adds a value to the end of the queue
func (queue *Queue) Enqueue(value interface{}) {
if queue.Full() {
queue.Dequeue()
}
queue.values[queue.end] = value
queue.end = queue.end + 1
if queue.end >= queue.maxSize {
queue.end = 0
}
if queue.end == queue.start {
queue.full = true
}
queue.size = queue.calculateSize()
}
// Dequeue removes first element of the queue and returns it, or nil if queue is empty.
// Second return parameter is true, unless the queue was empty and there was nothing to dequeue.
func (queue *Queue) Dequeue() (value interface{}, ok bool) {
if queue.Empty() {
return nil, false
}
value, ok = queue.values[queue.start], true
if value != nil {
queue.values[queue.start] = nil
queue.start = queue.start + 1
if queue.start >= queue.maxSize {
queue.start = 0
}
queue.full = false
}
queue.size = queue.size - 1
return
}
// Peek returns first element of the queue without removing it, or nil if queue is empty.
// Second return parameter is true, unless the queue was empty and there was nothing to peek.
func (queue *Queue) Peek() (value interface{}, ok bool) {
if queue.Empty() {
return nil, false
}
return queue.values[queue.start], true
}
// Empty returns true if queue does not contain any elements.
func (queue *Queue) Empty() bool {
return queue.Size() == 0
}
// Full returns true if the queue is full, i.e. has reached the maximum number of elements that it can hold.
func (queue *Queue) Full() bool {
return queue.Size() == queue.maxSize
}
// Size returns number of elements within the queue.
func (queue *Queue) Size() int {
return queue.size
}
// Clear removes all elements from the queue.
func (queue *Queue) Clear() {
queue.values = make([]interface{}, queue.maxSize, queue.maxSize)
queue.start = 0
queue.end = 0
queue.full = false
queue.size = 0
}
// Values returns all elements in the queue (FIFO order).
func (queue *Queue) Values() []interface{} {
values := make([]interface{}, queue.Size(), queue.Size())
for i := 0; i < queue.Size(); i++ {
values[i] = queue.values[(queue.start+i)%queue.maxSize]
}
return values
}
// String returns a string representation of container
func (queue *Queue) String() string {
str := "CircularBuffer\n"
var values []string
for _, value := range queue.Values() {
values = append(values, fmt.Sprintf("%v", value))
}
str += strings.Join(values, ", ")
return str
}
// Check that the index is within bounds of the list
func (queue *Queue) withinRange(index int) bool {
return index >= 0 && index < queue.size
}
func (queue *Queue) calculateSize() int {
if queue.end < queue.start {
return queue.maxSize - queue.start + queue.end
} else if queue.end == queue.start {
if queue.full {
return queue.maxSize
}
return 0
}
return queue.end - queue.start
}

@ -0,0 +1,687 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package circularbuffer
import (
"encoding/json"
"fmt"
"strings"
"testing"
)
func TestQueueEnqueue(t *testing.T) {
queue := New(3)
if actualValue := queue.Empty(); actualValue != true {
t.Errorf("Got %v expected %v", actualValue, true)
}
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
if actualValue := queue.Values(); actualValue[0].(int) != 1 || actualValue[1].(int) != 2 || actualValue[2].(int) != 3 {
t.Errorf("Got %v expected %v", actualValue, "[1,2,3]")
}
if actualValue := queue.Empty(); actualValue != false {
t.Errorf("Got %v expected %v", actualValue, false)
}
if actualValue := queue.Size(); actualValue != 3 {
t.Errorf("Got %v expected %v", actualValue, 3)
}
if actualValue, ok := queue.Peek(); actualValue != 1 || !ok {
t.Errorf("Got %v expected %v", actualValue, 1)
}
}
func TestQueuePeek(t *testing.T) {
queue := New(3)
if actualValue, ok := queue.Peek(); actualValue != nil || ok {
t.Errorf("Got %v expected %v", actualValue, nil)
}
queue.Enqueue(1)
queue.Enqueue(2)
queue.Enqueue(3)
if actualValue, ok := queue.Peek(); actualValue != 1 || !ok {
t.Errorf("Got %v expected %v", actualValue, 1)
}
}
func TestQueueDequeue(t *testing.T) {
queue := New(3)
if actualValue, expectedValue := queue.Empty(), true; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Full(), false; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Size(), 0; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Enqueue(1)
if actualValue, expectedValue := queue.Size(), 1; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Enqueue(2)
if actualValue, expectedValue := queue.Size(), 2; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Enqueue(3)
if actualValue, expectedValue := queue.Size(), 3; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Empty(), false; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Full(), true; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Dequeue()
if actualValue, expectedValue := queue.Size(), 2; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Peek(); actualValue != 2 || !ok {
t.Errorf("Got %v expected %v", actualValue, 2)
}
if actualValue, expectedValue := queue.Size(), 2; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Dequeue(); actualValue != 2 || !ok {
t.Errorf("Got %v expected %v", actualValue, 2)
}
if actualValue, expectedValue := queue.Size(), 1; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Dequeue(); actualValue != 3 || !ok {
t.Errorf("Got %v expected %v", actualValue, 3)
}
if actualValue, expectedValue := queue.Size(), 0; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Empty(), true; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Full(), false; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Dequeue(); actualValue != nil || ok {
t.Errorf("Got %v expected %v", actualValue, nil)
}
if actualValue, expectedValue := queue.Size(), 0; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue := queue.Empty(); actualValue != true {
t.Errorf("Got %v expected %v", actualValue, true)
}
if actualValue := queue.Full(); actualValue != false {
t.Errorf("Got %v expected %v", actualValue, false)
}
if actualValue := queue.Values(); len(actualValue) != 0 {
t.Errorf("Got %v expected %v", actualValue, "[]")
}
}
func TestQueueDequeueFull(t *testing.T) {
queue := New(2)
if actualValue, expectedValue := queue.Empty(), true; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Full(), false; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Size(), 0; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Enqueue(1)
if actualValue, expectedValue := queue.Size(), 1; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Enqueue(2)
if actualValue, expectedValue := queue.Size(), 2; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Full(), true; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Peek(); actualValue != 1 || !ok {
t.Errorf("Got %v expected %v", actualValue, 2)
}
queue.Enqueue(3) // overwrites 1
if actualValue, expectedValue := queue.Size(), 2; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Peek(); actualValue != 2 || !ok {
t.Errorf("Got %v expected %v", actualValue, 2)
}
if actualValue, ok := queue.Dequeue(); actualValue != 2 || !ok {
t.Errorf("Got %v expected %v", actualValue, 2)
}
if actualValue, expectedValue := queue.Size(), 1; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Peek(); actualValue != 3 || !ok {
t.Errorf("Got %v expected %v", actualValue, 3)
}
if actualValue, expectedValue := queue.Size(), 1; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Dequeue(); actualValue != 3 || !ok {
t.Errorf("Got %v expected %v", actualValue, 3)
}
if actualValue, expectedValue := queue.Size(), 0; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, ok := queue.Dequeue(); actualValue != nil || ok {
t.Errorf("Got %v expected %v", actualValue, nil)
}
if actualValue, expectedValue := queue.Empty(), true; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Full(), false; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue := queue.Values(); len(actualValue) != 0 {
t.Errorf("Got %v expected %v", actualValue, "[]")
}
}
func TestQueueIteratorOnEmpty(t *testing.T) {
queue := New(3)
it := queue.Iterator()
for it.Next() {
t.Errorf("Shouldn't iterate on empty queue")
}
}
func TestQueueIteratorNext(t *testing.T) {
queue := New(3)
queue.Enqueue("a")
queue.Enqueue("b")
queue.Enqueue("c")
it := queue.Iterator()
count := 0
for it.Next() {
count++
index := it.Index()
value := it.Value()
switch index {
case 0:
if actualValue, expectedValue := value, "a"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
case 1:
if actualValue, expectedValue := value, "b"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
case 2:
if actualValue, expectedValue := value, "c"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
default:
t.Errorf("Too many")
}
if actualValue, expectedValue := index, count-1; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
}
if actualValue, expectedValue := count, 3; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Clear()
it = queue.Iterator()
for it.Next() {
t.Errorf("Shouldn't iterate on empty queue")
}
}
func TestQueueIteratorPrev(t *testing.T) {
queue := New(3)
queue.Enqueue("a")
queue.Enqueue("b")
queue.Enqueue("c")
it := queue.Iterator()
for it.Next() {
}
count := 0
for it.Prev() {
count++
index := it.Index()
value := it.Value()
switch index {
case 0:
if actualValue, expectedValue := value, "a"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
case 1:
if actualValue, expectedValue := value, "b"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
case 2:
if actualValue, expectedValue := value, "c"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
default:
t.Errorf("Too many")
}
if actualValue, expectedValue := index, 3-count; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
}
if actualValue, expectedValue := count, 3; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
}
func TestQueueIteratorBegin(t *testing.T) {
queue := New(3)
it := queue.Iterator()
it.Begin()
queue.Enqueue("a")
queue.Enqueue("b")
queue.Enqueue("c")
for it.Next() {
}
it.Begin()
it.Next()
if index, value := it.Index(), it.Value(); index != 0 || value != "a" {
t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "a")
}
}
func TestQueueIteratorEnd(t *testing.T) {
queue := New(3)
it := queue.Iterator()
if index := it.Index(); index != -1 {
t.Errorf("Got %v expected %v", index, -1)
}
it.End()
if index := it.Index(); index != 0 {
t.Errorf("Got %v expected %v", index, 0)
}
queue.Enqueue("a")
queue.Enqueue("b")
queue.Enqueue("c")
it.End()
if index := it.Index(); index != queue.Size() {
t.Errorf("Got %v expected %v", index, queue.Size())
}
it.Prev()
if index, value := it.Index(), it.Value(); index != queue.Size()-1 || value != "c" {
t.Errorf("Got %v,%v expected %v,%v", index, value, queue.Size()-1, "c")
}
}
func TestQueueIteratorFirst(t *testing.T) {
queue := New(3)
it := queue.Iterator()
if actualValue, expectedValue := it.First(), false; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Enqueue("a")
queue.Enqueue("b")
queue.Enqueue("c")
if actualValue, expectedValue := it.First(), true; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if index, value := it.Index(), it.Value(); index != 0 || value != "a" {
t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "a")
}
}
func TestQueueIteratorLast(t *testing.T) {
queue := New(3)
it := queue.Iterator()
if actualValue, expectedValue := it.Last(), false; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
queue.Enqueue("a")
queue.Enqueue("b")
queue.Enqueue("c")
if actualValue, expectedValue := it.Last(), true; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if index, value := it.Index(), it.Value(); index != 2 || value != "c" {
t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "c")
}
}
func TestQueueIteratorNextTo(t *testing.T) {
// Sample seek function, i.e. string starting with "b"
seek := func(index int, value interface{}) bool {
return strings.HasSuffix(value.(string), "b")
}
// NextTo (empty)
{
queue := New(3)
it := queue.Iterator()
for it.NextTo(seek) {
t.Errorf("Shouldn't iterate on empty queue")
}
}
// NextTo (not found)
{
queue := New(3)
queue.Enqueue("xx")
queue.Enqueue("yy")
it := queue.Iterator()
for it.NextTo(seek) {
t.Errorf("Shouldn't iterate on empty queue")
}
}
// NextTo (found)
{
queue := New(3)
queue.Enqueue("aa")
queue.Enqueue("bb")
queue.Enqueue("cc")
it := queue.Iterator()
it.Begin()
if !it.NextTo(seek) {
t.Errorf("Shouldn't iterate on empty queue")
}
if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" {
t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb")
}
if !it.Next() {
t.Errorf("Should go to first element")
}
if index, value := it.Index(), it.Value(); index != 2 || value.(string) != "cc" {
t.Errorf("Got %v,%v expected %v,%v", index, value, 2, "cc")
}
if it.Next() {
t.Errorf("Should not go past last element")
}
}
}
func TestQueueIteratorPrevTo(t *testing.T) {
// Sample seek function, i.e. string starting with "b"
seek := func(index int, value interface{}) bool {
return strings.HasSuffix(value.(string), "b")
}
// PrevTo (empty)
{
queue := New(3)
it := queue.Iterator()
it.End()
for it.PrevTo(seek) {
t.Errorf("Shouldn't iterate on empty queue")
}
}
// PrevTo (not found)
{
queue := New(3)
queue.Enqueue("xx")
queue.Enqueue("yy")
it := queue.Iterator()
it.End()
for it.PrevTo(seek) {
t.Errorf("Shouldn't iterate on empty queue")
}
}
// PrevTo (found)
{
queue := New(3)
queue.Enqueue("aa")
queue.Enqueue("bb")
queue.Enqueue("cc")
it := queue.Iterator()
it.End()
if !it.PrevTo(seek) {
t.Errorf("Shouldn't iterate on empty queue")
}
if index, value := it.Index(), it.Value(); index != 1 || value.(string) != "bb" {
t.Errorf("Got %v,%v expected %v,%v", index, value, 1, "bb")
}
if !it.Prev() {
t.Errorf("Should go to first element")
}
if index, value := it.Index(), it.Value(); index != 0 || value.(string) != "aa" {
t.Errorf("Got %v,%v expected %v,%v", index, value, 0, "aa")
}
if it.Prev() {
t.Errorf("Should not go before first element")
}
}
}
func TestQueueIterator(t *testing.T) {
queue := New(2)
queue.Enqueue("a")
queue.Enqueue("b")
queue.Enqueue("c") // overwrites "a"
it := queue.Iterator()
if actualIndex, expectedIndex := it.Index(), -1; actualIndex != expectedIndex {
t.Errorf("Got %v expected %v", actualIndex, expectedIndex)
}
if !it.Next() {
t.Errorf("Still has some")
}
if actualValue, actualIndex, expectedValue, expectedIndex := it.Value(), it.Index(), "b", 0; actualValue != expectedValue || actualIndex != expectedIndex {
t.Errorf("Got %v expected %v, Got %v expected %v", actualValue, expectedValue, actualIndex, expectedIndex)
}
if !it.Next() {
t.Errorf("Still has some")
}
if actualValue, actualIndex, expectedValue, expectedIndex := it.Value(), it.Index(), "c", 1; actualValue != expectedValue || actualIndex != expectedIndex {
t.Errorf("Got %v expected %v, Got %v expected %v", actualValue, expectedValue, actualIndex, expectedIndex)
}
if it.Next() {
t.Errorf("Should have reached the end")
}
if actualIndex, expectedIndex := it.Index(), 2; actualIndex != expectedIndex {
t.Errorf("Got %v expected %v", actualIndex, expectedIndex)
}
if it.Next() {
t.Errorf("Should have reached the end")
}
if !it.Prev() {
t.Errorf("Still has some")
}
if actualValue, actualIndex, expectedValue, expectedIndex := it.Value(), it.Index(), "c", 1; actualValue != expectedValue || actualIndex != expectedIndex {
t.Errorf("Got %v expected %v, Got %v expected %v", actualValue, expectedValue, actualIndex, expectedIndex)
}
if !it.Prev() {
t.Errorf("Still has some")
}
if actualValue, actualIndex, expectedValue, expectedIndex := it.Value(), it.Index(), "b", 0; actualValue != expectedValue || actualIndex != expectedIndex {
t.Errorf("Got %v expected %v, Got %v expected %v", actualValue, expectedValue, actualIndex, expectedIndex)
}
if it.Prev() {
t.Errorf("Nothing before this")
}
if actualIndex, expectedIndex := it.Index(), -1; actualIndex != expectedIndex {
t.Errorf("Got %v expected %v", actualIndex, expectedIndex)
}
}
func TestQueueSerialization(t *testing.T) {
queue := New(3)
queue.Enqueue("a")
queue.Enqueue("b")
queue.Enqueue("c")
var err error
assert := func() {
if actualValue, expectedValue := fmt.Sprintf("%s%s%s", queue.Values()...), "abc"; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if actualValue, expectedValue := queue.Size(), 3; actualValue != expectedValue {
t.Errorf("Got %v expected %v", actualValue, expectedValue)
}
if err != nil {
t.Errorf("Got error %v", err)
}
}
assert()
bytes, err := queue.ToJSON()
assert()
err = queue.FromJSON(bytes)
assert()
bytes, err = json.Marshal([]interface{}{"a", "b", "c", queue})
if err != nil {
t.Errorf("Got error %v", err)
}
err = json.Unmarshal([]byte(`[1,2,3]`), &queue)
if err != nil {
t.Errorf("Got error %v", err)
}
}
func TestQueueString(t *testing.T) {
c := New(3)
c.Enqueue(1)
if !strings.HasPrefix(c.String(), "CircularBuffer") {
t.Errorf("String should start with container name")
}
}
func benchmarkEnqueue(b *testing.B, queue *Queue, size int) {
for i := 0; i < b.N; i++ {
for n := 0; n < size; n++ {
queue.Enqueue(n)
}
}
}
func benchmarkDequeue(b *testing.B, queue *Queue, size int) {
for i := 0; i < b.N; i++ {
for n := 0; n < size; n++ {
queue.Dequeue()
}
}
}
func BenchmarkArrayQueueDequeue100(b *testing.B) {
b.StopTimer()
size := 100
queue := New(3)
for n := 0; n < size; n++ {
queue.Enqueue(n)
}
b.StartTimer()
benchmarkDequeue(b, queue, size)
}
func BenchmarkArrayQueueDequeue1000(b *testing.B) {
b.StopTimer()
size := 1000
queue := New(3)
for n := 0; n < size; n++ {
queue.Enqueue(n)
}
b.StartTimer()
benchmarkDequeue(b, queue, size)
}
func BenchmarkArrayQueueDequeue10000(b *testing.B) {
b.StopTimer()
size := 10000
queue := New(3)
for n := 0; n < size; n++ {
queue.Enqueue(n)
}
b.StartTimer()
benchmarkDequeue(b, queue, size)
}
func BenchmarkArrayQueueDequeue100000(b *testing.B) {
b.StopTimer()
size := 100000
queue := New(3)
for n := 0; n < size; n++ {
queue.Enqueue(n)
}
b.StartTimer()
benchmarkDequeue(b, queue, size)
}
func BenchmarkArrayQueueEnqueue100(b *testing.B) {
b.StopTimer()
size := 100
queue := New(3)
b.StartTimer()
benchmarkEnqueue(b, queue, size)
}
func BenchmarkArrayQueueEnqueue1000(b *testing.B) {
b.StopTimer()
size := 1000
queue := New(3)
for n := 0; n < size; n++ {
queue.Enqueue(n)
}
b.StartTimer()
benchmarkEnqueue(b, queue, size)
}
func BenchmarkArrayQueueEnqueue10000(b *testing.B) {
b.StopTimer()
size := 10000
queue := New(3)
for n := 0; n < size; n++ {
queue.Enqueue(n)
}
b.StartTimer()
benchmarkEnqueue(b, queue, size)
}
func BenchmarkArrayQueueEnqueue100000(b *testing.B) {
b.StopTimer()
size := 100000
queue := New(3)
for n := 0; n < size; n++ {
queue.Enqueue(n)
}
b.StartTimer()
benchmarkEnqueue(b, queue, size)
}

@ -0,0 +1,112 @@
// Copyright (c) 2021, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package circularbuffer
import "github.com/emirpasic/gods/containers"
// Assert Iterator implementation
var _ containers.ReverseIteratorWithIndex = (*Iterator)(nil)
// Iterator returns a stateful iterator whose values can be fetched by an index.
type Iterator struct {
queue *Queue
index int
}
// Iterator returns a stateful iterator whose values can be fetched by an index.
func (queue *Queue) Iterator() Iterator {
return Iterator{queue: queue, index: -1}
}
// Next moves the iterator to the next element and returns true if there was a next element in the container.
// If Next() returns true, then next element's index and value can be retrieved by Index() and Value().
// If Next() was called for the first time, then it will point the iterator to the first element if it exists.
// Modifies the state of the iterator.
func (iterator *Iterator) Next() bool {
if iterator.index < iterator.queue.size {
iterator.index++
}
return iterator.queue.withinRange(iterator.index)
}
// Prev moves the iterator to the previous element and returns true if there was a previous element in the container.
// If Prev() returns true, then previous element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Prev() bool {
if iterator.index >= 0 {
iterator.index--
}
return iterator.queue.withinRange(iterator.index)
}
// Value returns the current element's value.
// Does not modify the state of the iterator.
func (iterator *Iterator) Value() interface{} {
index := (iterator.index + iterator.queue.start) % iterator.queue.maxSize
value := iterator.queue.values[index]
return value
}
// Index returns the current element's index.
// Does not modify the state of the iterator.
func (iterator *Iterator) Index() int {
return iterator.index
}
// Begin resets the iterator to its initial state (one-before-first)
// Call Next() to fetch the first element if any.
func (iterator *Iterator) Begin() {
iterator.index = -1
}
// End moves the iterator past the last element (one-past-the-end).
// Call Prev() to fetch the last element if any.
func (iterator *Iterator) End() {
iterator.index = iterator.queue.size
}
// First moves the iterator to the first element and returns true if there was a first element in the container.
// If First() returns true, then first element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) First() bool {
iterator.Begin()
return iterator.Next()
}
// Last moves the iterator to the last element and returns true if there was a last element in the container.
// If Last() returns true, then last element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) Last() bool {
iterator.End()
return iterator.Prev()
}
// NextTo moves the iterator to the next element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If NextTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) NextTo(f func(index int, value interface{}) bool) bool {
for iterator.Next() {
index, value := iterator.Index(), iterator.Value()
if f(index, value) {
return true
}
}
return false
}
// PrevTo moves the iterator to the previous element from current position that satisfies the condition given by the
// passed function, and returns true if there was a next element in the container.
// If PrevTo() returns true, then next element's index and value can be retrieved by Index() and Value().
// Modifies the state of the iterator.
func (iterator *Iterator) PrevTo(f func(index int, value interface{}) bool) bool {
for iterator.Prev() {
index, value := iterator.Index(), iterator.Value()
if f(index, value) {
return true
}
}
return false
}

@ -0,0 +1,41 @@
// Copyright (c) 2015, Emir Pasic. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package circularbuffer
import (
"encoding/json"
"github.com/emirpasic/gods/containers"
)
// Assert Serialization implementation
var _ containers.JSONSerializer = (*Queue)(nil)
var _ containers.JSONDeserializer = (*Queue)(nil)
// ToJSON outputs the JSON representation of queue's elements.
func (queue *Queue) ToJSON() ([]byte, error) {
return json.Marshal(queue.values[:queue.maxSize])
}
// FromJSON populates list's elements from the input JSON representation.
func (queue *Queue) FromJSON(data []byte) error {
var values []interface{}
err := json.Unmarshal(data, &values)
if err == nil {
for _, value := range values {
queue.Enqueue(value)
}
}
return err
}
// UnmarshalJSON @implements json.Unmarshaler
func (queue *Queue) UnmarshalJSON(bytes []byte) error {
return queue.FromJSON(bytes)
}
// MarshalJSON @implements json.Marshaler
func (queue *Queue) MarshalJSON() ([]byte, error) {
return queue.ToJSON()
}
Loading…
Cancel
Save