diff --git a/README.md b/README.md
index 0c7b25bb..0925a5a1 100644
--- a/README.md
+++ b/README.md
@@ -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 |
-| | | | *reversible | | *bidirectional |
+| | [LinkedListQueue](#linkedlistqueue) | yes | yes | no | index |
+| | [ArrayQueue](#arrayqueue) | yes | yes* | no | index |
+| | [CircularBuffer](#circularbuffer) | yes | yes* | no | index |
+| | | | *reversible | | *bidirectional |
### 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.
+
+
![](https://upload.wikimedia.org/wikipedia/commons/thumb/f/fd/Circular_Buffer_Animation.gif/400px-Circular_Buffer_Animation.gif)
+
+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.
diff --git a/queues/circularbuffer/circularbuffer.go b/queues/circularbuffer/circularbuffer.go
new file mode 100644
index 00000000..f74a55b1
--- /dev/null
+++ b/queues/circularbuffer/circularbuffer.go
@@ -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
+}
diff --git a/queues/circularbuffer/circularbuffer_test.go b/queues/circularbuffer/circularbuffer_test.go
new file mode 100644
index 00000000..2f826073
--- /dev/null
+++ b/queues/circularbuffer/circularbuffer_test.go
@@ -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)
+}
diff --git a/queues/circularbuffer/iterator.go b/queues/circularbuffer/iterator.go
new file mode 100644
index 00000000..dae30ce4
--- /dev/null
+++ b/queues/circularbuffer/iterator.go
@@ -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
+}
diff --git a/queues/circularbuffer/serialization.go b/queues/circularbuffer/serialization.go
new file mode 100644
index 00000000..da2543d0
--- /dev/null
+++ b/queues/circularbuffer/serialization.go
@@ -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()
+}