diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml deleted file mode 100644 index b650d7a..0000000 --- a/.github/workflows/go.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Go - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: 1.16 - - - name: Vet - run: go vet ./... - - - name: Build - run: go build -v ./... - - - name: Test - run: go test -v ./... -coverprofile=coverage.txt -covermode=atomic - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - files: coverage.txt diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 5edb155..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: golangci-lint - -on: - push: - tags: - - v* - branches: - - master - - main - pull_request: - -jobs: - - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: golangci-lint - uses: golangci/golangci-lint-action@v2 diff --git a/README.md b/README.md index 45a0647..aa7893b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # deque -[![GoDoc](https://pkg.go.dev/badge/github.com/gammazero/deque)](https://pkg.go.dev/github.com/gammazero/deque) -[![Build Status](https://github.com/gammazero/deque/actions/workflows/go.yml/badge.svg)](https://github.com/gammazero/deque/actions/workflows/go.yml) -[![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)](LICENSE) +Fork of [github.com/gammazero/deque](https://github.com/gammazero/deque) Fast ring-buffer deque ([double-ended queue](https://en.wikipedia.org/wiki/Double-ended_queue)) implementation. diff --git a/deque.go b/deque.go index 7baed9d..9264628 100644 --- a/deque.go +++ b/deque.go @@ -6,7 +6,7 @@ const minCapacity = 16 // Deque represents a single instance of the deque data structure. type Deque struct { - buf []interface{} + buf []*int head int tail int count int @@ -39,13 +39,13 @@ func New(size ...int) *Deque { minCap <<= 1 } - var buf []interface{} + var buf []*int if capacity != 0 { bufSize := minCap for bufSize < capacity { bufSize <<= 1 } - buf = make([]interface{}, bufSize) + buf = make([]*int, bufSize) } return &Deque{ @@ -67,29 +67,29 @@ func (q *Deque) Len() int { // 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{}) { +func (q *Deque) PushBack(elem int) { q.growIfFull() - q.buf[q.tail] = elem + q.buf[q.tail] = &elem // Calculate new tail position. q.tail = q.next(q.tail) q.count++ } // PushFront prepends an element to the front of the queue. -func (q *Deque) PushFront(elem interface{}) { +func (q *Deque) PushFront(elem int) { q.growIfFull() // Calculate new head position. q.head = q.prev(q.head) - q.buf[q.head] = elem + 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{} { +func (q *Deque) PopFront() int { if q.count <= 0 { panic("deque: PopFront() called on empty queue") } @@ -100,13 +100,13 @@ func (q *Deque) PopFront() interface{} { q.count-- q.shrinkIfExcess() - return ret + 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{} { +func (q *Deque) PopBack() int { if q.count <= 0 { panic("deque: PopBack() called on empty queue") } @@ -120,27 +120,27 @@ func (q *Deque) PopBack() interface{} { q.count-- q.shrinkIfExcess() - return ret + 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{} { +func (q *Deque) Front() int { if q.count <= 0 { panic("deque: Front() called when empty") } - return q.buf[q.head] + 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{} { +func (q *Deque) Back() int { if q.count <= 0 { panic("deque: Back() called when empty") } - return q.buf[q.prev(q.tail)] + return *q.buf[q.prev(q.tail)] } // At returns the element at index i in the queue without removing the element @@ -155,23 +155,23 @@ func (q *Deque) Back() interface{} { // case of a fixed-size circular log buffer: A new entry is pushed onto one end // and when full the oldest is popped from the other end. All the log entries // in the buffer must be readable without altering the buffer contents. -func (q *Deque) At(i int) interface{} { +func (q *Deque) At(i int) int { if i < 0 || i >= q.count { panic("deque: At() called with index out of range") } // bitwise modulus - return q.buf[(q.head+i)&(len(q.buf)-1)] + return *q.buf[(q.head+i)&(len(q.buf)-1)] } // Set puts the element at index i in the queue. Set shares the same purpose // than At() but perform the opposite operation. The index i is the same // index defined by At(). If the index is invalid, the call panics. -func (q *Deque) Set(i int, elem interface{}) { +func (q *Deque) Set(i int, elem int) { if i < 0 || i >= q.count { panic("deque: Set() called with index out of range") } // bitwise modulus - q.buf[(q.head+i)&(len(q.buf)-1)] = elem + q.buf[(q.head+i)&(len(q.buf)-1)] = &elem } // Clear removes all elements from the queue, but retains the current capacity. @@ -270,7 +270,7 @@ func (q *Deque) growIfFull() { if q.minCap == 0 { q.minCap = minCapacity } - q.buf = make([]interface{}, q.minCap) + q.buf = make([]*int, q.minCap) return } q.resize() @@ -287,7 +287,7 @@ func (q *Deque) shrinkIfExcess() { // used to grow the queue when it is full, and also to shrink it when it is // only a quarter full. func (q *Deque) resize() { - newBuf := make([]interface{}, q.count<<1) + newBuf := make([]*int, q.count<<1) if q.tail > q.head { copy(newBuf, q.buf[q.head:q.tail]) } else { diff --git a/deque_test.go b/deque_test.go deleted file mode 100644 index dd11f58..0000000 --- a/deque_test.go +++ /dev/null @@ -1,727 +0,0 @@ -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 TestGrowShrinkBack(t *testing.T) { - var q Deque - size := minCapacity * 2 - - for i := 0; i < size; i++ { - if q.Len() != i { - t.Error("q.Len() =", q.Len(), "expected", i) - } - q.PushBack(i) - } - bufLen := len(q.buf) - - // Remove from back. - for i := size; 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") - } -} - -func TestGrowShrinkFront(t *testing.T) { - var q Deque - size := minCapacity * 2 - - for i := 0; i < size; 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 < size; i++ { - if q.Len() != size-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 TestSimple(t *testing.T) { - var q 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 TestBufferWrap(t *testing.T) { - var q 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 TestBufferWrapReverse(t *testing.T) { - var q 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 TestLen(t *testing.T) { - var q 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 TestBack(t *testing.T) { - var q Deque - - for i := 0; i < minCapacity+5; i++ { - q.PushBack(i) - if q.Back() != i { - t.Errorf("Back returned wrong value") - } - } -} - -func TestNew(t *testing.T) { - minCap := 64 - q := New(0, minCap) - if q.Cap() != 0 { - t.Fatal("should not have allowcated mem yet") - } - q.PushBack("foo") - q.PopFront() - if q.Len() != 0 { - t.Fatal("Len() should return 0") - } - if q.Cap() != minCap { - t.Fatalf("worng capactiy expected %d, got %d", minCap, q.Cap()) - } - - curCap := 128 - q = New(curCap, minCap) - if q.Cap() != curCap { - t.Fatalf("Cap() should return %d, got %d", curCap, q.Cap()) - } - if q.Len() != 0 { - t.Fatalf("Len() should return 0") - } - q.PushBack("foo") - if q.Cap() != curCap { - t.Fatalf("Cap() should return %d, got %d", curCap, q.Cap()) - } -} - -func checkRotate(t *testing.T, size int) { - var q Deque - for i := 0; i < size; i++ { - q.PushBack(i) - } - - for i := 0; i < q.Len(); i++ { - x := i - for n := 0; n < q.Len(); n++ { - if q.At(n) != x { - t.Fatalf("a[%d] != %d after rotate and copy", n, x) - } - x++ - if x == q.Len() { - x = 0 - } - } - q.Rotate(1) - if q.Back().(int) != i { - t.Fatal("wrong value during rotation") - } - } - for i := q.Len() - 1; i >= 0; i-- { - q.Rotate(-1) - if q.Front().(int) != i { - t.Fatal("wrong value during reverse rotation") - } - } -} - -func TestRotate(t *testing.T) { - checkRotate(t, 10) - checkRotate(t, minCapacity) - checkRotate(t, minCapacity+minCapacity/2) - - var q Deque - for i := 0; i < 10; i++ { - q.PushBack(i) - } - q.Rotate(11) - if q.Front() != 1 { - t.Error("rotating 11 places should have been same as one") - } - q.Rotate(-21) - if q.Front() != 0 { - t.Error("rotating -21 places should have been same as one -1") - } - q.Rotate(q.Len()) - if q.Front() != 0 { - t.Error("should not have rotated") - } - q.Clear() - q.PushBack(0) - q.Rotate(13) - if q.Front() != 0 { - t.Error("should not have rotated") - } -} - -func TestAt(t *testing.T) { - var q Deque - - for i := 0; i < 1000; i++ { - q.PushBack(i) - } - - // Front to back. - for j := 0; j < q.Len(); j++ { - if q.At(j).(int) != j { - t.Errorf("index %d doesn't contain %d", j, j) - } - } - - // Back to front - for j := 1; j <= q.Len(); j++ { - if q.At(q.Len()-j).(int) != q.Len()-j { - t.Errorf("index %d doesn't contain %d", q.Len()-j, q.Len()-j) - } - } -} - -func TestSet(t *testing.T) { - var q Deque - - for i := 0; i < 1000; i++ { - q.PushBack(i) - q.Set(i, i+50) - } - - // Front to back. - for j := 0; j < q.Len(); j++ { - if q.At(j).(int) != j+50 { - t.Errorf("index %d doesn't contain %d", j, j+50) - } - } -} - -func TestClear(t *testing.T) { - var q 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 TestInsert(t *testing.T) { - q := new(Deque) - for _, x := range "ABCDEFG" { - q.PushBack(x) - } - insert(q, 4, 'x') // ABCDxEFG - if q.At(4) != 'x' { - t.Error("expected x at position 4") - } - - insert(q, 2, 'y') // AByCDxEFG - if q.At(2) != 'y' { - t.Error("expected y at position 2") - } - if q.At(5) != 'x' { - t.Error("expected x at position 5") - } - - insert(q, 0, 'b') // bAByCDxEFG - if q.Front() != 'b' { - t.Error("expected b inserted at front") - } - - insert(q, q.Len(), 'e') // bAByCDxEFGe - - for i, x := range "bAByCDxEFGe" { - if q.PopFront() != x { - t.Error("expected", x, "at position", i) - } - } -} - -func TestRemove(t *testing.T) { - q := new(Deque) - for _, x := range "ABCDEFG" { - q.PushBack(x) - } - - if remove(q, 4) != 'E' { // ABCDFG - t.Error("expected E from position 4") - } - - if remove(q, 2) != 'C' { // ABDFG - t.Error("expected C at position 2") - } - if q.Back() != 'G' { - t.Error("expected G at back") - } - - if remove(q, 0) != 'A' { // BDFG - t.Error("expected to remove A from front") - } - if q.Front() != 'B' { - t.Error("expected G at back") - } - - if remove(q, q.Len()-1) != 'G' { // BDF - t.Error("expected to remove G from back") - } - if q.Back() != 'F' { - t.Error("expected F at back") - } - - if q.Len() != 3 { - t.Error("wrong length") - } -} - -func TestFrontBackOutOfRangePanics(t *testing.T) { - const msg = "should panic when peeking empty queue" - var q 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) { - var q 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) { - var q 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 TestAtOutOfRangePanics(t *testing.T) { - var q Deque - - q.PushBack(1) - q.PushBack(2) - q.PushBack(3) - - assertPanics(t, "should panic when negative index", func() { - q.At(-4) - }) - - assertPanics(t, "should panic when index greater than length", func() { - q.At(4) - }) -} - -func TestSetOutOfRangePanics(t *testing.T) { - var q Deque - - q.PushBack(1) - q.PushBack(2) - q.PushBack(3) - - assertPanics(t, "should panic when negative index", func() { - q.Set(-4, 1) - }) - - assertPanics(t, "should panic when index greater than length", func() { - q.Set(4, 1) - }) -} - -func TestInsertOutOfRangePanics(t *testing.T) { - q := new(Deque) - - assertPanics(t, "should panic when inserting out of range", func() { - insert(q, 1, "X") - }) - - q.PushBack("A") - - assertPanics(t, "should panic when inserting at negative index", func() { - insert(q, -1, "Y") - }) - - assertPanics(t, "should panic when inserting out of range", func() { - insert(q, 2, "B") - }) -} - -func TestRemoveOutOfRangePanics(t *testing.T) { - q := new(Deque) - - assertPanics(t, "should panic when removing from empty queue", func() { - remove(q, 0) - }) - - q.PushBack("A") - - assertPanics(t, "should panic when removing at negative index", func() { - remove(q, -1) - }) - - assertPanics(t, "should panic when removing out of range", func() { - remove(q, 1) - }) -} - -func TestSetMinCapacity(t *testing.T) { - var q Deque - exp := uint(8) - q.SetMinCapacity(exp) - q.PushBack("A") - if q.minCap != 1< q.Len() { - panic("deque: Insert() called with index out of range") - } - if i == 0 { - q.PushFront(elem) - return - } - if i == q.Len() { - q.PushBack(elem) - return - } - if i <= q.Len()/2 { - q.Rotate(i) - q.PushFront(elem) - q.Rotate(-i) - } else { - rots := q.Len() - i - q.Rotate(-rots) - q.PushBack(elem) - q.Rotate(rots) - } -} - -// remove removes and returns an element from the middle of the queue, at the -// specified index. remove(0) is the same as PopFront() and remove(Len()-1) is -// the same as PopBack(). Complexity is constant plus linear in the lesser of -// the distances between i and either of the ends of the queue. Accepts only -// non-negative index values, and panics if index is out of range. -func remove(q *Deque, i int) interface{} { - if i < 0 || i >= q.Len() { - panic("deque: Remove() called with index out of range") - } - if i == 0 { - return q.PopFront() - } - if i == q.Len()-1 { - return q.PopBack() - } - if i <= q.Len()/2 { - q.Rotate(i) - elem := q.PopFront() - q.Rotate(-i) - return elem - } - rots := q.Len() - 1 - i - q.Rotate(-rots) - elem := q.PopBack() - q.Rotate(rots) - return elem -} diff --git a/go.mod b/go.mod index b440602..4e36457 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/gammazero/deque +module git.gammaspectra.live/S.O.N.G/deque-int -go 1.15 +go 1.17