initial commit
This commit is contained in:
commit
4e0a647678
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
16
.travis.yml
Normal file
16
.travis.yml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- "1.8"
|
||||||
|
- "1.9"
|
||||||
|
- "1.10"
|
||||||
|
- "tip"
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- go vet ./...
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./go.test.sh
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash)
|
56
README.md
Normal file
56
README.md
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# deque
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/gammazero/deque.svg)](https://travis-ci.org/gammazero/deque)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/gammazero/deque)](https://goreportcard.com/report/github.com/gammazero/deque)
|
||||||
|
[![codecov](https://codecov.io/gh/gammazero/deque/branch/master/graph/badge.svg)](https://codecov.io/gh/gammazero/deque)
|
||||||
|
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/gammazero/deque/blob/master/LICENSE)
|
||||||
|
|
||||||
|
A fast ring-buffer deque (double-ended queue) implementation.
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/gammazero/deque?status.png)](https://godoc.org/github.com/gammazero/deque)
|
||||||
|
|
||||||
|
This deque implementation automatically re-sizes by powers of two, growing when additional capacity is needed and shrinking when only a quarter of the capacity is used. This allows bitwise arithmetic for all calculations.
|
||||||
|
|
||||||
|
The ring-buffer implementation significantly improves memory and time performance with fewer GC pauses, compared to implementations based on slices and linked lists.
|
||||||
|
|
||||||
|
For maximum speed, this deque implementation leaves concurrency safety up to the application to provide, however is best for the application if needed at all.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go get github.com/gammazero/deque
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gammazero/deque"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var q deque.Deque
|
||||||
|
q.PushBack("foo")
|
||||||
|
q.PushBack("bar")
|
||||||
|
q.PushBack("baz")
|
||||||
|
|
||||||
|
fmt.Println(q.Len()) // Prints: 3
|
||||||
|
fmt.Println(q.Front()) // Prints: foo
|
||||||
|
fmt.Println(q.Back()) // Prints: baz
|
||||||
|
|
||||||
|
q.PopFront() // remove "foo"
|
||||||
|
q.PopBack() // remove "baz"
|
||||||
|
|
||||||
|
q.PushFront("hello")
|
||||||
|
q.PushBack("world")
|
||||||
|
|
||||||
|
// Print: hello bar world
|
||||||
|
for i := 0; i < q.Len(); i++ {
|
||||||
|
fmt.Print(q.PeekAt(i), " ")
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
```
|
193
deque.go
Normal file
193
deque.go
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
Package deque provides a fast, ring-buffer deque (double-ended queue) that
|
||||||
|
automatically re-sizes by powers of two. This allows bitwise arithmetic for
|
||||||
|
all calculations. The ring-buffer implementation significantly improves memory
|
||||||
|
and time performance with fewer GC pauses, compared to implementations based on
|
||||||
|
slices and linked lists.
|
||||||
|
|
||||||
|
For maximum speed, this deque implementation leaves concurrency safety up to
|
||||||
|
the application to provide, however is best for the application if needed at
|
||||||
|
all.
|
||||||
|
|
||||||
|
Queue (FIFO) operations are supported using PushBack() and PopFront(). Stack
|
||||||
|
(LIFO) operations are supported using PushBack() and PopBack().
|
||||||
|
|
||||||
|
*/
|
||||||
|
package deque
|
||||||
|
|
||||||
|
// minCapacity is the smallest capacity that deque may have.
|
||||||
|
// Must be power of 2 for bitwise modulus: x % n == x & (n - 1).
|
||||||
|
const minCapacity = 16
|
||||||
|
|
||||||
|
// Deque represents a single instance of the deque data structure.
|
||||||
|
type Deque struct {
|
||||||
|
buf []interface{}
|
||||||
|
head int
|
||||||
|
tail int
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of elements currently stored in the queue.
|
||||||
|
func (q *Deque) Len() int {
|
||||||
|
return q.count
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack appends an element to the back of the queue. Implements FIFO when
|
||||||
|
// elements are removed with PopFront(), and LIFO when elements are removed
|
||||||
|
// with PopBack().
|
||||||
|
func (q *Deque) PushBack(elem interface{}) {
|
||||||
|
if q.count == len(q.buf) {
|
||||||
|
q.resize()
|
||||||
|
}
|
||||||
|
|
||||||
|
q.buf[q.tail] = elem
|
||||||
|
// calculate new tail using bitwise modulus
|
||||||
|
q.tail = (q.tail + 1) & (len(q.buf) - 1)
|
||||||
|
q.count++
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushFront prepends an element to the front of the queue.
|
||||||
|
func (q *Deque) PushFront(elem interface{}) {
|
||||||
|
if q.count == len(q.buf) {
|
||||||
|
q.resize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate new head using bitwise modulus
|
||||||
|
q.head = (q.head - 1) & (len(q.buf) - 1)
|
||||||
|
q.buf[q.head] = elem
|
||||||
|
q.count++
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopFront removes and returns the element from the front of the queue.
|
||||||
|
// Implements FIFO when used with PushBack(). If the queue is empty, the call
|
||||||
|
// panics.
|
||||||
|
func (q *Deque) PopFront() interface{} {
|
||||||
|
if q.count <= 0 {
|
||||||
|
panic("deque: PopFront() called on empty queue")
|
||||||
|
}
|
||||||
|
ret := q.buf[q.head]
|
||||||
|
q.buf[q.head] = nil
|
||||||
|
// Calculate new head using bitwise modulus.
|
||||||
|
q.head = (q.head + 1) & (len(q.buf) - 1)
|
||||||
|
q.count--
|
||||||
|
// Resize down if buffer 1/4 full.
|
||||||
|
if len(q.buf) > minCapacity && (q.count<<2) == len(q.buf) {
|
||||||
|
q.resize()
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopBack removes and returns the element from the back of the queue.
|
||||||
|
// Implements LIFO when used with PushBack(). If the queue is empty, the call
|
||||||
|
// panics.
|
||||||
|
func (q *Deque) PopBack() interface{} {
|
||||||
|
if q.count <= 0 {
|
||||||
|
panic("deque: PopBack() called on empty queue")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate new tail using bitwise modulus.
|
||||||
|
q.tail = (q.tail - 1) & (len(q.buf) - 1)
|
||||||
|
|
||||||
|
// Remove value at tail.
|
||||||
|
ret := q.buf[q.tail]
|
||||||
|
q.buf[q.tail] = nil
|
||||||
|
q.count--
|
||||||
|
|
||||||
|
// Resize down if buffer 1/4 full.
|
||||||
|
if len(q.buf) > minCapacity && (q.count<<2) == len(q.buf) {
|
||||||
|
q.resize()
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Front returns the element at the front of the queue. This is the element
|
||||||
|
// that would be returned by PopFront(). This call panics if the queue is
|
||||||
|
// empty.
|
||||||
|
func (q *Deque) Front() interface{} {
|
||||||
|
if q.count <= 0 {
|
||||||
|
panic("deque: Front() called on empty queue")
|
||||||
|
}
|
||||||
|
return q.buf[q.head]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back returns the element at the back of the queue. This is the element
|
||||||
|
// that would be returned by PopBack(). This call panics if the queue is
|
||||||
|
// empty.
|
||||||
|
func (q *Deque) Back() interface{} {
|
||||||
|
if q.count <= 0 {
|
||||||
|
panic("deque: Back() called on empty queue")
|
||||||
|
}
|
||||||
|
// bitwise modulus
|
||||||
|
return q.buf[(q.tail-1)&(len(q.buf)-1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekAt returns the element at index i in the queue. This method accepts
|
||||||
|
// both positive and negative index values. PeekAt(0) refers to the first
|
||||||
|
// (earliest added) element and is the same as Front(). PeekAt(-1) refers to
|
||||||
|
// the last (latest added) element and is the same as Back(). If the index
|
||||||
|
// is invalid, the call panics.
|
||||||
|
func (q *Deque) PeekAt(i int) interface{} {
|
||||||
|
// If indexing backwards, convert to positive index.
|
||||||
|
if i < 0 {
|
||||||
|
i += q.count
|
||||||
|
}
|
||||||
|
if i < 0 || i >= q.count {
|
||||||
|
panic("deque: PeekAt() called with index out of range")
|
||||||
|
}
|
||||||
|
// bitwise modulus
|
||||||
|
return q.buf[(q.head+i)&(len(q.buf)-1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes all elements from the queue, but retains the current capacity.
|
||||||
|
func (q *Deque) Clear() {
|
||||||
|
// bitwise modulus
|
||||||
|
mbits := len(q.buf) - 1
|
||||||
|
for h := q.head; h != q.tail; h = (h + 1) & mbits {
|
||||||
|
q.buf[h] = nil
|
||||||
|
}
|
||||||
|
q.head = 0
|
||||||
|
q.tail = 0
|
||||||
|
q.count = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy copies elements from the queue to the destination slice, from front to
|
||||||
|
// back. Copy returns the number of elements copied, which will be the minimum
|
||||||
|
// of q.Len() and len(dst).
|
||||||
|
func (q *Deque) Copy(dst []interface{}) int {
|
||||||
|
count := q.count
|
||||||
|
if len(dst) < q.count {
|
||||||
|
count = len(dst)
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if q.head+count <= len(q.buf) {
|
||||||
|
copy(dst, q.buf[q.head:q.head+count])
|
||||||
|
} else {
|
||||||
|
n := copy(dst, q.buf[q.head:])
|
||||||
|
copy(dst[n:], q.buf[:count-n])
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize resizes the deque to fit exactly twice its current contents.
|
||||||
|
// This results in shrinking if the queue is less than half-full, or growing
|
||||||
|
// the queue when it is full.
|
||||||
|
func (q *Deque) resize() {
|
||||||
|
if len(q.buf) == 0 {
|
||||||
|
q.buf = make([]interface{}, minCapacity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newBuf := make([]interface{}, q.count<<1)
|
||||||
|
if q.tail > q.head {
|
||||||
|
copy(newBuf, q.buf[q.head:q.tail])
|
||||||
|
} else {
|
||||||
|
n := copy(newBuf, q.buf[q.head:])
|
||||||
|
copy(newBuf[n:], q.buf[:q.tail])
|
||||||
|
}
|
||||||
|
|
||||||
|
q.head = 0
|
||||||
|
q.tail = q.count
|
||||||
|
q.buf = newBuf
|
||||||
|
}
|
507
deque_test.go
Normal file
507
deque_test.go
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
package deque
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestEmpty(t *testing.T) {
|
||||||
|
var q Deque
|
||||||
|
if q.Len() != 0 {
|
||||||
|
t.Error("q.Len() =", q.Len(), "expect 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFrontBack(t *testing.T) {
|
||||||
|
var q Deque
|
||||||
|
q.PushBack("foo")
|
||||||
|
q.PushBack("bar")
|
||||||
|
q.PushBack("baz")
|
||||||
|
if q.Front() != "foo" {
|
||||||
|
t.Error("wrong value at front of queue")
|
||||||
|
}
|
||||||
|
if q.Back() != "baz" {
|
||||||
|
t.Error("wrong value at back of queue")
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.PopFront() != "foo" {
|
||||||
|
t.Error("wrong value removed from front of queue")
|
||||||
|
}
|
||||||
|
if q.Front() != "bar" {
|
||||||
|
t.Error("wrong value remaining at front of queue")
|
||||||
|
}
|
||||||
|
if q.Back() != "baz" {
|
||||||
|
t.Error("wrong value remaining at back of queue")
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.PopBack() != "baz" {
|
||||||
|
t.Error("wrong value removed from back of queue")
|
||||||
|
}
|
||||||
|
if q.Front() != "bar" {
|
||||||
|
t.Error("wrong value remaining at front of queue")
|
||||||
|
}
|
||||||
|
if q.Back() != "bar" {
|
||||||
|
t.Error("wrong value remaining at back of queue")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGrowShrink(t *testing.T) {
|
||||||
|
var q Deque
|
||||||
|
for i := 0; i < minCapacity*2; i++ {
|
||||||
|
if q.Len() != i {
|
||||||
|
t.Error("q.Len() =", q.Len(), "expected", i)
|
||||||
|
}
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
// Check that all values are as expected.
|
||||||
|
for i := 0; i < minCapacity*2; i++ {
|
||||||
|
if q.PeekAt(i) != i {
|
||||||
|
t.Errorf("q.PeekAt(%d) = %d, expected %d", i, q.PeekAt(i), i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bufLen := len(q.buf)
|
||||||
|
|
||||||
|
// Remove from back.
|
||||||
|
for i := minCapacity * 2; i > 0; i-- {
|
||||||
|
if q.Len() != i {
|
||||||
|
t.Error("q.Len() =", q.Len(), "expected", i)
|
||||||
|
}
|
||||||
|
x := q.PopBack()
|
||||||
|
if x != i-1 {
|
||||||
|
t.Error("q.PopBack() =", x, "expected", i-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if q.Len() != 0 {
|
||||||
|
t.Error("q.Len() =", q.Len(), "expected 0")
|
||||||
|
}
|
||||||
|
if len(q.buf) == bufLen {
|
||||||
|
t.Error("queue buffer did not shrink")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill up queue again.
|
||||||
|
for i := 0; i < minCapacity*2; i++ {
|
||||||
|
if q.Len() != i {
|
||||||
|
t.Error("q.Len() =", q.Len(), "expected", i)
|
||||||
|
}
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
bufLen = len(q.buf)
|
||||||
|
|
||||||
|
// Remove from Front
|
||||||
|
for i := 0; i < minCapacity*2; i++ {
|
||||||
|
if q.Len() != minCapacity*2-i {
|
||||||
|
t.Error("q.Len() =", q.Len(), "expected", minCapacity*2-i)
|
||||||
|
}
|
||||||
|
x := q.PopFront()
|
||||||
|
if x != i {
|
||||||
|
t.Error("q.PopBack() =", x, "expected", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if q.Len() != 0 {
|
||||||
|
t.Error("q.Len() =", q.Len(), "expected 0")
|
||||||
|
}
|
||||||
|
if len(q.buf) == bufLen {
|
||||||
|
t.Error("queue buffer did not shrink")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDequeSimple(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
for i := 0; i < minCapacity; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
for i := 0; i < minCapacity; i++ {
|
||||||
|
if q.Front() != i {
|
||||||
|
t.Error("peek", i, "had value", q.Front())
|
||||||
|
}
|
||||||
|
x := q.PopFront()
|
||||||
|
if x != i {
|
||||||
|
t.Error("remove", i, "had value", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q.Clear()
|
||||||
|
for i := 0; i < minCapacity; i++ {
|
||||||
|
q.PushFront(i)
|
||||||
|
}
|
||||||
|
for i := minCapacity - 1; i >= 0; i-- {
|
||||||
|
x := q.PopFront()
|
||||||
|
if x != i {
|
||||||
|
t.Error("remove", i, "had value", x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDequeWrap(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
for i := 0; i < minCapacity; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
q.PopFront()
|
||||||
|
q.PushBack(minCapacity + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < minCapacity; i++ {
|
||||||
|
if q.Front().(int) != i+3 {
|
||||||
|
t.Error("peek", i, "had value", q.Front())
|
||||||
|
}
|
||||||
|
q.PopFront()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDequeWrapReverse(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
for i := 0; i < minCapacity; i++ {
|
||||||
|
q.PushFront(i)
|
||||||
|
}
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
q.PopBack()
|
||||||
|
q.PushFront(minCapacity + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < minCapacity; i++ {
|
||||||
|
if q.Back().(int) != i+3 {
|
||||||
|
t.Error("peek", i, "had value", q.Front())
|
||||||
|
}
|
||||||
|
q.PopBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDequeLen(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
if q.Len() != 0 {
|
||||||
|
t.Error("empty queue length not 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
if q.Len() != i+1 {
|
||||||
|
t.Error("adding: queue with", i, "elements has length", q.Len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
q.PopFront()
|
||||||
|
if q.Len() != 1000-i-1 {
|
||||||
|
t.Error("removing: queue with", 1000-i-i, "elements has length", q.Len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDequePeekAt(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
for j := 0; j < q.Len(); j++ {
|
||||||
|
if q.PeekAt(j).(int) != j {
|
||||||
|
t.Errorf("index %d doesn't contain %d", j, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDequePeekAtNegative(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
for j := 1; j <= q.Len(); j++ {
|
||||||
|
if q.PeekAt(-j).(int) != q.Len()-j {
|
||||||
|
t.Errorf("index %d doesn't contain %d", -j, q.Len()-j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDequeBack(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
for i := 0; i < minCapacity+5; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
if q.Back() != i {
|
||||||
|
t.Errorf("Back returned wrong value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
a := make([]interface{}, minCapacity)
|
||||||
|
if q.Copy(a) != 0 {
|
||||||
|
t.Error("Copied wrong size, expected 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < minCapacity/2; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
q.PopFront()
|
||||||
|
}
|
||||||
|
for i := 0; i < minCapacity; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
q.Copy(a)
|
||||||
|
for i := range a {
|
||||||
|
if a[i].(int) != i {
|
||||||
|
t.Error("Copy has wrong value at position", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a = []interface{}{}
|
||||||
|
if q.Copy(a) != 0 {
|
||||||
|
t.Error("Copied wrong size, expected 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
a = make([]interface{}, q.Len()/2)
|
||||||
|
if q.Copy(a) != len(a) {
|
||||||
|
t.Error("Copied wrong size, expected", len(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
a = make([]interface{}, q.Len()*2)
|
||||||
|
if q.Copy(a) != q.Len() {
|
||||||
|
t.Error("Copied wrong size", q.Len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRotate(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := make([]interface{}, q.Len())
|
||||||
|
for i := 0; i < q.Len(); i++ {
|
||||||
|
q.Copy(a)
|
||||||
|
x := i
|
||||||
|
for n := range a {
|
||||||
|
if a[n] != x {
|
||||||
|
t.Fatalf("a[%d] != %d after rotate and copy", n, x)
|
||||||
|
}
|
||||||
|
x++
|
||||||
|
if x == q.Len() {
|
||||||
|
x = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v := q.PopFront()
|
||||||
|
if v.(int) != i {
|
||||||
|
t.Fatal("wrong value during rotation")
|
||||||
|
}
|
||||||
|
q.PushBack(v)
|
||||||
|
|
||||||
|
}
|
||||||
|
for i := q.Len() - 1; i >= 0; i-- {
|
||||||
|
v := q.PopBack()
|
||||||
|
if v.(int) != i {
|
||||||
|
t.Fatal("wrong value during reverse rotation")
|
||||||
|
}
|
||||||
|
q.PushFront(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDequeClear(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
if q.Len() != 100 {
|
||||||
|
t.Error("push: queue with 100 elements has length", q.Len())
|
||||||
|
}
|
||||||
|
cap := len(q.buf)
|
||||||
|
q.Clear()
|
||||||
|
if q.Len() != 0 {
|
||||||
|
t.Error("empty queue length not 0 after clear")
|
||||||
|
}
|
||||||
|
if len(q.buf) != cap {
|
||||||
|
t.Error("queue capacity changed after clear")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there are no remaining references after Clear()
|
||||||
|
for i := 0; i < len(q.buf); i++ {
|
||||||
|
if q.buf[i] != nil {
|
||||||
|
t.Error("queue has non-nil deleted elements after Clear()")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeekAtOutOfRangePanics(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
q.PushBack(1)
|
||||||
|
q.PushBack(2)
|
||||||
|
q.PushBack(3)
|
||||||
|
|
||||||
|
assertPanics(t, "should panic when negative index", func() {
|
||||||
|
q.PeekAt(-4)
|
||||||
|
})
|
||||||
|
|
||||||
|
assertPanics(t, "should panic when index greater than length", func() {
|
||||||
|
q.PeekAt(4)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFrontBackOutOfRangePanics(t *testing.T) {
|
||||||
|
const msg = "should panic when peeking empty queue"
|
||||||
|
q := new(Deque)
|
||||||
|
assertPanics(t, msg, func() {
|
||||||
|
q.Front()
|
||||||
|
})
|
||||||
|
assertPanics(t, msg, func() {
|
||||||
|
q.Back()
|
||||||
|
})
|
||||||
|
|
||||||
|
q.PushBack(1)
|
||||||
|
q.PopFront()
|
||||||
|
|
||||||
|
assertPanics(t, msg, func() {
|
||||||
|
q.Front()
|
||||||
|
})
|
||||||
|
assertPanics(t, msg, func() {
|
||||||
|
q.Back()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPopFrontOutOfRangePanics(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
assertPanics(t, "should panic when removing empty queue", func() {
|
||||||
|
q.PopFront()
|
||||||
|
})
|
||||||
|
|
||||||
|
q.PushBack(1)
|
||||||
|
q.PopFront()
|
||||||
|
|
||||||
|
assertPanics(t, "should panic when removing emptied queue", func() {
|
||||||
|
q.PopFront()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPopBackOutOfRangePanics(t *testing.T) {
|
||||||
|
q := new(Deque)
|
||||||
|
|
||||||
|
assertPanics(t, "should panic when removing empty queue", func() {
|
||||||
|
q.PopBack()
|
||||||
|
})
|
||||||
|
|
||||||
|
q.PushBack(1)
|
||||||
|
q.PopBack()
|
||||||
|
|
||||||
|
assertPanics(t, "should panic when removing emptied queue", func() {
|
||||||
|
q.PopBack()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertPanics(t *testing.T, name string, f func()) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r == nil {
|
||||||
|
t.Errorf("%s: didn't panic as expected", name)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size (number of items) of Deque to use for benchmarks.
|
||||||
|
const size = minCapacity + (minCapacity / 2)
|
||||||
|
|
||||||
|
func BenchmarkPushFront(b *testing.B) {
|
||||||
|
var q Deque
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for n := 0; n < size; n++ {
|
||||||
|
q.PushFront(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPushBack(b *testing.B) {
|
||||||
|
var q Deque
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for n := 0; n < size; n++ {
|
||||||
|
q.PushBack(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSerial(b *testing.B) {
|
||||||
|
var q Deque
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for n := 0; n < size; n++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
for n := 0; n < size; n++ {
|
||||||
|
x := q.Front()
|
||||||
|
if q.PopFront() != x {
|
||||||
|
panic("bad PopFront()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSerialReverse(b *testing.B) {
|
||||||
|
var q Deque
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for n := 0; n < size; n++ {
|
||||||
|
q.PushFront(i)
|
||||||
|
}
|
||||||
|
for n := 0; n < size; n++ {
|
||||||
|
x := q.Back()
|
||||||
|
if q.PopBack() != x {
|
||||||
|
panic("bad PopBack()")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRotate(b *testing.B) {
|
||||||
|
q := new(Deque)
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for j := 0; j < size; j++ {
|
||||||
|
v := q.PopFront()
|
||||||
|
q.PushBack(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRotateReverse(b *testing.B) {
|
||||||
|
q := new(Deque)
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for j := 0; j < size; j++ {
|
||||||
|
v := q.PopBack()
|
||||||
|
q.PushFront(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDequePeekAt(b *testing.B) {
|
||||||
|
q := new(Deque)
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
q.PushBack(i)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for j := 0; j < size; j++ {
|
||||||
|
q.PeekAt(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDequePushPop(b *testing.B) {
|
||||||
|
q := new(Deque)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for n := 0; n < size; n++ {
|
||||||
|
q.PushBack(nil)
|
||||||
|
q.PopFront()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
go.test.sh
Executable file
12
go.test.sh
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
echo "" > coverage.txt
|
||||||
|
|
||||||
|
for d in $(go list ./... | grep -v vendor); do
|
||||||
|
go test -race -coverprofile=profile.out -covermode=atomic $d
|
||||||
|
if [ -f profile.out ]; then
|
||||||
|
cat profile.out >> coverage.txt
|
||||||
|
rm profile.out
|
||||||
|
fi
|
||||||
|
done
|
Loading…
Reference in a new issue