From 77eda70de9c3624aafc1d23eabd3b611419a4395 Mon Sep 17 00:00:00 2001 From: Andrew Morozko Date: Mon, 25 Nov 2024 20:09:38 +0400 Subject: [PATCH] TakeSlice --- deque.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++- deque_test.go | 43 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/deque.go b/deque.go index ff10996..44e9ebd 100644 --- a/deque.go +++ b/deque.go @@ -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). @@ -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 +} diff --git a/deque_test.go b/deque_test.go index c91e6b4..6d7fd49 100644 --- a/deque_test.go +++ b/deque_test.go @@ -2,6 +2,7 @@ package deque import ( "fmt" + "slices" "testing" "unicode" ) @@ -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) { @@ -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)