Skip to content

Commit

Permalink
Implements CircularBuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
emirpasic committed Apr 14, 2022
1 parent 5b23854 commit 9606c1a
Show file tree
Hide file tree
Showing 5 changed files with 1,049 additions and 21 deletions.
77 changes: 56 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down
153 changes: 153 additions & 0 deletions queues/circularbuffer/circularbuffer.go
Original file line number Diff line number Diff line change
@@ -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 comments on commit 9606c1a

Please sign in to comment.