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