Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: TakeSlice method #37

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion deque.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package deque

import "fmt"
import (
"fmt"
"slices"
)

// minCapacity is the smallest capacity that deque may have. Must be power of 2
// for bitwise modulus: x % n == x & (n - 1).
Expand Down Expand Up @@ -432,3 +435,50 @@ func (q *Deque[T]) resize(newSize int) {
q.tail = q.count
q.buf = newBuf
}

// TakeSlice returns the contents of the queue as a contiguous slice,
// leaving the queue empty.
func (q *Deque[T]) TakeSlice() []T {
if q == nil {
return nil
}
if q.head == 0 {
// buffer is in correct order, no need to move memory
} else if q.head < q.tail {
// ..ABCDEFGH..
copy(q.buf, q.buf[q.head:q.tail])
// ABCDEFGHgh..
clear(q.buf[q.count:q.tail])
// ABCDEFGH....
} else {
free := len(q.buf) - q.count
headLen := len(q.buf) - q.head
if free >= headLen {
// Shift tail forwards and copy head to the correct position
// tail head
// v v
// DEFGH....ABC
copy(q.buf[headLen:], q.buf[:q.tail])
// defDEFGH.ABC
copy(q.buf, q.buf[q.head:])
// ABCDEFGH.abc
clear(q.buf[q.head:])
// ABCDEFGH....
} else {
// Rotate in place, same as slices module does it.
// FGH..ABCDE or FGHABCDE
slices.Reverse(q.buf[:q.tail])
// HGF..ABCDE or HGFABCDE
slices.Reverse(q.buf[q.tail:])
// HGFEDCBA.. or HGFEDCBA
slices.Reverse(q.buf[:q.count])
// ABCDEFGH.. or ABCDEFGH
}
}
buf := q.buf[:q.count]
q.head = 0
q.tail = 0
q.count = 0
q.buf = nil
return buf
}
43 changes: 43 additions & 0 deletions deque_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package deque

import (
"fmt"
"slices"
"testing"
"unicode"
)
Expand Down Expand Up @@ -49,6 +50,10 @@ func TestNil(t *testing.T) {
if idx != -1 {
t.Error("should return -1 index for nil deque")
}
slice := q.TakeSlice()
if slice != nil {
t.Error("should return nil slice for nil deque")
}
}

func TestFrontBack(t *testing.T) {
Expand Down Expand Up @@ -797,6 +802,44 @@ func TestRemoveOutOfRangePanics(t *testing.T) {
})
}

func TestTakeSlice(t *testing.T) {
testData := make([]byte, minCapacity)
for i := range testData {
testData[i] = byte('A' + i)
}
for initPos := range minCapacity + 3 {
for length := range len(testData) {
// not running as individual tests to avoid polluting the output
// besides, they are small and fast enough to run all at once
q := new(Deque[byte])
for range initPos {
q.PushBack(255)
q.PopFront()
}
for _, el := range testData[:length] {
q.PushBack(el)
}

slice := q.TakeSlice()
if !slices.Equal(testData[:length], slice) {
t.Errorf("expected %v, got %v (pos %d, len %d)", testData[:length], slice, initPos, length)
}
leftover := slice[len(slice):cap(slice)]
for _, x := range leftover {
if x != 0 {
t.Errorf("slice's capacity was not zeroed out (pos %d, len %d)", initPos, length)
}
}
if len(slice) > 0 && cap(slice) != minCapacity {
t.Errorf("slice's capacity was not preserved (pos %d, len %d)", initPos, length)
}
if q.Len() != 0 || q.Cap() != 0 {
t.Errorf("deque was not cleared (pos %d, len %d)", initPos, length)
}
}
}
}

func TestSetMBaseapacity(t *testing.T) {
var q Deque[string]
q.SetBaseCap(200)
Expand Down