├── .gitattributes ├── go.mod ├── v2 ├── go.mod ├── example_test.go ├── coverage.sh ├── vet.sh ├── benchmark_test.go ├── README.md ├── deque.go └── deque_test.go ├── .gitignore ├── chunkPool.go ├── example_test.go ├── coverage.sh ├── vet.sh ├── LICENSE ├── README.md ├── interface.go ├── benchmark_test.go ├── harden.sh ├── deque.go └── deque_test.go /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/edwingeng/deque 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/edwingeng/deque/v2 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Misc 15 | /.idea 16 | -------------------------------------------------------------------------------- /chunkPool.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | type chunkPool struct { 9 | sync.Pool 10 | numChunksAllocated int64 11 | } 12 | 13 | func newChunkPool(newChunk func() interface{}) *chunkPool { 14 | var x chunkPool 15 | x.New = func() interface{} { 16 | atomic.AddInt64(&x.numChunksAllocated, 1) 17 | return newChunk() 18 | } 19 | return &x 20 | } 21 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import "fmt" 4 | 5 | func Example() { 6 | dq := NewDeque() 7 | dq.PushBack(100) 8 | dq.PushBack(200) 9 | dq.PushBack(300) 10 | for !dq.Empty() { 11 | fmt.Println(dq.PopFront()) 12 | } 13 | 14 | dq.PushFront(100) 15 | dq.PushFront(200) 16 | dq.PushFront(300) 17 | for i, n := 0, dq.Len(); i < n; i++ { 18 | fmt.Println(dq.PopFront()) 19 | } 20 | 21 | // Output: 22 | // 100 23 | // 200 24 | // 300 25 | // 300 26 | // 200 27 | // 100 28 | } 29 | -------------------------------------------------------------------------------- /v2/example_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import "fmt" 4 | 5 | func Example() { 6 | dq := NewDeque[int]() 7 | dq.PushBack(100) 8 | dq.PushBack(200) 9 | dq.PushBack(300) 10 | for !dq.IsEmpty() { 11 | fmt.Println(dq.PopFront()) 12 | } 13 | 14 | dq.PushFront(100) 15 | dq.PushFront(200) 16 | dq.PushFront(300) 17 | for i, n := 0, dq.Len(); i < n; i++ { 18 | fmt.Println(dq.PopFront()) 19 | } 20 | 21 | // Output: 22 | // 100 23 | // 200 24 | // 300 25 | // 300 26 | // 200 27 | // 100 28 | } 29 | -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [[ "$TRACE" ]] && set -x 4 | pushd `dirname "$0"` > /dev/null 5 | trap __EXIT EXIT 6 | 7 | colorful=false 8 | tput setaf 7 > /dev/null 2>&1 9 | if [[ $? -eq 0 ]]; then 10 | colorful=true 11 | fi 12 | 13 | function __EXIT() { 14 | popd > /dev/null 15 | } 16 | 17 | function printError() { 18 | $colorful && tput setaf 1 19 | >&2 echo "Error: $@" 20 | $colorful && tput setaf 7 21 | } 22 | 23 | function printImportantMessage() { 24 | $colorful && tput setaf 3 25 | >&2 echo "$@" 26 | $colorful && tput setaf 7 27 | } 28 | 29 | function printUsage() { 30 | $colorful && tput setaf 3 31 | >&2 echo "$@" 32 | $colorful && tput setaf 7 33 | } 34 | 35 | go test -cover -coverprofile=c.out -v "$@" && go tool cover -html=c.out 36 | -------------------------------------------------------------------------------- /v2/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [[ "$TRACE" ]] && set -x 4 | pushd `dirname "$0"` > /dev/null 5 | trap __EXIT EXIT 6 | 7 | colorful=false 8 | tput setaf 7 > /dev/null 2>&1 9 | if [[ $? -eq 0 ]]; then 10 | colorful=true 11 | fi 12 | 13 | function __EXIT() { 14 | popd > /dev/null 15 | } 16 | 17 | function printError() { 18 | $colorful && tput setaf 1 19 | >&2 echo "Error: $@" 20 | $colorful && tput setaf 7 21 | } 22 | 23 | function printImportantMessage() { 24 | $colorful && tput setaf 3 25 | >&2 echo "$@" 26 | $colorful && tput setaf 7 27 | } 28 | 29 | function printUsage() { 30 | $colorful && tput setaf 3 31 | >&2 echo "$@" 32 | $colorful && tput setaf 7 33 | } 34 | 35 | go test -cover -coverprofile=c.out -v "$@" && go tool cover -html=c.out 36 | -------------------------------------------------------------------------------- /v2/vet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [[ "$TRACE" ]] && set -x 4 | pushd `dirname "$0"` > /dev/null 5 | trap __EXIT EXIT 6 | 7 | colorful=false 8 | tput setaf 7 > /dev/null 2>&1 9 | if [[ $? -eq 0 ]]; then 10 | colorful=true 11 | fi 12 | 13 | function __EXIT() { 14 | popd > /dev/null 15 | } 16 | 17 | function printError() { 18 | $colorful && tput setaf 1 19 | >&2 echo "Error: $@" 20 | $colorful && tput setaf 7 21 | } 22 | 23 | function printImportantMessage() { 24 | $colorful && tput setaf 3 25 | >&2 echo "$@" 26 | $colorful && tput setaf 7 27 | } 28 | 29 | function printUsage() { 30 | $colorful && tput setaf 3 31 | >&2 echo "$@" 32 | $colorful && tput setaf 7 33 | } 34 | 35 | printImportantMessage "====== gofmt" 36 | gofmt -w . 37 | 38 | printImportantMessage "====== go vet" 39 | go vet ./... 40 | 41 | printImportantMessage "====== gocyclo" 42 | gocyclo -over 15 . 43 | 44 | printImportantMessage "====== ineffassign" 45 | ineffassign ./... 46 | 47 | printImportantMessage "====== misspell" 48 | misspell * 49 | -------------------------------------------------------------------------------- /vet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [[ "$TRACE" ]] && set -x 4 | pushd `dirname "$0"` > /dev/null 5 | trap __EXIT EXIT 6 | 7 | colorful=false 8 | tput setaf 7 > /dev/null 2>&1 9 | if [[ $? -eq 0 ]]; then 10 | colorful=true 11 | fi 12 | 13 | function __EXIT() { 14 | popd > /dev/null 15 | } 16 | 17 | function printError() { 18 | $colorful && tput setaf 1 19 | >&2 echo "Error: $@" 20 | $colorful && tput setaf 7 21 | } 22 | 23 | function printImportantMessage() { 24 | $colorful && tput setaf 3 25 | >&2 echo "$@" 26 | $colorful && tput setaf 7 27 | } 28 | 29 | function printUsage() { 30 | $colorful && tput setaf 3 31 | >&2 echo "$@" 32 | $colorful && tput setaf 7 33 | } 34 | 35 | printImportantMessage "====== gofmt" 36 | gofmt -w . 37 | 38 | printImportantMessage "====== go vet" 39 | go vet ./... 40 | 41 | printImportantMessage "====== gocyclo" 42 | gocyclo -over 15 . 43 | 44 | printImportantMessage "====== ineffassign" 45 | ineffassign ./... 46 | 47 | printImportantMessage "====== misspell" 48 | misspell * 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019-2022, Edwin Geng 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /v2/benchmark_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import ( 4 | "container/list" 5 | "math/rand" 6 | "testing" 7 | ) 8 | 9 | func BenchmarkPushBack(b *testing.B) { 10 | b.Run("Deque", func(b *testing.B) { 11 | dq := NewDeque[int]() 12 | for i := 0; i < b.N; i++ { 13 | dq.PushBack(i) 14 | } 15 | }) 16 | b.Run("list.List", func(b *testing.B) { 17 | lst := list.New() 18 | for i := 0; i < b.N; i++ { 19 | lst.PushBack(i) 20 | } 21 | }) 22 | } 23 | 24 | func BenchmarkPushFront(b *testing.B) { 25 | b.Run("Deque", func(b *testing.B) { 26 | dq := NewDeque[int]() 27 | for i := 0; i < b.N; i++ { 28 | dq.PushFront(i) 29 | } 30 | }) 31 | b.Run("list.List", func(b *testing.B) { 32 | lst := list.New() 33 | for i := 0; i < b.N; i++ { 34 | lst.PushFront(i) 35 | } 36 | }) 37 | } 38 | 39 | func BenchmarkRandom(b *testing.B) { 40 | const nn = 10000 41 | a := make([]int, nn) 42 | for i := 0; i < nn; i++ { 43 | a[i] = rand.Int() 44 | } 45 | 46 | b.Run("Deque", func(b *testing.B) { 47 | dq := NewDeque[int]() 48 | for i := 0; i < b.N; i++ { 49 | switch a[i%nn] % 4 { 50 | case 0: 51 | dq.PushBack(i) 52 | case 1: 53 | dq.PushFront(i) 54 | case 2: 55 | dq.TryPopBack() 56 | case 3: 57 | dq.TryPopFront() 58 | } 59 | } 60 | }) 61 | b.Run("list.List", func(b *testing.B) { 62 | lst := list.New() 63 | for i := 0; i < b.N; i++ { 64 | switch a[i%nn] % 4 { 65 | case 0: 66 | lst.PushBack(i) 67 | case 1: 68 | lst.PushFront(i) 69 | case 2: 70 | if v := lst.Back(); v != nil { 71 | lst.Remove(v) 72 | } 73 | case 3: 74 | if v := lst.Front(); v != nil { 75 | lst.Remove(v) 76 | } 77 | } 78 | } 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Please use [v2](v2), the generic version, if you have golang 1.18 or above.** 2 | 3 | # Overview 4 | Deque is a highly optimized double-ended queue, which is 5 | much efficient compared with `list.List` when adding or removing elements from 6 | the beginning or the end. 7 | 8 | # Benchmark 9 | ``` 10 | PushBack/Deque 100000000 12.0 ns/op 8 B/op 0 allocs/op 11 | PushBack/Deque 20000000 55.5 ns/op 24 B/op 1 allocs/op 12 | PushBack/list.List 5000000 158.7 ns/op 56 B/op 1 allocs/op 13 | 14 | PushFront/Deque 195840157 9.2 ns/op 8 B/op 0 allocs/op 15 | PushFront/Deque 30000000 49.2 ns/op 24 B/op 1 allocs/op 16 | PushFront/list.List 5000000 159.2 ns/op 56 B/op 1 allocs/op 17 | 18 | Random/Deque 65623633 15.1 ns/op 0 B/op 0 allocs/op 19 | Random/Deque 50000000 24.7 ns/op 4 B/op 0 allocs/op 20 | Random/list.List 30000000 46.9 ns/op 28 B/op 1 allocs/op 21 | ``` 22 | 23 | # Getting Started 24 | ``` 25 | go get -u github.com/edwingeng/deque 26 | ``` 27 | 28 | # Usage 29 | ``` go 30 | dq := deque.NewDeque() 31 | dq.PushBack(100) 32 | dq.PushBack(200) 33 | dq.PushBack(300) 34 | for !dq.Empty() { 35 | fmt.Println(dq.PopFront()) 36 | } 37 | 38 | dq.PushFront(100) 39 | dq.PushFront(200) 40 | dq.PushFront(300) 41 | for i, n := 0, dq.Len(); i < n; i++ { 42 | fmt.Println(dq.PopFront()) 43 | } 44 | 45 | // Output: 46 | // 100 47 | // 200 48 | // 300 49 | // 300 50 | // 200 51 | // 100 52 | ``` 53 | 54 | # Harden the element data type 55 | ``` bash 56 | ./harden.sh [elemType] 57 | ``` 58 | -------------------------------------------------------------------------------- /interface.go: -------------------------------------------------------------------------------- 1 | // Package deque implements a highly optimized double-ended queue, which is 2 | // much efficient compared with list.List when adding or removing elements from 3 | // the beginning or the end. 4 | package deque 5 | 6 | type Elem = interface{} 7 | 8 | // Deque is a highly optimized double-ended queue. 9 | type Deque interface { 10 | // PushBack adds a new value v at the back of Deque. 11 | PushBack(v Elem) 12 | // PushFront adds a new value v at the front of Deque. 13 | PushFront(v Elem) 14 | // PopBack removes a value from the back of Deque and returns 15 | // the removed value or nil if the Deque is empty. 16 | PopBack() Elem 17 | // PopFront removes a value from the front of Deque and returns 18 | // the removed value or nil if the Deque is empty. 19 | PopFront() Elem 20 | // Back returns the last value of Deque or nil if the Deque is empty. 21 | Back() Elem 22 | // Front returns the first value of Deque or nil if the Deque is empty. 23 | Front() Elem 24 | // Empty returns whether Deque is empty. 25 | Empty() bool 26 | // Len returns the number of values in Deque. 27 | Len() int 28 | 29 | // Enqueue is an alias of PushBack. 30 | Enqueue(v Elem) 31 | // Dequeue is an alias of PopFront. 32 | Dequeue() Elem 33 | // DequeueMany removes a number of values from the front of Deque and 34 | // returns the removed values or nil if the Deque is empty. 35 | // If max <= 0, DequeueMany removes and returns all the values in Deque. 36 | DequeueMany(max int) []Elem 37 | // DequeueManyWithBuffer is similar to DequeueMany except that it uses 38 | // buf to store the removed values as long as it has enough space. 39 | DequeueManyWithBuffer(max int, buf []Elem) []Elem 40 | 41 | // Range iterates all the values in Deque. 42 | Range(f func(i int, v Elem) bool) 43 | 44 | // Peek returns the value at idx. 45 | Peek(idx int) Elem 46 | // Replace replaces the value at idx. 47 | Replace(idx int, v Elem) 48 | } 49 | -------------------------------------------------------------------------------- /benchmark_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | "math/rand" 7 | "reflect" 8 | "regexp" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func benchNameSuffix() string { 14 | var elem Elem 15 | t := reflect.TypeOf(elem) 16 | if t == nil { 17 | return "" 18 | } 19 | rex1 := regexp.MustCompile(`\w+\.`) 20 | str1 := rex1.ReplaceAllString(t.String(), "") 21 | str2 := strings.ReplaceAll(str1, "interface {}", "interface{}") 22 | str3 := fmt.Sprintf("<%s>", str2) 23 | return str3 24 | } 25 | 26 | func BenchmarkPushBack(b *testing.B) { 27 | b.Run("Deque"+benchNameSuffix(), func(b *testing.B) { 28 | dq := NewDeque() 29 | for i := 0; i < b.N; i++ { 30 | dq.PushBack(Elem(i)) 31 | } 32 | }) 33 | b.Run("list.List", func(b *testing.B) { 34 | lst := list.New() 35 | for i := 0; i < b.N; i++ { 36 | lst.PushBack(i) 37 | } 38 | }) 39 | } 40 | 41 | func BenchmarkPushFront(b *testing.B) { 42 | b.Run("Deque"+benchNameSuffix(), func(b *testing.B) { 43 | dq := NewDeque() 44 | for i := 0; i < b.N; i++ { 45 | dq.PushFront(Elem(i)) 46 | } 47 | }) 48 | b.Run("list.List", func(b *testing.B) { 49 | lst := list.New() 50 | for i := 0; i < b.N; i++ { 51 | lst.PushFront(i) 52 | } 53 | }) 54 | } 55 | 56 | func BenchmarkRandom(b *testing.B) { 57 | const na = 100000 58 | a := make([]int, na) 59 | for i := 0; i < na; i++ { 60 | a[i] = rand.Int() 61 | } 62 | 63 | b.Run("Deque"+benchNameSuffix(), func(b *testing.B) { 64 | dq := NewDeque() 65 | for i := 0; i < b.N; i++ { 66 | switch a[i%na] % 4 { 67 | case 0: 68 | dq.PushBack(Elem(i)) 69 | case 1: 70 | dq.PushFront(Elem(i)) 71 | case 2: 72 | dq.PopBack() 73 | case 3: 74 | dq.PopFront() 75 | } 76 | } 77 | }) 78 | b.Run("list.List", func(b *testing.B) { 79 | lst := list.New() 80 | for i := 0; i < b.N; i++ { 81 | switch a[i%na] % 4 { 82 | case 0: 83 | lst.PushBack(i) 84 | case 1: 85 | lst.PushFront(i) 86 | case 2: 87 | if v := lst.Back(); v != nil { 88 | lst.Remove(v) 89 | } 90 | case 3: 91 | if v := lst.Front(); v != nil { 92 | lst.Remove(v) 93 | } 94 | } 95 | } 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /harden.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | [[ "$TRACE" ]] && set -x 4 | pushd `dirname "$0"` > /dev/null 5 | trap __EXIT EXIT 6 | 7 | colorful=false 8 | tput setaf 7 > /dev/null 2>&1 9 | if [[ $? -eq 0 ]]; then 10 | colorful=true 11 | fi 12 | 13 | function __EXIT() { 14 | popd > /dev/null 15 | } 16 | 17 | function printError() { 18 | $colorful && tput setaf 1 19 | >&2 echo "Error: $@" 20 | $colorful && tput setaf 7 21 | } 22 | 23 | function printImportantMessage() { 24 | $colorful && tput setaf 3 25 | >&2 echo "$@" 26 | $colorful && tput setaf 7 27 | } 28 | 29 | function printUsage() { 30 | $colorful && tput setaf 3 31 | >&2 echo "$@" 32 | $colorful && tput setaf 7 33 | } 34 | 35 | if [[ $# -lt 2 ]]; then 36 | printUsage "Usage: ./`basename $0` [elemType]" 37 | exit 1 38 | fi 39 | 40 | if [ "${1:0:1}" != "/" ]; then 41 | printError "outputDir must be an absolute directory" 42 | exit 1 43 | fi 44 | 45 | elemType="$3" 46 | if [[ "$3" == *'/'* ]] && [[ "$3" == *'.'* ]]; then 47 | elemPackage=$(echo "$3" | perl -pe 's/(.+)\.[^\/]+/$1/g') 48 | elemType=$(echo "$3" | perl -pe 's/.+\/([^\/]+)/$1/g') 49 | fi 50 | if [[ "$elemType" == "" ]]; then 51 | elemType='interface{}' 52 | fi 53 | 54 | mkdir -p "$1" 55 | [[ $? -ne 0 ]] && exit 1 56 | 57 | # 1 58 | cat < "$1"/deque.go 59 | package $2 60 | 61 | import ( 62 | "fmt" 63 | "sync" 64 | "sync/atomic" 65 | 66 | "github.com/edwingeng/deque" 67 | EOF 68 | [[ $? -ne 0 ]] && exit 1 69 | 70 | # 2 71 | if [[ "$elemPackage" != '' ]]; then 72 | echo "\"$elemPackage\"" >> "$1"/deque.go 73 | [[ $? -ne 0 ]] && exit 1 74 | fi 75 | 76 | # 3 77 | cat <> "$1"/deque.go 78 | ) 79 | 80 | type Elem = $elemType 81 | 82 | var ( 83 | _ = deque.NumChunksAllocated 84 | ) 85 | EOF 86 | [[ $? -ne 0 ]] && exit 1 87 | 88 | cat chunkPool.go | perl -0pe 's/^package.+^import \(.+?\)//gms' >> "$1"/deque.go 89 | [[ $? -ne 0 ]] && exit 1 90 | cat deque.go | perl -0pe 's/^package.+^import \(.+?\)//gms' >> "$1"/deque.go 91 | [[ $? -ne 0 ]] && exit 1 92 | 93 | perl -pi -e "s/^package deque$/package $2/g" "$1"/deque.go 94 | [[ $? -ne 0 ]] && exit 1 95 | perl -pi -e "s/type deque struct/type Deque struct/g" "$1"/deque.go 96 | [[ $? -ne 0 ]] && exit 1 97 | perl -pi -e "s/func \(dq \*deque\) /func (dq *Deque) /g" "$1"/deque.go 98 | [[ $? -ne 0 ]] && exit 1 99 | perl -pi -e "s/func NewDeque\(\) Deque {/func NewDeque() *Deque {/g" "$1"/deque.go 100 | [[ $? -ne 0 ]] && exit 1 101 | perl -pi -e "s/dq := &deque{/dq := &Deque{/g" "$1"/deque.go 102 | [[ $? -ne 0 ]] && exit 1 103 | 104 | gofmt -w "$1"/deque.go 105 | -------------------------------------------------------------------------------- /v2/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | Deque is a highly optimized double-ended queue, which is 3 | much efficient compared with `list.List` when adding or removing elements from 4 | the beginning or the end. 5 | 6 | # Benchmark 7 | ``` 8 | PushBack/Deque 20000000 13.6 ns/op 8 B/op 0 allocs/op 9 | PushBack/list.List 5000000 158.7 ns/op 56 B/op 1 allocs/op 10 | 11 | PushFront/Deque 30000000 9.8 ns/op 8 B/op 0 allocs/op 12 | PushFront/list.List 5000000 159.2 ns/op 56 B/op 1 allocs/op 13 | 14 | Random/Deque 50000000 13.9 ns/op 0 B/op 0 allocs/op 15 | Random/list.List 30000000 46.9 ns/op 28 B/op 1 allocs/op 16 | ``` 17 | 18 | # Getting Started 19 | ``` 20 | go get -u github.com/edwingeng/deque/v2 21 | ``` 22 | 23 | # Usage 24 | ``` go 25 | dq := deque.NewDeque[int]() 26 | dq.PushBack(100) 27 | dq.PushBack(200) 28 | dq.PushBack(300) 29 | for !dq.IsEmpty() { 30 | fmt.Println(dq.PopFront()) 31 | } 32 | 33 | dq.PushFront(100) 34 | dq.PushFront(200) 35 | dq.PushFront(300) 36 | for i, n := 0, dq.Len(); i < n; i++ { 37 | fmt.Println(dq.PopFront()) 38 | } 39 | 40 | // Output: 41 | // 100 42 | // 200 43 | // 300 44 | // 300 45 | // 200 46 | // 100 47 | ``` 48 | 49 | # Documentation 50 | ``` 51 | func NewDeque[T any](opts ...Option) *Deque[T] 52 | NewDeque creates a new Deque instance. 53 | 54 | func (dq *Deque[T]) PushBack(v T) 55 | PushBack adds a new value at the back of dq. 56 | 57 | func (dq *Deque[T]) PushFront(v T) 58 | PushFront adds a new value at the front of dq. 59 | 60 | func (dq *Deque[T]) PopBack() T 61 | PopBack removes a value from the back of dq and returns the removed value. 62 | It panics if dq is empty. 63 | 64 | func (dq *Deque[T]) PopFront() T 65 | PopFront removes a value from the front of dq and returns the removed value. 66 | It panics if dq is empty. 67 | 68 | func (dq *Deque[T]) TryPopBack() (_ T, ok bool) 69 | TryPopBack tries to remove a value from the back of dq and returns the 70 | removed value if any. The return value ok indicates whether it succeeded. 71 | 72 | func (dq *Deque[T]) TryPopFront() (_ T, ok bool) 73 | TryPopFront tries to remove a value from the front of dq and returns the 74 | removed value if any. The return value ok indicates whether it succeeded. 75 | 76 | func (dq *Deque[T]) Back() (_ T, ok bool) 77 | Back returns the last value of dq if any. The return value ok indicates 78 | whether it succeeded. 79 | 80 | func (dq *Deque[T]) Front() (_ T, ok bool) 81 | Front returns the first value of dq if any. The return value ok indicates 82 | whether it succeeded. 83 | 84 | func (dq *Deque[T]) Enqueue(v T) 85 | Enqueue is an alias of PushBack. 86 | 87 | func (dq *Deque[T]) Dequeue() T 88 | Dequeue is an alias of PopFront. 89 | 90 | func (dq *Deque[T]) TryDequeue() (_ T, ok bool) 91 | TryDequeue is an alias of TryPopFront. 92 | 93 | func (dq *Deque[T]) DequeueMany(max int) []T 94 | DequeueMany removes a number of values from the front of dq and returns the 95 | removed values or nil if dq is empty. 96 | 97 | If max <= 0, DequeueMany removes and returns all the values in dq. 98 | 99 | func (dq *Deque[T]) DequeueManyWithBuffer(max int, buf []T) []T 100 | DequeueManyWithBuffer is similar to DequeueMany except that it uses buf to 101 | store the removed values as long as it has enough space. 102 | 103 | func (dq *Deque[T]) Insert(idx int, v T) 104 | Insert inserts a new value v before the value at idx. 105 | 106 | Insert may cause the split of a chunk inside dq. Because the size of a chunk 107 | is fixed, the amount of time taken by Insert has a reasonable limit. 108 | 109 | func (dq *Deque[T]) Remove(idx int) 110 | Remove removes the value at idx. It panics if idx is out of range. 111 | 112 | func (dq *Deque[T]) Replace(idx int, v T) 113 | Replace replaces the value at idx with v. It panics if idx is out of range. 114 | 115 | func (dq *Deque[T]) Swap(idx1, idx2 int) 116 | Swap exchanges the two values at idx1 and idx2. It panics if idx1 or idx2 is 117 | out of range. 118 | 119 | func (dq *Deque[T]) Clear() 120 | Clear removes all the values from dq. 121 | 122 | func (dq *Deque[T]) IsEmpty() bool 123 | IsEmpty returns whether dq is empty. 124 | 125 | func (dq *Deque[T]) Len() int 126 | Len returns the number of values in dq. 127 | 128 | func (dq *Deque[T]) Range(f func(i int, v T) bool) 129 | Range iterates all the values in dq. Do NOT add values to dq or remove 130 | values from dq during Range. 131 | 132 | func (dq *Deque[T]) Peek(idx int) T 133 | Peek returns the value at idx. It panics if idx is out of range. 134 | 135 | func (dq *Deque[T]) Dump() []T 136 | Dump returns all the values in dq. 137 | ``` -------------------------------------------------------------------------------- /deque.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import ( 4 | "fmt" 5 | "sync/atomic" 6 | ) 7 | 8 | const chunkSize = 254 9 | 10 | var elemDefValue Elem 11 | 12 | type chunk struct { 13 | s int 14 | e int 15 | data [chunkSize]Elem 16 | } 17 | 18 | func (c *chunk) back() Elem { 19 | if c.e > c.s { 20 | return c.data[c.e-1] 21 | } 22 | return elemDefValue 23 | } 24 | 25 | func (c *chunk) front() Elem { 26 | if c.e > c.s { 27 | return c.data[c.s] 28 | } 29 | return elemDefValue 30 | } 31 | 32 | type deque struct { 33 | chunks []*chunk 34 | 35 | chunkPitch []*chunk 36 | sFree int 37 | eFree int 38 | } 39 | 40 | var ( 41 | sharedChunkPool = newChunkPool(func() interface{} { 42 | return &chunk{} 43 | }) 44 | ) 45 | 46 | // NewDeque creates a new Deque instance. 47 | func NewDeque() Deque { 48 | dq := &deque{ 49 | chunkPitch: make([]*chunk, 64), 50 | sFree: 32, 51 | eFree: 32, 52 | } 53 | return dq 54 | } 55 | 56 | func (dq *deque) balance() { 57 | var pitchLen = len(dq.chunkPitch) 58 | n := len(dq.chunks) 59 | dq.sFree = pitchLen/2 - n/2 60 | dq.eFree = pitchLen - dq.sFree - n 61 | newChunks := dq.chunkPitch[dq.sFree : dq.sFree+n] 62 | copy(newChunks, dq.chunks) 63 | dq.chunks = newChunks 64 | for i := 0; i < dq.sFree; i++ { 65 | dq.chunkPitch[i] = nil 66 | } 67 | for i := pitchLen - dq.eFree; i < pitchLen; i++ { 68 | dq.chunkPitch[i] = nil 69 | } 70 | } 71 | 72 | func (dq *deque) realloc() { 73 | if len(dq.chunks) < len(dq.chunkPitch)/2 { 74 | dq.balance() 75 | return 76 | } 77 | 78 | newLen := len(dq.chunkPitch) * 2 79 | newPitch := make([]*chunk, newLen) 80 | n := len(dq.chunks) 81 | dq.sFree = newLen/2 - n/2 82 | dq.eFree = newLen - dq.sFree - n 83 | newChunks := newPitch[dq.sFree : dq.sFree+n] 84 | for i := 0; i < n; i++ { 85 | newChunks[i] = dq.chunks[i] 86 | } 87 | dq.chunkPitch = newPitch 88 | dq.chunks = newChunks 89 | } 90 | 91 | func (dq *deque) expandEnd() { 92 | if f := dq.eFree; f == 0 { 93 | dq.realloc() 94 | } 95 | c := sharedChunkPool.Get().(*chunk) 96 | c.s, c.e = 0, 0 97 | dq.eFree-- 98 | newEnd := len(dq.chunkPitch) - dq.eFree 99 | dq.chunkPitch[newEnd-1] = c 100 | dq.chunks = dq.chunkPitch[dq.sFree:newEnd] 101 | } 102 | 103 | func (dq *deque) expandStart() { 104 | if f := dq.sFree; f == 0 { 105 | dq.realloc() 106 | } 107 | c := sharedChunkPool.Get().(*chunk) 108 | c.s, c.e = chunkSize, chunkSize 109 | dq.sFree-- 110 | dq.chunkPitch[dq.sFree] = c 111 | newEnd := len(dq.chunkPitch) - dq.eFree 112 | dq.chunks = dq.chunkPitch[dq.sFree:newEnd] 113 | } 114 | 115 | func (dq *deque) shrinkEnd() { 116 | n := len(dq.chunkPitch) 117 | if dq.sFree+dq.eFree == n { 118 | return 119 | } 120 | newEnd := n - dq.eFree - 1 121 | c := dq.chunkPitch[newEnd] 122 | dq.chunkPitch[newEnd] = nil 123 | sharedChunkPool.Put(c) 124 | dq.eFree++ 125 | dq.chunks = dq.chunkPitch[dq.sFree:newEnd] 126 | if dq.sFree+dq.eFree < n { 127 | return 128 | } 129 | dq.sFree = n / 2 130 | dq.eFree = n - dq.sFree 131 | } 132 | 133 | func (dq *deque) shrinkStart() { 134 | n := len(dq.chunkPitch) 135 | if dq.sFree+dq.eFree == n { 136 | return 137 | } 138 | c := dq.chunkPitch[dq.sFree] 139 | dq.chunkPitch[dq.sFree] = nil 140 | sharedChunkPool.Put(c) 141 | dq.sFree++ 142 | curEnd := len(dq.chunkPitch) - dq.eFree 143 | dq.chunks = dq.chunkPitch[dq.sFree:curEnd] 144 | if dq.sFree+dq.eFree < n { 145 | return 146 | } 147 | dq.sFree = n / 2 148 | dq.eFree = n - dq.sFree 149 | } 150 | 151 | func (dq *deque) PushBack(v Elem) { 152 | var c *chunk 153 | n := len(dq.chunks) 154 | if n == 0 { 155 | dq.expandEnd() 156 | c = dq.chunks[n] 157 | } else { 158 | c = dq.chunks[n-1] 159 | if c.e == chunkSize { 160 | dq.expandEnd() 161 | c = dq.chunks[n] 162 | } 163 | } 164 | c.data[c.e] = v 165 | c.e++ 166 | } 167 | 168 | func (dq *deque) PushFront(v Elem) { 169 | var c *chunk 170 | n := len(dq.chunks) 171 | if n == 0 { 172 | dq.expandStart() 173 | c = dq.chunks[0] 174 | } else { 175 | c = dq.chunks[0] 176 | if c.s == 0 { 177 | dq.expandStart() 178 | c = dq.chunks[0] 179 | } 180 | } 181 | c.s-- 182 | c.data[c.s] = v 183 | } 184 | 185 | func (dq *deque) PopBack() Elem { 186 | n := len(dq.chunks) 187 | if n == 0 { 188 | return elemDefValue 189 | } 190 | c := dq.chunks[n-1] 191 | if c.e == c.s { 192 | return elemDefValue 193 | } 194 | c.e-- 195 | r := c.data[c.e] 196 | c.data[c.e] = elemDefValue 197 | if c.e == 0 { 198 | dq.shrinkEnd() 199 | } 200 | return r 201 | } 202 | 203 | func (dq *deque) PopFront() Elem { 204 | n := len(dq.chunks) 205 | if n == 0 { 206 | return elemDefValue 207 | } 208 | c := dq.chunks[0] 209 | if c.e == c.s { 210 | return elemDefValue 211 | } 212 | r := c.data[c.s] 213 | c.data[c.s] = elemDefValue 214 | c.s++ 215 | if c.s == chunkSize { 216 | dq.shrinkStart() 217 | } 218 | return r 219 | } 220 | 221 | func (dq *deque) DequeueMany(max int) []Elem { 222 | return dq.dequeueManyImpl(max, nil) 223 | } 224 | 225 | func (dq *deque) dequeueManyImpl(max int, buf []Elem) []Elem { 226 | n := dq.Len() 227 | if n == 0 { 228 | return nil 229 | } 230 | if max > 0 && n > max { 231 | n = max 232 | } 233 | if n <= cap(buf) { 234 | buf = buf[:n] 235 | } else { 236 | buf = make([]Elem, n) 237 | } 238 | for i := 0; i < n; i++ { 239 | c := dq.chunks[0] 240 | buf[i] = c.data[c.s] 241 | c.data[c.s] = elemDefValue 242 | c.s++ 243 | if c.s == chunkSize { 244 | dq.shrinkStart() 245 | } 246 | } 247 | return buf 248 | } 249 | 250 | func (dq *deque) DequeueManyWithBuffer(max int, buf []Elem) []Elem { 251 | return dq.dequeueManyImpl(max, buf) 252 | } 253 | 254 | func (dq *deque) Back() Elem { 255 | n := len(dq.chunks) 256 | if n == 0 { 257 | return elemDefValue 258 | } 259 | return dq.chunks[n-1].back() 260 | } 261 | 262 | func (dq *deque) Front() Elem { 263 | n := len(dq.chunks) 264 | if n == 0 { 265 | return elemDefValue 266 | } 267 | return dq.chunks[0].front() 268 | } 269 | 270 | func (dq *deque) Empty() bool { 271 | n := len(dq.chunks) 272 | return n == 0 || n == 1 && dq.chunks[0].e == dq.chunks[0].s 273 | } 274 | 275 | func (dq *deque) Len() int { 276 | n := len(dq.chunks) 277 | switch n { 278 | case 0: 279 | return 0 280 | case 1: 281 | return dq.chunks[0].e - dq.chunks[0].s 282 | default: 283 | return chunkSize - dq.chunks[0].s + dq.chunks[n-1].e + (n-2)*chunkSize 284 | } 285 | } 286 | 287 | func (dq *deque) Enqueue(v Elem) { 288 | dq.PushBack(v) 289 | } 290 | 291 | func (dq *deque) Dequeue() Elem { 292 | return dq.PopFront() 293 | } 294 | 295 | func (dq *deque) Dump() []Elem { 296 | n := dq.Len() 297 | if n == 0 { 298 | return nil 299 | } 300 | 301 | vals := make([]Elem, n) 302 | var idx int 303 | for _, c := range dq.chunks { 304 | for i := c.s; i < c.e; i++ { 305 | vals[idx] = c.data[i] 306 | idx++ 307 | } 308 | } 309 | return vals 310 | } 311 | 312 | func (dq *deque) Range(f func(i int, v Elem) bool) { 313 | n := dq.Len() 314 | if n == 0 { 315 | return 316 | } 317 | 318 | var i int 319 | for _, c := range dq.chunks { 320 | for j := c.s; j < c.e; j++ { 321 | v := c.data[j] 322 | if !f(i, v) { 323 | return 324 | } 325 | i++ 326 | } 327 | } 328 | } 329 | 330 | func (dq *deque) Peek(idx int) Elem { 331 | i := idx 332 | for _, c := range dq.chunks { 333 | if i < 0 { 334 | break 335 | } 336 | n := c.e - c.s 337 | if i < n { 338 | return c.data[c.s+i] 339 | } 340 | i -= n 341 | } 342 | panic(fmt.Errorf("out of range: %d", idx)) 343 | } 344 | 345 | func (dq *deque) Replace(idx int, v Elem) { 346 | i := idx 347 | for _, c := range dq.chunks { 348 | if i < 0 { 349 | break 350 | } 351 | n := c.e - c.s 352 | if i < n { 353 | c.data[c.s+i] = v 354 | return 355 | } 356 | i -= n 357 | } 358 | panic(fmt.Errorf("out of range: %d", idx)) 359 | } 360 | 361 | // NumChunksAllocated returns the number of chunks allocated by now. 362 | func NumChunksAllocated() int64 { 363 | return atomic.LoadInt64(&sharedChunkPool.numChunksAllocated) 364 | } 365 | -------------------------------------------------------------------------------- /deque_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "reflect" 7 | "testing" 8 | "time" 9 | "unsafe" 10 | ) 11 | 12 | func invariant(dq Deque, t *testing.T) { 13 | t.Helper() 14 | dq1 := dq.(*deque) 15 | if dq1.sFree < 0 || dq1.sFree > len(dq1.chunkPitch) { 16 | t.Fatal("dq1.sFree < 0 || dq1.sFree > len(dq1.chunkPitch)") 17 | } 18 | if dq1.eFree < 0 || dq1.eFree > len(dq1.chunkPitch) { 19 | t.Fatal("dq1.eFree < 0 || dq1.eFree > len(dq1.chunkPitch)") 20 | } 21 | if dq1.sFree+dq1.eFree+len(dq1.chunks) != len(dq1.chunkPitch) { 22 | t.Fatal("dq1.sFree+dq1.eFree+len(dq1.chunks) != len(dq1.chunkPitch)") 23 | } 24 | if n := dq1.Len(); n <= chunkSize { 25 | if len(dq1.chunks) > 2 { 26 | t.Fatal("len(dq1.chunks) > 2") 27 | } 28 | } else { 29 | if len(dq1.chunks) < n/chunkSize { 30 | t.Fatal("len(dq1.chunks) < n/chunkSize") 31 | } 32 | if len(dq1.chunks) > n/chunkSize+2 { 33 | t.Fatal("len(dq1.chunks) > n/chunkSize+2") 34 | } 35 | } 36 | for _, c := range dq1.chunks { 37 | if c.s < 0 || c.s > chunkSize { 38 | t.Fatal("c.s < 0 || c.s > chunkSize") 39 | } 40 | if c.e < 0 || c.e > chunkSize { 41 | t.Fatal("c.e < 0 || c.e > chunkSize") 42 | } 43 | } 44 | } 45 | 46 | func TestChunk(t *testing.T) { 47 | dq1 := NewDeque() 48 | for i := 0; i < 5; i++ { 49 | dq1.PushBack(i) 50 | invariant(dq1, t) 51 | if dq1.Back().(int) != i { 52 | t.Fatal("dq1.Back().(int) != i") 53 | } 54 | if dq1.Front().(int) != 0 { 55 | t.Fatal("dq1.Front().(int) != 0") 56 | } 57 | } 58 | 59 | for dq1.Len() > 0 { 60 | dq1.PopFront() 61 | } 62 | if dq1.(*deque).chunks[0].back() != nil { 63 | t.Fatal("dq1.(*deque).chunks[0].back() != nil") 64 | } 65 | if dq1.(*deque).chunks[0].front() != nil { 66 | t.Fatal("dq1.(*deque).chunks[0].front() != nil") 67 | } 68 | 69 | dq2 := NewDeque() 70 | for i := 0; i < 5; i++ { 71 | dq2.PushFront(i) 72 | invariant(dq2, t) 73 | if dq2.Back().(int) != 0 { 74 | t.Fatal("dq2.Back().(int) != 0") 75 | } 76 | if dq2.Front().(int) != i { 77 | t.Fatal("dq2.Front().(int) != i") 78 | } 79 | } 80 | } 81 | 82 | func TestDeque_realloc(t *testing.T) { 83 | for pushes := 0; pushes < chunkSize*3; pushes++ { 84 | dq := NewDeque().(*deque) 85 | for i := 0; i < pushes; i++ { 86 | dq.PushBack(i) 87 | } 88 | numChunks := pushes / chunkSize 89 | if pushes%chunkSize != 0 { 90 | numChunks++ 91 | } 92 | for i := 0; i < 3; i++ { 93 | dq.realloc() 94 | invariant(dq, t) 95 | const n = 64 96 | if len(dq.chunkPitch) != n { 97 | t.Fatal("len(dq.chunkPitch) != n") 98 | } 99 | if len(dq.chunks) != numChunks { 100 | t.Fatal("len(dq.chunks) != numChunks") 101 | } 102 | if dq.sFree != n/2-numChunks/2 { 103 | t.Fatal("dq.sFree != n/2-numChunks/2") 104 | } 105 | if dq.eFree != n-dq.sFree-numChunks { 106 | t.Fatal("dq.eFree != n-dq.sFree-numChunks") 107 | } 108 | } 109 | } 110 | 111 | dq := NewDeque().(*deque) 112 | for i := 0; i < 32; i++ { 113 | dq.chunks = append(dq.chunks, &chunk{ 114 | e: chunkSize, 115 | }) 116 | } 117 | dq.eFree = 0 118 | invariant(dq, t) 119 | 120 | dq.PushBack(1) 121 | invariant(dq, t) 122 | const n = 128 123 | if len(dq.chunkPitch) != n { 124 | t.Fatal("len(dq.chunkPitch) != n") 125 | } 126 | } 127 | 128 | func TestDeque_expandEnd(t *testing.T) { 129 | dq := NewDeque().(*deque) 130 | dq.sFree += 10 131 | dq.eFree -= 10 132 | sf := dq.sFree 133 | ef := dq.eFree 134 | dq.expandEnd() 135 | invariant(dq, t) 136 | if len(dq.chunks) != 1 { 137 | t.Fatal("len(dq.chunks) != 1") 138 | } 139 | if dq.sFree != sf { 140 | t.Fatal("dq.sFree != sf") 141 | } 142 | if dq.eFree != ef-1 { 143 | t.Fatal("dq.eFree != ef-1") 144 | } 145 | if dq.chunks[0] == nil { 146 | t.Fatal("dq.chunks[0] == nil") 147 | } 148 | } 149 | 150 | func TestDeque_expandStart(t *testing.T) { 151 | dq := NewDeque().(*deque) 152 | dq.sFree += 10 153 | dq.eFree -= 10 154 | sf := dq.sFree 155 | ef := dq.eFree 156 | dq.expandStart() 157 | invariant(dq, t) 158 | if len(dq.chunks) != 1 { 159 | t.Fatal("len(dq.chunks) != 1") 160 | } 161 | if dq.sFree != sf-1 { 162 | t.Fatal("dq.sFree != sf-1") 163 | } 164 | if dq.eFree != ef { 165 | t.Fatal("dq.eFree != ef") 166 | } 167 | if dq.chunks[0] == nil { 168 | t.Fatal("dq.chunks[0] == nil") 169 | } 170 | } 171 | 172 | func TestDeque_shrinkEnd(t *testing.T) { 173 | dq := NewDeque().(*deque) 174 | for i := 0; i < len(dq.chunkPitch); i++ { 175 | dq.chunkPitch[i] = &chunk{} 176 | } 177 | dq.eFree -= 10 178 | sf := dq.sFree 179 | ef := dq.eFree 180 | dq.shrinkEnd() 181 | invariant(dq, t) 182 | if len(dq.chunks) != 9 { 183 | t.Fatal("len(dq.chunks) != 9") 184 | } 185 | if dq.sFree != sf { 186 | t.Fatal("dq.sFree != sf") 187 | } 188 | if dq.eFree != ef+1 { 189 | t.Fatal("dq.eFree != ef+1") 190 | } 191 | b := 0 192 | idx := 0 193 | for i := 0; i < len(dq.chunkPitch); i++ { 194 | if dq.chunkPitch[i] != nil { 195 | b++ 196 | } else { 197 | idx = i 198 | } 199 | } 200 | if b != len(dq.chunkPitch)-1 { 201 | t.Fatal("b != len(dq.chunkPitch)-1") 202 | } 203 | if idx != len(dq.chunkPitch)-ef-1 { 204 | t.Fatal("idx != len(dq.chunkPitch)-ef-1") 205 | } 206 | 207 | dq.sFree = 60 208 | dq.eFree = len(dq.chunkPitch) - dq.sFree - 1 209 | dq.shrinkEnd() 210 | invariant(dq, t) 211 | if dq.sFree != 32 { 212 | t.Fatal("dq.sFree != 32") 213 | } 214 | if dq.eFree != 32 { 215 | t.Fatal("dq.eFree != 32") 216 | } 217 | } 218 | 219 | func TestDeque_shrinkStart(t *testing.T) { 220 | dq := NewDeque().(*deque) 221 | for i := 0; i < len(dq.chunkPitch); i++ { 222 | dq.chunkPitch[i] = &chunk{} 223 | } 224 | dq.eFree -= 10 225 | sf := dq.sFree 226 | ef := dq.eFree 227 | dq.shrinkStart() 228 | invariant(dq, t) 229 | if len(dq.chunks) != 9 { 230 | t.Fatal("len(dq.chunks) != 9") 231 | } 232 | if dq.sFree != sf+1 { 233 | t.Fatal("dq.sFree != sf+1") 234 | } 235 | if dq.eFree != ef { 236 | t.Fatal("dq.eFree != ef") 237 | } 238 | b := 0 239 | idx := 0 240 | for i := 0; i < len(dq.chunkPitch); i++ { 241 | if dq.chunkPitch[i] != nil { 242 | b++ 243 | } else { 244 | idx = i 245 | } 246 | } 247 | if b != len(dq.chunkPitch)-1 { 248 | t.Fatal("b != len(dq.chunkPitch)-1") 249 | } 250 | if idx != sf { 251 | t.Fatal("idx != sf") 252 | } 253 | 254 | dq.sFree = 60 255 | dq.eFree = len(dq.chunkPitch) - dq.sFree - 1 256 | dq.shrinkEnd() 257 | invariant(dq, t) 258 | if dq.sFree != 32 { 259 | t.Fatal("dq.sFree != 32") 260 | } 261 | if dq.eFree != 32 { 262 | t.Fatal("dq.eFree != 32") 263 | } 264 | } 265 | 266 | func TestDeque_PushBack(t *testing.T) { 267 | dq := NewDeque() 268 | const total = 10000 269 | for i := 0; i < total; i++ { 270 | if dq.Len() != i { 271 | t.Fatal("dq.Len() != i") 272 | } 273 | dq.PushBack(i) 274 | } 275 | for i := 0; i < total; i++ { 276 | if dq.PopFront().(int) != i { 277 | invariant(dq, t) 278 | t.Fatal("dq.PopFront().(int) != i") 279 | } 280 | } 281 | 282 | for i := 0; i < total; i++ { 283 | dq.PushBack(i) 284 | } 285 | for i := 0; i < total; i++ { 286 | if dq.PopBack().(int) != total-i-1 { 287 | invariant(dq, t) 288 | t.Fatal("dq.PopBack().(int) != total-i-1") 289 | } 290 | } 291 | } 292 | 293 | func TestDeque_PushFront(t *testing.T) { 294 | dq := NewDeque() 295 | const total = 10000 296 | for i := 0; i < total; i++ { 297 | if dq.Len() != i { 298 | t.Fatal("dq.Len() != i") 299 | } 300 | dq.PushFront(i) 301 | } 302 | for i := 0; i < total; i++ { 303 | if dq.PopFront().(int) != total-i-1 { 304 | invariant(dq, t) 305 | t.Fatal("dq.PopFront().(int) != total-i-1") 306 | } 307 | } 308 | 309 | for i := 0; i < total; i++ { 310 | dq.PushFront(i) 311 | } 312 | for i := 0; i < total; i++ { 313 | if dq.PopBack().(int) != i { 314 | invariant(dq, t) 315 | t.Fatal("dq.PopBack().(int) != i") 316 | } 317 | } 318 | } 319 | 320 | func TestDeque_DequeueMany(t *testing.T) { 321 | dq1 := NewDeque() 322 | for i := -1; i <= 1; i++ { 323 | if dq1.DequeueMany(i) != nil { 324 | t.Fatalf("dq1.DequeueMany(%d) should return nil while dq1 is empty", i) 325 | } 326 | invariant(dq1, t) 327 | } 328 | 329 | dq2 := NewDeque() 330 | for i := 0; i < 1000; i += 5 { 331 | for j := 0; j < i; j++ { 332 | dq2.PushBack(j) 333 | } 334 | invariant(dq2, t) 335 | if len(dq2.DequeueMany(0)) != i { 336 | t.Fatalf("dq2.DequeueMany(0) should return %d values", i) 337 | } 338 | invariant(dq2, t) 339 | } 340 | 341 | for i := 0; i < 2000; i += 5 { 342 | for j := 5; j < 600; j += 25 { 343 | dq3 := NewDeque() 344 | for k := 0; k < i; k++ { 345 | dq3.PushBack(k) 346 | } 347 | invariant(dq3, t) 348 | left := i 349 | for left > 0 { 350 | c := j 351 | if left < j { 352 | c = left 353 | } 354 | vals := dq3.DequeueMany(j) 355 | if len(vals) != c { 356 | t.Fatalf("len(vals) != c. len: %d, c: %d, i: %d, j: %d", len(vals), c, i, j) 357 | } 358 | left -= c 359 | invariant(dq3, t) 360 | } 361 | if dq3.DequeueMany(0) != nil { 362 | t.Fatalf("dq3.DequeueMany(0) != nil") 363 | } 364 | } 365 | } 366 | } 367 | 368 | func compareBufs(bufA, bufB []interface{}, suffix string, t *testing.T) { 369 | t.Helper() 370 | if bufB == nil { 371 | return 372 | } 373 | 374 | ptrA := (*reflect.SliceHeader)(unsafe.Pointer(&bufA)).Data 375 | ptrB := (*reflect.SliceHeader)(unsafe.Pointer(&bufB)).Data 376 | if len(bufB) <= cap(bufA) { 377 | if ptrB != ptrA { 378 | t.Fatal("ptrB != ptrA. " + suffix) 379 | } 380 | } else { 381 | if ptrB == ptrA { 382 | t.Fatal("ptrB != ptrA. " + suffix) 383 | } 384 | } 385 | } 386 | 387 | func TestDeque_DequeueManyWithBuffer(t *testing.T) { 388 | dq1 := NewDeque() 389 | for i := -1; i <= 1; i++ { 390 | if dq1.DequeueManyWithBuffer(i, nil) != nil { 391 | t.Fatalf("dq1.DequeueManyWithBuffer(%d, nil) should return nil while dq1 is empty", i) 392 | } 393 | invariant(dq1, t) 394 | } 395 | 396 | dq2 := NewDeque() 397 | for i := 0; i < 1000; i += 5 { 398 | for j := 0; j < i; j++ { 399 | dq2.PushBack(j) 400 | } 401 | invariant(dq2, t) 402 | bufA := make([]interface{}, 64, 64) 403 | bufB := dq2.DequeueManyWithBuffer(0, bufA) 404 | if len(bufB) != i { 405 | t.Fatalf("dq2.DequeueManyWithBuffer(0, bufA) should return %d values", i) 406 | } 407 | invariant(dq2, t) 408 | compareBufs(bufA, bufB, fmt.Sprintf("i: %d", i), t) 409 | } 410 | 411 | for i := 0; i < 2000; i += 5 { 412 | for j := 5; j < 600; j += 25 { 413 | dq3 := NewDeque() 414 | for k := 0; k < i; k++ { 415 | dq3.PushBack(k) 416 | } 417 | invariant(dq3, t) 418 | left := i 419 | for left > 0 { 420 | c := j 421 | if left < j { 422 | c = left 423 | } 424 | bufA := make([]interface{}, 64, 64) 425 | bufB := dq3.DequeueManyWithBuffer(j, bufA) 426 | if len(bufB) != c { 427 | t.Fatalf("len(bufB) != c. len: %d, c: %d, i: %d, j: %d", len(bufB), c, i, j) 428 | } 429 | left -= c 430 | invariant(dq3, t) 431 | str := fmt.Sprintf("len: %d, c: %d, i: %d, j: %d", len(bufB), c, i, j) 432 | compareBufs(bufA, bufB, str, t) 433 | } 434 | if dq3.DequeueMany(0) != nil { 435 | t.Fatalf("dq3.DequeueMany(0) != nil") 436 | } 437 | } 438 | } 439 | } 440 | 441 | func TestDeque_Back(t *testing.T) { 442 | dq := NewDeque() 443 | if dq.Back() != nil { 444 | t.Fatal("dq.Back() != nil") 445 | } 446 | for i := 0; i < 1000; i++ { 447 | dq.PushBack(i) 448 | if dq.Back().(int) != i { 449 | t.Fatal("dq.Back().(int) != i") 450 | } 451 | } 452 | } 453 | 454 | func TestDeque_Front(t *testing.T) { 455 | dq := NewDeque() 456 | if dq.Front() != nil { 457 | t.Fatal("dq.Front() != nil") 458 | } 459 | for i := 0; i < 1000; i++ { 460 | dq.PushFront(i) 461 | if dq.Front().(int) != i { 462 | t.Fatal("dq.Front().(int) != i") 463 | } 464 | } 465 | } 466 | 467 | func TestDeque_Random(t *testing.T) { 468 | rand.Seed(time.Now().UnixNano()) 469 | dq := NewDeque() 470 | var n int 471 | for i := 0; i < 100000; i++ { 472 | invariant(dq, t) 473 | switch rand.Int() % 4 { 474 | case 0: 475 | dq.PushBack(i) 476 | n++ 477 | case 1: 478 | dq.PushFront(i) 479 | n++ 480 | case 2: 481 | if dq.PopBack() != nil { 482 | n-- 483 | } 484 | case 3: 485 | if dq.PopFront() != nil { 486 | n-- 487 | } 488 | } 489 | if dq.Len() != n { 490 | t.Fatal("dq.Len() != n") 491 | } 492 | if n == 0 != dq.Empty() { 493 | t.Fatal("n == 0 != dq.Empty()") 494 | } 495 | } 496 | } 497 | 498 | func TestDeque_Dump(t *testing.T) { 499 | rand.Seed(time.Now().UnixNano()) 500 | var a []Elem 501 | dq := NewDeque().(*deque) 502 | for i := 0; i < 10000; i++ { 503 | invariant(dq, t) 504 | switch rand.Int() % 5 { 505 | case 0, 1: 506 | dq.PushBack(i) 507 | a = append(a, i) 508 | case 2: 509 | dq.PushFront(i) 510 | a = append([]Elem{i}, a...) 511 | case 3: 512 | if dq.PopBack() != nil { 513 | a = a[:len(a)-1] 514 | } 515 | case 4: 516 | if dq.PopFront() != nil { 517 | a = a[1:] 518 | } 519 | } 520 | 521 | b := dq.Dump() 522 | if len(b) != len(a) { 523 | t.Fatal("len(b) != len(a)") 524 | } 525 | for i, v := range a { 526 | if b[i] != v { 527 | t.Fatalf("b[i] != v. i: %d", i) 528 | } 529 | } 530 | } 531 | } 532 | 533 | func rec() { 534 | _ = recover() 535 | } 536 | 537 | func TestDeque_Replace(t *testing.T) { 538 | for _, n := range []int{1, 100, chunkSize, chunkSize + 1, chunkSize * 2, 1000} { 539 | a := make([]int, n) 540 | dq := NewDeque() 541 | for i := 0; i < n; i++ { 542 | v := rand.Int() 543 | a[i] = v 544 | dq.PushBack(v) 545 | } 546 | 547 | func() { 548 | defer rec() 549 | dq.Peek(-1) 550 | t.Fatal("impossible") 551 | }() 552 | func() { 553 | defer rec() 554 | dq.Peek(n + 10) 555 | t.Fatal("impossible") 556 | }() 557 | func() { 558 | defer rec() 559 | dq.Replace(-1, 0x7FFFFFFFFFFFFFFF) 560 | t.Fatal("impossible") 561 | }() 562 | func() { 563 | defer rec() 564 | dq.Replace(n+10, 0x7FFFFFFFFFFFFFFF) 565 | t.Fatal("impossible") 566 | }() 567 | 568 | for i := 0; i < 2000; i++ { 569 | idx := rand.Intn(n) 570 | if dq.Peek(idx).(int) != a[idx] { 571 | t.Fatal("dq.Peek(idx).(int) != a[idx]") 572 | } 573 | 574 | val := rand.Int() 575 | a[idx] = val 576 | dq.Replace(idx, val) 577 | 578 | dq.Range(func(i int, v Elem) bool { 579 | if a[i] != v.(int) { 580 | t.Fatal("a[i] != v.(int)") 581 | } 582 | return true 583 | }) 584 | } 585 | } 586 | } 587 | 588 | func TestDeque_Range(t *testing.T) { 589 | dq := NewDeque() 590 | dq.Range(func(i int, v Elem) bool { 591 | panic("impossible") 592 | }) 593 | 594 | for i := 0; i < 10; i++ { 595 | dq.PushFront(1) 596 | } 597 | for i := 0; i < 10; i++ { 598 | dq.PushBack(i) 599 | } 600 | 601 | if len(dq.(*deque).chunks) != 2 { 602 | t.Fatal(`len(dq.(*deque).chunks) != 2`) 603 | } 604 | 605 | var count int 606 | dq.Range(func(i int, v Elem) bool { 607 | if i < 15 { 608 | count++ 609 | return true 610 | } else { 611 | return false 612 | } 613 | }) 614 | if count != 15 { 615 | t.Fatal(`count != 15`, count) 616 | } 617 | } 618 | -------------------------------------------------------------------------------- /v2/deque.go: -------------------------------------------------------------------------------- 1 | // Package deque implements a highly optimized double-ended queue, which is 2 | // much efficient compared with list.List when adding or removing elements from 3 | // the beginning or the end. 4 | package deque 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "sync" 10 | "unsafe" 11 | ) 12 | 13 | const ( 14 | defaultPitchSize = 64 15 | ) 16 | 17 | var ( 18 | errEmpty = errors.New("deque is empty") 19 | ) 20 | 21 | type chunk[T any] struct { 22 | s int 23 | e int // not included 24 | data []T 25 | } 26 | 27 | func (c *chunk[T]) back() (T, bool) { 28 | if c.e > c.s { 29 | return c.data[c.e-1], true 30 | } 31 | return *new(T), false 32 | } 33 | 34 | func (c *chunk[T]) front() (T, bool) { 35 | if c.e > c.s { 36 | return c.data[c.s], true 37 | } 38 | return *new(T), false 39 | } 40 | 41 | // Deque is a highly optimized double-ended queue. 42 | type Deque[T any] struct { 43 | chunks []*chunk[T] 44 | count int 45 | 46 | chunkPitch []*chunk[T] 47 | sFree int 48 | eFree int 49 | chunkSize int 50 | chunkPool sync.Pool 51 | } 52 | 53 | func minInt(a, b int) int { 54 | if a <= b { 55 | return a 56 | } else { 57 | return b 58 | } 59 | } 60 | 61 | func maxInt(a, b int) int { 62 | if a >= b { 63 | return a 64 | } else { 65 | return b 66 | } 67 | } 68 | 69 | type optionHolder struct { 70 | chunkSize int 71 | } 72 | 73 | // NewDeque creates a new Deque instance. 74 | func NewDeque[T any](opts ...Option) *Deque[T] { 75 | dq := &Deque[T]{ 76 | chunkPitch: make([]*chunk[T], defaultPitchSize), 77 | sFree: 32, 78 | eFree: 32, 79 | } 80 | 81 | var holder optionHolder 82 | holder.chunkSize = maxInt(1024/int(unsafe.Sizeof(*new(T))), 16) 83 | for _, opt := range opts { 84 | opt(&holder) 85 | } 86 | 87 | dq.chunkSize = holder.chunkSize 88 | dq.chunkPool = sync.Pool{ 89 | New: func() any { 90 | return &chunk[T]{ 91 | data: make([]T, dq.chunkSize, dq.chunkSize), 92 | } 93 | }, 94 | } 95 | 96 | return dq 97 | } 98 | 99 | func (dq *Deque[T]) balance() { 100 | var pitchLen = len(dq.chunkPitch) 101 | n := len(dq.chunks) 102 | dq.sFree = pitchLen/2 - n/2 103 | dq.eFree = pitchLen - dq.sFree - n 104 | newChunks := dq.chunkPitch[dq.sFree : dq.sFree+n] 105 | copy(newChunks, dq.chunks) 106 | dq.chunks = newChunks 107 | for i := 0; i < dq.sFree; i++ { 108 | dq.chunkPitch[i] = nil 109 | } 110 | for i := pitchLen - dq.eFree; i < pitchLen; i++ { 111 | dq.chunkPitch[i] = nil 112 | } 113 | } 114 | 115 | func (dq *Deque[T]) realloc() { 116 | if len(dq.chunks) < len(dq.chunkPitch)/2 { 117 | dq.balance() 118 | return 119 | } 120 | 121 | newLen := len(dq.chunkPitch) * 2 122 | newPitch := make([]*chunk[T], newLen, newLen) 123 | n := len(dq.chunks) 124 | dq.sFree = (newLen - n) / 2 125 | dq.eFree = (newLen - n) - dq.sFree 126 | chunks := newPitch[dq.sFree : dq.sFree+n] 127 | copy(chunks, dq.chunks) 128 | dq.chunkPitch = newPitch 129 | dq.chunks = chunks 130 | } 131 | 132 | func (dq *Deque[T]) expandEnd() { 133 | if f := dq.eFree; f == 0 { 134 | dq.realloc() 135 | } 136 | c := dq.chunkPool.Get().(*chunk[T]) 137 | c.s, c.e = 0, 0 138 | dq.eFree-- 139 | newEnd := len(dq.chunkPitch) - dq.eFree 140 | dq.chunkPitch[newEnd-1] = c 141 | dq.chunks = dq.chunkPitch[dq.sFree:newEnd] 142 | } 143 | 144 | func (dq *Deque[T]) expandStart() { 145 | if f := dq.sFree; f == 0 { 146 | dq.realloc() 147 | } 148 | c := dq.chunkPool.Get().(*chunk[T]) 149 | c.s, c.e = dq.chunkSize, dq.chunkSize 150 | dq.sFree-- 151 | dq.chunkPitch[dq.sFree] = c 152 | pitchLen := len(dq.chunkPitch) 153 | dq.chunks = dq.chunkPitch[dq.sFree : pitchLen-dq.eFree] 154 | } 155 | 156 | func (dq *Deque[T]) shrinkEnd() { 157 | pitchLen := len(dq.chunkPitch) 158 | dq.eFree++ 159 | newEnd := pitchLen - dq.eFree 160 | c := dq.chunkPitch[newEnd] 161 | dq.chunkPitch[newEnd] = nil 162 | dq.chunkPool.Put(c) 163 | dq.chunks = dq.chunkPitch[dq.sFree:newEnd] 164 | 165 | if dq.sFree+dq.eFree < pitchLen { 166 | return 167 | } 168 | dq.sFree = pitchLen / 2 169 | dq.eFree = pitchLen - dq.sFree 170 | } 171 | 172 | func (dq *Deque[T]) shrinkStart() { 173 | c := dq.chunkPitch[dq.sFree] 174 | dq.chunkPitch[dq.sFree] = nil 175 | dq.chunkPool.Put(c) 176 | dq.sFree++ 177 | pitchLen := len(dq.chunkPitch) 178 | dq.chunks = dq.chunkPitch[dq.sFree : pitchLen-dq.eFree] 179 | 180 | if dq.sFree+dq.eFree < pitchLen { 181 | return 182 | } 183 | dq.sFree = pitchLen / 2 184 | dq.eFree = pitchLen - dq.sFree 185 | } 186 | 187 | // PushBack adds a new value at the back of dq. 188 | func (dq *Deque[T]) PushBack(v T) { 189 | n := len(dq.chunks) 190 | if n == 0 || dq.chunks[n-1].e == dq.chunkSize { 191 | dq.expandEnd() 192 | n++ 193 | } 194 | c := dq.chunks[n-1] 195 | c.data[c.e] = v 196 | c.e++ 197 | dq.count++ 198 | } 199 | 200 | // PushFront adds a new value at the front of dq. 201 | func (dq *Deque[T]) PushFront(v T) { 202 | n := len(dq.chunks) 203 | if n == 0 || dq.chunks[0].s == 0 { 204 | dq.expandStart() 205 | } 206 | c := dq.chunks[0] 207 | c.s-- 208 | c.data[c.s] = v 209 | dq.count++ 210 | } 211 | 212 | // TryPopBack tries to remove a value from the back of dq and returns the removed value if any. 213 | // The return value ok indicates whether it succeeded. 214 | func (dq *Deque[T]) TryPopBack() (_ T, ok bool) { 215 | n := len(dq.chunks) 216 | if n == 0 { 217 | return *new(T), false 218 | } 219 | c := dq.chunks[n-1] 220 | if c.e == c.s { 221 | return *new(T), false 222 | } 223 | c.e-- 224 | r := c.data[c.e] 225 | var defVal T 226 | c.data[c.e] = defVal 227 | if n > 1 { 228 | if c.e == c.s { 229 | dq.shrinkEnd() 230 | } 231 | } else { 232 | if c.e == 0 { 233 | dq.shrinkEnd() 234 | } 235 | } 236 | dq.count-- 237 | return r, true 238 | } 239 | 240 | // PopBack removes a value from the back of dq and returns the removed value. 241 | // It panics if dq is empty. 242 | func (dq *Deque[T]) PopBack() T { 243 | n := len(dq.chunks) 244 | if n == 0 { 245 | panic(errEmpty) 246 | } 247 | c := dq.chunks[n-1] 248 | if c.e == c.s { 249 | panic(errEmpty) 250 | } 251 | c.e-- 252 | r := c.data[c.e] 253 | var defVal T 254 | c.data[c.e] = defVal 255 | if n > 1 { 256 | if c.e == c.s { 257 | dq.shrinkEnd() 258 | } 259 | } else { 260 | if c.e == 0 { 261 | dq.shrinkEnd() 262 | } 263 | } 264 | dq.count-- 265 | return r 266 | } 267 | 268 | // TryPopFront tries to remove a value from the front of dq and returns the removed value 269 | // if any. The return value ok indicates whether it succeeded. 270 | func (dq *Deque[T]) TryPopFront() (_ T, ok bool) { 271 | n := len(dq.chunks) 272 | if n == 0 { 273 | return *new(T), false 274 | } 275 | c := dq.chunks[0] 276 | if c.e == c.s { 277 | return *new(T), false 278 | } 279 | r := c.data[c.s] 280 | var defVal T 281 | c.data[c.s] = defVal 282 | c.s++ 283 | if n > 1 { 284 | if c.s == c.e { 285 | dq.shrinkStart() 286 | } 287 | } else { 288 | if c.s == dq.chunkSize { 289 | dq.shrinkStart() 290 | } 291 | } 292 | dq.count-- 293 | return r, true 294 | } 295 | 296 | // PopFront removes a value from the front of dq and returns the removed value. 297 | // It panics if dq is empty. 298 | func (dq *Deque[T]) PopFront() T { 299 | n := len(dq.chunks) 300 | if n == 0 { 301 | panic(errEmpty) 302 | } 303 | c := dq.chunks[0] 304 | if c.e == c.s { 305 | panic(errEmpty) 306 | } 307 | r := c.data[c.s] 308 | var defVal T 309 | c.data[c.s] = defVal 310 | c.s++ 311 | if n > 1 { 312 | if c.s == c.e { 313 | dq.shrinkStart() 314 | } 315 | } else { 316 | if c.s == dq.chunkSize { 317 | dq.shrinkStart() 318 | } 319 | } 320 | dq.count-- 321 | return r 322 | } 323 | 324 | // DequeueMany removes a number of values from the front of dq and returns 325 | // the removed values or nil if dq is empty. 326 | // 327 | // If max <= 0, DequeueMany removes and returns all the values in dq. 328 | func (dq *Deque[T]) DequeueMany(max int) []T { 329 | return dq.DequeueManyWithBuffer(max, nil) 330 | } 331 | 332 | // DequeueManyWithBuffer is similar to DequeueMany except that it uses 333 | // buf to store the removed values as long as it has enough space. 334 | func (dq *Deque[T]) DequeueManyWithBuffer(max int, buf []T) []T { 335 | n := dq.count 336 | if n == 0 { 337 | return nil 338 | } 339 | if max > 0 && n > max { 340 | n = max 341 | } 342 | if n <= cap(buf) { 343 | buf = buf[:n] 344 | } else { 345 | buf = make([]T, n) 346 | } 347 | 348 | var defVal T 349 | var i int 350 | for n > 0 { 351 | c := dq.chunks[0] 352 | num := minInt(n, c.e-c.s) 353 | copy(buf[i:], c.data[c.s:c.s+num]) 354 | i += num 355 | for j := c.s; j < c.s+num; j++ { 356 | c.data[j] = defVal 357 | } 358 | c.s += num 359 | if c.s == c.e { 360 | dq.shrinkStart() 361 | } 362 | n -= num 363 | } 364 | 365 | dq.count -= len(buf) 366 | return buf 367 | } 368 | 369 | // Back returns the last value of dq if any. The return value ok 370 | // indicates whether it succeeded. 371 | func (dq *Deque[T]) Back() (_ T, ok bool) { 372 | n := len(dq.chunks) 373 | if n == 0 { 374 | return *new(T), false 375 | } 376 | return dq.chunks[n-1].back() 377 | } 378 | 379 | // Front returns the first value of dq if any. The return value ok 380 | // indicates whether it succeeded. 381 | func (dq *Deque[T]) Front() (_ T, ok bool) { 382 | n := len(dq.chunks) 383 | if n == 0 { 384 | return *new(T), false 385 | } 386 | return dq.chunks[0].front() 387 | } 388 | 389 | // IsEmpty returns whether dq is empty. 390 | func (dq *Deque[T]) IsEmpty() bool { 391 | return dq.count == 0 392 | } 393 | 394 | // Len returns the number of values in dq. 395 | func (dq *Deque[T]) Len() int { 396 | return dq.count 397 | } 398 | 399 | // Enqueue is an alias of PushBack. 400 | func (dq *Deque[T]) Enqueue(v T) { 401 | dq.PushBack(v) 402 | } 403 | 404 | // TryDequeue is an alias of TryPopFront. 405 | func (dq *Deque[T]) TryDequeue() (_ T, ok bool) { 406 | return dq.TryPopFront() 407 | } 408 | 409 | // Dequeue is an alias of PopFront. 410 | func (dq *Deque[T]) Dequeue() T { 411 | return dq.PopFront() 412 | } 413 | 414 | // Dump returns all the values in dq. 415 | func (dq *Deque[T]) Dump() []T { 416 | n := dq.Len() 417 | if n == 0 { 418 | return nil 419 | } 420 | 421 | vals := make([]T, n) 422 | var i int 423 | for _, c := range dq.chunks { 424 | for j := c.s; j < c.e; j++ { 425 | vals[i] = c.data[j] 426 | i++ 427 | } 428 | } 429 | return vals 430 | } 431 | 432 | // Range iterates all the values in dq. Do NOT add values to dq or remove values from dq during Range. 433 | func (dq *Deque[T]) Range(f func(i int, v T) bool) { 434 | var i int 435 | for _, c := range dq.chunks { 436 | for j := c.s; j < c.e; j++ { 437 | if !f(i, c.data[j]) { 438 | return 439 | } 440 | i++ 441 | } 442 | } 443 | } 444 | 445 | // Peek returns the value at idx. It panics if idx is out of range. 446 | func (dq *Deque[T]) Peek(idx int) T { 447 | if idx < 0 || idx >= dq.count { 448 | panic(fmt.Errorf("out of range: %d", idx)) 449 | } 450 | 451 | i := idx 452 | for _, c := range dq.chunks { 453 | n := c.e - c.s 454 | if i < n { 455 | return c.data[c.s+i] 456 | } 457 | i -= n 458 | } 459 | 460 | panic("impossible") 461 | } 462 | 463 | // Replace replaces the value at idx with v. It panics if idx is out of range. 464 | func (dq *Deque[T]) Replace(idx int, v T) { 465 | if idx < 0 || idx >= dq.count { 466 | panic(fmt.Errorf("out of range: %d", idx)) 467 | } 468 | 469 | i := idx 470 | for _, c := range dq.chunks { 471 | n := c.e - c.s 472 | if i < n { 473 | c.data[c.s+i] = v 474 | return 475 | } 476 | i -= n 477 | } 478 | 479 | panic("impossible") 480 | } 481 | 482 | // Swap exchanges the two values at idx1 and idx2. It panics if idx1 or idx2 is out of range. 483 | func (dq *Deque[T]) Swap(idx1, idx2 int) { 484 | if idx1 < 0 || idx1 >= dq.count { 485 | panic(fmt.Errorf("out of range: %d", idx1)) 486 | } 487 | if idx2 < 0 || idx2 >= dq.count { 488 | panic(fmt.Errorf("out of range: %d", idx2)) 489 | } 490 | 491 | i1, i2 := idx1, idx2 492 | var p1, p2 *T 493 | for _, c := range dq.chunks { 494 | n := c.e - c.s 495 | if p1 == nil { 496 | if i1 < n { 497 | p1 = &c.data[c.s+i1] 498 | } else { 499 | i1 -= n 500 | } 501 | } 502 | if p2 == nil { 503 | if i2 < n { 504 | p2 = &c.data[c.s+i2] 505 | } else { 506 | i2 -= n 507 | } 508 | } 509 | if p1 != nil && p2 != nil { 510 | *p1, *p2 = *p2, *p1 511 | return 512 | } 513 | } 514 | 515 | panic("impossible") 516 | } 517 | 518 | // Insert inserts a new value v before the value at idx. 519 | // 520 | // Insert may cause the split of a chunk inside dq. Because the size of a chunk is fixed, 521 | // the amount of time taken by Insert has a reasonable limit. 522 | func (dq *Deque[T]) Insert(idx int, v T) { 523 | if idx <= 0 { 524 | dq.PushFront(v) 525 | return 526 | } 527 | if idx >= dq.Len() { 528 | dq.PushBack(v) 529 | return 530 | } 531 | 532 | i := idx 533 | for j, c := range dq.chunks { 534 | n := c.e - c.s 535 | if i < n { 536 | dq.insertImpl(i, v, j, c) 537 | dq.count++ 538 | return 539 | } 540 | i -= n 541 | } 542 | 543 | panic("impossible") 544 | } 545 | 546 | func (dq *Deque[T]) insertImpl(i int, v T, j int, c *chunk[T]) { 547 | sf0 := c.s > 0 548 | sf1 := j > 0 && dq.chunks[j-1].e < dq.chunkSize 549 | ef0 := c.e < dq.chunkSize 550 | ef1 := j+1 < len(dq.chunks) && dq.chunks[j+1].s > 0 551 | sfx := sf0 || sf1 552 | efx := ef0 || ef1 553 | if sfx { 554 | if efx { 555 | if i < c.e-c.s-i { 556 | dq.insertHead(i, v, j, c, sf0) 557 | return 558 | } else { 559 | dq.insertTail(i, v, j, c, ef0) 560 | return 561 | } 562 | } else { 563 | dq.insertHead(i, v, j, c, sf0) 564 | return 565 | } 566 | } else if efx { 567 | dq.insertTail(i, v, j, c, ef0) 568 | return 569 | } 570 | 571 | // Now c.s is zero 572 | if i < c.e-c.s-i { 573 | if i == 0 { 574 | cc := dq.insertNewChunk(j, true) 575 | cc.s, cc.e = 0, 1 576 | cc.data[0] = v 577 | } else { 578 | cc := dq.insertNewChunk(j, true) 579 | cc.s, cc.e = 0, i 580 | copy(cc.data, c.data[:i]) 581 | var defVal T 582 | for k := 0; k < i-1; k++ { 583 | c.data[k] = defVal 584 | } 585 | c.s = i - 1 586 | c.data[c.s] = v 587 | } 588 | } else { 589 | cc := dq.insertNewChunk(j, false) 590 | start := i 591 | cc.s, cc.e = start, c.e 592 | copy(cc.data[start:], c.data[i:]) 593 | var defVal T 594 | for k := i + 1; k < c.e; k++ { 595 | c.data[k] = defVal 596 | } 597 | c.data[i] = v 598 | c.e = i + 1 599 | } 600 | } 601 | 602 | func (dq *Deque[T]) insertHead(i int, v T, j int, c *chunk[T], sf0 bool) { 603 | if sf0 { 604 | if i == 0 { 605 | c.s-- 606 | c.data[c.s] = v 607 | } else { 608 | old := c.s 609 | c.s-- 610 | copy(c.data[c.s:], c.data[old:old+i]) 611 | c.data[c.s+i] = v 612 | } 613 | } else { 614 | // Now c.s is zero 615 | if i == 0 { 616 | cc := dq.chunks[j-1] 617 | cc.data[cc.e] = v 618 | cc.e++ 619 | } else { 620 | cc := dq.chunks[j-1] 621 | cc.data[cc.e] = c.data[0] 622 | cc.e++ 623 | copy(c.data, c.data[1:i]) 624 | c.data[i-1] = v 625 | } 626 | } 627 | } 628 | 629 | func (dq *Deque[T]) insertTail(i int, v T, j int, c *chunk[T], ef0 bool) { 630 | if ef0 { 631 | copy(c.data[c.s+i+1:], c.data[c.s+i:c.e]) 632 | c.data[c.s+i] = v 633 | c.e++ 634 | } else { 635 | cc := dq.chunks[j+1] 636 | cc.s-- 637 | cc.data[cc.s] = c.data[c.e-1] 638 | copy(c.data[c.s+i+1:], c.data[c.s+i:c.e-1]) 639 | c.data[c.s+i] = v 640 | } 641 | } 642 | 643 | func (dq *Deque[T]) insertNewChunk(j int, before bool) *chunk[T] { 644 | cc := dq.chunkPool.Get().(*chunk[T]) 645 | if before { 646 | if f := dq.sFree; f == 0 { 647 | dq.realloc() 648 | } 649 | dq.sFree-- 650 | pitchLen := len(dq.chunkPitch) 651 | a := dq.chunkPitch[dq.sFree : pitchLen-dq.eFree] 652 | copy(a, dq.chunks[:j]) 653 | a[j] = cc 654 | dq.chunks = a 655 | } else { 656 | if f := dq.eFree; f == 0 { 657 | dq.realloc() 658 | } 659 | dq.eFree-- 660 | pitchLen := len(dq.chunkPitch) 661 | a := dq.chunkPitch[dq.sFree : pitchLen-dq.eFree] 662 | copy(a[j+2:], dq.chunks[j+1:]) 663 | a[j+1] = cc 664 | dq.chunks = a 665 | } 666 | return cc 667 | } 668 | 669 | // Remove removes the value at idx. It panics if idx is out of range. 670 | func (dq *Deque[T]) Remove(idx int) { 671 | if idx < 0 || idx >= dq.count { 672 | panic(fmt.Errorf("out of range: %d", idx)) 673 | } 674 | 675 | i := idx 676 | for j, c := range dq.chunks { 677 | n := c.e - c.s 678 | if i < n { 679 | dq.removeElement(i, j, c) 680 | dq.count-- 681 | return 682 | } 683 | i -= n 684 | } 685 | 686 | panic("impossible") 687 | } 688 | 689 | func (dq *Deque[T]) removeElement(i, j int, c *chunk[T]) { 690 | n := c.e - c.s 691 | if n == 1 { 692 | c.data[c.s+i] = *new(T) 693 | dq.removeChunk(j, c) 694 | return 695 | } 696 | 697 | if i < n-i-1 { 698 | copy(c.data[c.s+1:], c.data[c.s:c.s+i]) 699 | c.data[c.s] = *new(T) 700 | c.s++ 701 | dq.mergeChunks(j - 1) 702 | } else { 703 | copy(c.data[c.s+i:], c.data[c.s+i+1:c.e]) 704 | c.data[c.e-1] = *new(T) 705 | c.e-- 706 | dq.mergeChunks(j) 707 | } 708 | } 709 | 710 | func (dq *Deque[T]) removeChunk(j int, c *chunk[T]) { 711 | dq.chunkPool.Put(c) 712 | if j < len(dq.chunks)-j-1 { 713 | copy(dq.chunks[1:], dq.chunks[:j]) 714 | dq.chunks[0] = nil 715 | dq.chunks = dq.chunks[1:] 716 | dq.sFree++ 717 | } else { 718 | copy(dq.chunks[j:], dq.chunks[j+1:]) 719 | newLen := len(dq.chunks) - 1 720 | dq.chunks[newLen] = nil 721 | dq.chunks = dq.chunks[:newLen] 722 | dq.eFree++ 723 | } 724 | } 725 | 726 | func (dq *Deque[T]) mergeChunks(j int) { 727 | if j < 0 { 728 | return 729 | } 730 | if j+1 >= len(dq.chunks) { 731 | return 732 | } 733 | 734 | c0 := dq.chunks[j] 735 | c1 := dq.chunks[j+1] 736 | n0 := c0.e - c0.s 737 | x0 := n0 + (dq.chunkSize >> 2) 738 | if x0 <= c1.s { 739 | c1.s -= n0 740 | copy(c1.data[c1.s:], c0.data[c0.s:c0.e]) 741 | var defVal T 742 | for i := c0.s; i < c0.e; i++ { 743 | c0.data[i] = defVal 744 | } 745 | dq.removeChunk(j, c0) 746 | return 747 | } 748 | 749 | n1 := c1.e - c1.s 750 | x1 := n1 + (dq.chunkSize >> 2) 751 | if x1 <= dq.chunkSize-c0.e { 752 | copy(c0.data[c0.e:], c1.data[c1.s:c1.e]) 753 | c0.e += n1 754 | var defVal T 755 | for i := c1.s; i < c1.e; i++ { 756 | c1.data[i] = defVal 757 | } 758 | dq.removeChunk(j+1, c1) 759 | return 760 | } 761 | } 762 | 763 | // Clear removes all the values from dq. 764 | func (dq *Deque[T]) Clear() { 765 | var defVal T 766 | for _, c := range dq.chunks { 767 | for j := c.s; j < c.e; j++ { 768 | c.data[j] = defVal 769 | } 770 | } 771 | for i, c := range dq.chunks { 772 | dq.chunkPool.Put(c) 773 | dq.chunks[i] = nil 774 | } 775 | 776 | dq.chunks = nil 777 | dq.count = 0 778 | 779 | dq.sFree = len(dq.chunkPitch) / 2 780 | dq.eFree = len(dq.chunkPitch) - dq.sFree 781 | } 782 | 783 | // Option represents the option of Deque. 784 | type Option func(*optionHolder) 785 | 786 | // WithChunkSize sets the chunk size of a Deque. 787 | func WithChunkSize(n int) Option { 788 | return func(holder *optionHolder) { 789 | if n >= 8 { 790 | holder.chunkSize = n 791 | } 792 | } 793 | } 794 | -------------------------------------------------------------------------------- /v2/deque_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "reflect" 7 | "sort" 8 | "testing" 9 | "time" 10 | "unsafe" 11 | ) 12 | 13 | type invariantParams struct { 14 | skipChunkMerge bool 15 | } 16 | 17 | type invariantOption func(params *invariantParams) 18 | 19 | func skipChunkMerge() invariantOption { 20 | return func(params *invariantParams) { 21 | params.skipChunkMerge = true 22 | } 23 | } 24 | 25 | //gocyclo:ignore 26 | func invariant(t *testing.T, dq *Deque[int], opts ...invariantOption) { 27 | t.Helper() 28 | var params invariantParams 29 | for _, opt := range opts { 30 | opt(¶ms) 31 | } 32 | 33 | if dq.sFree < 0 || dq.sFree > len(dq.chunkPitch) { 34 | t.Fatal("dq.sFree < 0 || dq.sFree > len(dq.chunkPitch)") 35 | } 36 | if dq.eFree < 0 || dq.eFree > len(dq.chunkPitch) { 37 | t.Fatal("dq.eFree < 0 || dq.eFree > len(dq.chunkPitch)") 38 | } 39 | if dq.sFree+dq.eFree+len(dq.chunks) != len(dq.chunkPitch) { 40 | t.Fatal("dq.sFree+dq.eFree+len(dq.chunks) != len(dq.chunkPitch)") 41 | } 42 | 43 | if dq.chunks != nil && dq.chunkPitch != nil { 44 | x1 := (*reflect.SliceHeader)(unsafe.Pointer(&dq.chunks)) 45 | x2 := (*reflect.SliceHeader)(unsafe.Pointer(&dq.chunkPitch)) 46 | beyond := x2.Data + uintptr(x2.Cap)*unsafe.Sizeof(*new(int)) 47 | if x1.Data < x2.Data || x1.Data >= beyond { 48 | t.Fatal(`x1.Data < x2.Data || x1.Data >= beyond`) 49 | } 50 | } 51 | 52 | if t.Name() != "TestDeque_shrinkEnd" && t.Name() != "TestDeque_shrinkStart" { 53 | for i := 0; i < len(dq.chunkPitch); i++ { 54 | if i < dq.sFree { 55 | if dq.chunkPitch[i] != nil { 56 | t.Fatal(`dq.chunkPitch[i] != nil [1]`) 57 | } 58 | } else if i >= len(dq.chunkPitch)-dq.eFree { 59 | if dq.chunkPitch[i] != nil { 60 | t.Fatal(`dq.chunkPitch[i] != nil [2]`) 61 | } 62 | } else { 63 | if dq.chunkPitch[i] == nil { 64 | t.Fatal(`dq.chunkPitch[i] == nil`) 65 | } 66 | } 67 | } 68 | } 69 | 70 | var count int 71 | for i, c := range dq.chunks { 72 | count += c.e - c.s 73 | if c.s < 0 || c.s > dq.chunkSize { 74 | t.Fatal("c.s < 0 || c.s > dq.chunkSize") 75 | } 76 | if c.e < 0 || c.e > dq.chunkSize { 77 | t.Fatal("c.e < 0 || c.e > dq.chunkSize") 78 | } 79 | if len(c.data) != dq.chunkSize { 80 | t.Fatal("len(c.data) != dq.chunkSize") 81 | } 82 | if c != dq.chunkPitch[dq.sFree+i] { 83 | t.Fatal("c != dq.chunkPitch[dq.sFree+i]") 84 | } 85 | if c.e < c.s { 86 | t.Fatal(`c.e < c.s`) 87 | } 88 | 89 | if !params.skipChunkMerge { 90 | if i+1 < len(dq.chunks) { 91 | if (c.e-c.s)+(dq.chunkSize>>2) <= dq.chunks[i+1].s { 92 | t.Fatal(`(c.e-c.s)+(dq.chunkSize>>2) <= dq.chunks[i+1].s`) 93 | } 94 | if (dq.chunks[i+1].e-dq.chunks[i+1].s)+(dq.chunkSize>>2) <= dq.chunkSize-c.e { 95 | t.Fatal(`(dq.chunks[i+1].e-dq.chunks[i+1].s)+(dq.chunkSize>>2) <= dq.chunkSize-c.e`) 96 | } 97 | } 98 | } 99 | 100 | for j, v := range c.data { 101 | if j < c.s || j >= c.e { 102 | if v != 0 { 103 | t.Fatal("any value beyond [s, e) in a chunk should always be the default value of its type") 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | func TestChunkSize(t *testing.T) { 111 | if NewDeque[int64]().chunkSize != 128 { 112 | t.Fatal(`NewDeque[int64]().chunkSize != 128`) 113 | } 114 | if NewDeque[[2]int64]().chunkSize != 64 { 115 | t.Fatal(`NewDeque[[2]int64]().chunkSize != 64`) 116 | } 117 | if NewDeque[[3]int64]().chunkSize != 42 { 118 | t.Fatal(`NewDeque[[3]int64]().chunkSize != 42`) 119 | } 120 | if NewDeque[[8]int64]().chunkSize != 16 { 121 | t.Fatal(`NewDeque[[8]int64]().chunkSize != 16`) 122 | } 123 | if NewDeque[[9]int64]().chunkSize != 16 { 124 | t.Fatal(`NewDeque[[9]int64]().chunkSize != 16`) 125 | } 126 | 127 | if NewDeque[int64](WithChunkSize(-100)).chunkSize != 128 { 128 | t.Fatal(`NewDeque[int64](WithChunkSize(-100)).chunkSize != 128`) 129 | } 130 | if NewDeque[int64](WithChunkSize(0)).chunkSize != 128 { 131 | t.Fatal(`NewDeque[int64](WithChunkSize(0)).chunkSize != 128`) 132 | } 133 | if NewDeque[int64](WithChunkSize(20)).chunkSize != 20 { 134 | t.Fatal(`NewDeque[int64](WithChunkSize(20)).chunkSize != 20`) 135 | } 136 | } 137 | 138 | //gocyclo:ignore 139 | func TestChunk(t *testing.T) { 140 | dq1 := NewDeque[int]() 141 | total1 := dq1.chunkSize * 3 142 | for i := 0; i < total1; i++ { 143 | dq1.PushBack(i) 144 | invariant(t, dq1) 145 | if v, ok := dq1.Back(); !ok || v != i { 146 | t.Fatal("!ok || v != i") 147 | } 148 | if v, ok := dq1.Front(); !ok || v != 0 { 149 | t.Fatal("!ok || v != 0") 150 | } 151 | } 152 | 153 | for i := 0; i < total1; i++ { 154 | dq1.PopFront() 155 | } 156 | if len(dq1.chunks) != 0 { 157 | t.Fatal(`len(dq1.chunks) != 0`) 158 | } 159 | if _, ok := dq1.Back(); ok { 160 | t.Fatal("ok should be false") 161 | } 162 | if _, ok := dq1.Front(); ok { 163 | t.Fatal("ok should be false") 164 | } 165 | 166 | dq1.PushBack(9) 167 | dq1.PopFront() 168 | if len(dq1.chunks) != 1 { 169 | t.Fatal(`len(dq1.chunks) != 1`) 170 | } 171 | if _, ok := dq1.chunks[0].back(); ok { 172 | t.Fatal("ok should be false") 173 | } 174 | if _, ok := dq1.chunks[0].front(); ok { 175 | t.Fatal("ok should be false") 176 | } 177 | invariant(t, dq1) 178 | 179 | dq2 := NewDeque[int]() 180 | total2 := dq2.chunkSize * 3 181 | for i := 0; i < total2; i++ { 182 | dq2.PushFront(i) 183 | invariant(t, dq2) 184 | if v, ok := dq2.Back(); !ok || v != 0 { 185 | t.Fatal("!ok || v != 0") 186 | } 187 | if v, ok := dq2.Front(); !ok || v != i { 188 | t.Fatal("!ok || v != i") 189 | } 190 | } 191 | 192 | for i := 0; i < total2; i++ { 193 | dq2.PopBack() 194 | } 195 | if len(dq2.chunks) != 0 { 196 | t.Fatal(`len(dq2.chunks) != 0`) 197 | } 198 | if _, ok := dq2.Back(); ok { 199 | t.Fatal("ok should be false") 200 | } 201 | if _, ok := dq2.Front(); ok { 202 | t.Fatal("ok should be false") 203 | } 204 | 205 | dq2.PushBack(9) 206 | dq2.PopFront() 207 | if len(dq2.chunks) != 1 { 208 | t.Fatal(`len(dq2.chunks) != 1`) 209 | } 210 | if _, ok := dq2.chunks[0].back(); ok { 211 | t.Fatal("ok should be false") 212 | } 213 | if _, ok := dq2.chunks[0].front(); ok { 214 | t.Fatal("ok should be false") 215 | } 216 | invariant(t, dq1) 217 | } 218 | 219 | //gocyclo:ignore 220 | func TestDeque_realloc(t *testing.T) { 221 | dq1 := NewDeque[int]() 222 | dq1.PushBack(1) 223 | for i := 0; i < 3; i++ { 224 | count := dq1.chunkSize * (dq1.eFree + 1) 225 | for j := 0; j < count; j++ { 226 | dq1.PushBack(j) 227 | } 228 | pitchLen := defaultPitchSize 229 | for j := 0; j < i+1; j++ { 230 | pitchLen *= 2 231 | } 232 | if len(dq1.chunkPitch) != pitchLen { 233 | t.Fatal(`len(dq1.chunkPitch) != pitchLen`) 234 | } 235 | if dq1.sFree != (pitchLen-(len(dq1.chunks)-1))/2 { 236 | t.Fatal(`dq1.sFree != (pitchLen-(len(dq1.chunks)-1))/2`) 237 | } 238 | } 239 | 240 | dq2 := NewDeque[int]() 241 | dq2.PushFront(1) 242 | for i := 0; i < 3; i++ { 243 | count := dq2.chunkSize * (dq2.sFree + 1) 244 | for j := 0; j < count; j++ { 245 | dq2.PushFront(j) 246 | } 247 | pitchLen := defaultPitchSize 248 | for j := 0; j < i+1; j++ { 249 | pitchLen *= 2 250 | } 251 | if len(dq2.chunkPitch) != pitchLen { 252 | t.Fatal(`len(dq2.chunkPitch) != pitchLen`) 253 | } 254 | if dq2.sFree != (pitchLen-(len(dq2.chunks)-1))/2-1 { 255 | t.Fatal(`dq2.sFree != (pitchLen-(len(dq2.chunks)-1))/2-1`) 256 | } 257 | } 258 | 259 | dq3 := NewDeque[int]() 260 | for i := 0; i < dq3.chunkSize*3; i++ { 261 | dq3.PushBack(i) 262 | } 263 | if dq3.sFree != 32 { 264 | t.Fatal(`dq3.sFree != 32`) 265 | } 266 | if dq3.eFree != 29 { 267 | t.Fatal(`dq3.eFree != 29`) 268 | } 269 | dq3.realloc() 270 | if dq3.sFree != 31 { 271 | t.Fatal(`dq3.sFree != 31`) 272 | } 273 | if dq3.eFree != 30 { 274 | t.Fatal(`dq3.eFree != 30`) 275 | } 276 | } 277 | 278 | func TestDeque_expandEnd(t *testing.T) { 279 | dq := NewDeque[int]() 280 | dq.sFree += 10 281 | dq.eFree -= 10 282 | sf := dq.sFree 283 | ef := dq.eFree 284 | dq.expandEnd() 285 | invariant(t, dq) 286 | if len(dq.chunks) != 1 { 287 | t.Fatal("len(dq.chunks) != 1") 288 | } 289 | if dq.sFree != sf { 290 | t.Fatal("dq.sFree != sf") 291 | } 292 | if dq.eFree != ef-1 { 293 | t.Fatal("dq.eFree != ef-1") 294 | } 295 | if dq.chunks[0] == nil { 296 | t.Fatal("dq.chunks[0] == nil") 297 | } 298 | } 299 | 300 | func TestDeque_expandStart(t *testing.T) { 301 | dq := NewDeque[int]() 302 | dq.sFree += 10 303 | dq.eFree -= 10 304 | sf := dq.sFree 305 | ef := dq.eFree 306 | dq.expandStart() 307 | invariant(t, dq) 308 | if len(dq.chunks) != 1 { 309 | t.Fatal("len(dq.chunks) != 1") 310 | } 311 | if dq.sFree != sf-1 { 312 | t.Fatal("dq.sFree != sf-1") 313 | } 314 | if dq.eFree != ef { 315 | t.Fatal("dq.eFree != ef") 316 | } 317 | if dq.chunks[0] == nil { 318 | t.Fatal("dq.chunks[0] == nil") 319 | } 320 | } 321 | 322 | func TestDeque_shrinkEnd(t *testing.T) { 323 | dq := NewDeque[int]() 324 | for i := 0; i < len(dq.chunkPitch); i++ { 325 | dq.chunkPitch[i] = &chunk[int]{ 326 | data: make([]int, dq.chunkSize), 327 | e: dq.chunkSize, 328 | } 329 | } 330 | dq.eFree -= 10 331 | sf := dq.sFree 332 | ef := dq.eFree 333 | dq.shrinkEnd() 334 | dq.count = dq.chunkSize * (len(dq.chunkPitch) - dq.sFree - dq.eFree) 335 | invariant(t, dq) 336 | if len(dq.chunks) != 9 { 337 | t.Fatal("len(dq.chunks) != 9") 338 | } 339 | if dq.sFree != sf { 340 | t.Fatal("dq.sFree != sf") 341 | } 342 | if dq.eFree != ef+1 { 343 | t.Fatal("dq.eFree != ef+1") 344 | } 345 | 346 | var cnt, idx int 347 | for i := 0; i < len(dq.chunkPitch); i++ { 348 | if dq.chunkPitch[i] != nil { 349 | cnt++ 350 | } else { 351 | idx = i 352 | } 353 | } 354 | if cnt != len(dq.chunkPitch)-1 { 355 | t.Fatal("cnt != len(dq.chunkPitch)-1") 356 | } 357 | if idx != len(dq.chunkPitch)-ef-1 { 358 | t.Fatal("idx != len(dq.chunkPitch)-ef-1") 359 | } 360 | 361 | dq.sFree = 60 362 | dq.eFree = len(dq.chunkPitch) - dq.sFree - 1 363 | dq.shrinkEnd() 364 | dq.count = dq.chunkSize * (len(dq.chunkPitch) - dq.sFree - dq.eFree) 365 | invariant(t, dq) 366 | if dq.sFree != 32 { 367 | t.Fatal("dq.sFree != 32") 368 | } 369 | if dq.eFree != 32 { 370 | t.Fatal("dq.eFree != 32") 371 | } 372 | } 373 | 374 | func TestDeque_shrinkStart(t *testing.T) { 375 | dq := NewDeque[int]() 376 | for i := 0; i < len(dq.chunkPitch); i++ { 377 | dq.chunkPitch[i] = &chunk[int]{ 378 | data: make([]int, dq.chunkSize), 379 | e: dq.chunkSize, 380 | } 381 | } 382 | dq.eFree -= 10 383 | sf := dq.sFree 384 | ef := dq.eFree 385 | dq.shrinkStart() 386 | dq.count = dq.chunkSize * (len(dq.chunkPitch) - dq.sFree - dq.eFree) 387 | invariant(t, dq) 388 | if len(dq.chunks) != 9 { 389 | t.Fatal("len(dq.chunks) != 9") 390 | } 391 | if dq.sFree != sf+1 { 392 | t.Fatal("dq.sFree != sf+1") 393 | } 394 | if dq.eFree != ef { 395 | t.Fatal("dq.eFree != ef") 396 | } 397 | 398 | var cnt, idx int 399 | for i := 0; i < len(dq.chunkPitch); i++ { 400 | if dq.chunkPitch[i] != nil { 401 | cnt++ 402 | } else { 403 | idx = i 404 | } 405 | } 406 | if cnt != len(dq.chunkPitch)-1 { 407 | t.Fatal("cnt != len(dq.chunkPitch)-1") 408 | } 409 | if idx != sf { 410 | t.Fatal("idx != sf") 411 | } 412 | 413 | dq.sFree = 60 414 | dq.eFree = len(dq.chunkPitch) - dq.sFree - 1 415 | dq.shrinkEnd() 416 | dq.count = dq.chunkSize * (len(dq.chunkPitch) - dq.sFree - dq.eFree) 417 | invariant(t, dq) 418 | if dq.sFree != 32 { 419 | t.Fatal("dq.sFree != 32") 420 | } 421 | if dq.eFree != 32 { 422 | t.Fatal("dq.eFree != 32") 423 | } 424 | } 425 | 426 | //gocyclo:ignore 427 | func TestDeque_PushBack(t *testing.T) { 428 | dq := NewDeque[int]() 429 | total := dq.chunkSize * 10 430 | for i := 0; i < total; i++ { 431 | if dq.Len() != i { 432 | t.Fatal("dq.Len() != i") 433 | } 434 | dq.PushBack(i) 435 | } 436 | for i := 0; i < total; i++ { 437 | if v := dq.PopFront(); v != i { 438 | invariant(t, dq) 439 | t.Fatal("v != i") 440 | } 441 | } 442 | for i := 0; i < total; i++ { 443 | dq.PushBack(i) 444 | } 445 | for i := 0; i < total; i++ { 446 | if v, ok := dq.TryPopFront(); !ok || v != i { 447 | invariant(t, dq) 448 | t.Fatal("!ok || v != i") 449 | } 450 | } 451 | 452 | for i := 0; i < total; i++ { 453 | dq.PushBack(i) 454 | } 455 | for i := 0; i < total; i++ { 456 | if v := dq.PopBack(); v != total-i-1 { 457 | invariant(t, dq) 458 | t.Fatal("v != total-i-1") 459 | } 460 | } 461 | for i := 0; i < total; i++ { 462 | dq.PushBack(i) 463 | } 464 | for i := 0; i < total; i++ { 465 | if v, ok := dq.TryPopBack(); !ok || v != total-i-1 { 466 | invariant(t, dq) 467 | t.Fatal("!ok || v != total-i-1") 468 | } 469 | } 470 | } 471 | 472 | //gocyclo:ignore 473 | func TestDeque_PushFront(t *testing.T) { 474 | dq := NewDeque[int]() 475 | total := dq.chunkSize * 10 476 | for i := 0; i < total; i++ { 477 | if dq.Len() != i { 478 | t.Fatal("dq.Len() != i") 479 | } 480 | dq.PushFront(i) 481 | } 482 | for i := 0; i < total; i++ { 483 | if v := dq.PopFront(); v != total-i-1 { 484 | invariant(t, dq) 485 | t.Fatal("v != total-i-1") 486 | } 487 | } 488 | for i := 0; i < total; i++ { 489 | dq.PushFront(i) 490 | } 491 | for i := 0; i < total; i++ { 492 | if v, ok := dq.TryPopFront(); !ok || v != total-i-1 { 493 | invariant(t, dq) 494 | t.Fatal("!ok || v != total-i-1") 495 | } 496 | } 497 | 498 | for i := 0; i < total; i++ { 499 | dq.PushFront(i) 500 | } 501 | for i := 0; i < total; i++ { 502 | if v := dq.PopBack(); v != i { 503 | invariant(t, dq) 504 | t.Fatal("v != i") 505 | } 506 | } 507 | for i := 0; i < total; i++ { 508 | dq.PushFront(i) 509 | } 510 | for i := 0; i < total; i++ { 511 | if v, ok := dq.TryPopBack(); !ok || v != i { 512 | invariant(t, dq) 513 | t.Fatal("!ok || v != i") 514 | } 515 | } 516 | } 517 | 518 | func TestDeque_PopBack(t *testing.T) { 519 | dq := NewDeque[int]() 520 | if _, ok := dq.TryPopBack(); ok { 521 | t.Fatal("ok should be false") 522 | } 523 | func() { 524 | defer func() { 525 | _ = recover() 526 | }() 527 | dq.PopBack() 528 | t.Fatal("PopBack should panic") 529 | }() 530 | 531 | dq.PushBack(1) 532 | dq.PopFront() 533 | if _, ok := dq.TryPopBack(); ok { 534 | t.Fatal("ok should be false") 535 | } 536 | func() { 537 | defer func() { 538 | _ = recover() 539 | }() 540 | dq.PopBack() 541 | t.Fatal("PopBack should panic") 542 | }() 543 | } 544 | 545 | func TestDeque_PopFront(t *testing.T) { 546 | dq := NewDeque[int]() 547 | if _, ok := dq.TryPopFront(); ok { 548 | t.Fatal("ok should be false") 549 | } 550 | func() { 551 | defer func() { 552 | _ = recover() 553 | }() 554 | dq.PopFront() 555 | t.Fatal("PopFront should panic") 556 | }() 557 | 558 | dq.PushBack(1) 559 | dq.PopFront() 560 | if _, ok := dq.TryPopFront(); ok { 561 | t.Fatal("ok should be false") 562 | } 563 | func() { 564 | defer func() { 565 | _ = recover() 566 | }() 567 | dq.PopFront() 568 | t.Fatal("PopFront should panic") 569 | }() 570 | } 571 | 572 | func TestDeque_DequeueMany(t *testing.T) { 573 | dq1 := NewDeque[int]() 574 | for i := -1; i <= 1; i++ { 575 | if dq1.DequeueMany(i) != nil { 576 | t.Fatalf("dq1.DequeueMany(%d) should return nil while dq1 is empty", i) 577 | } 578 | invariant(t, dq1) 579 | } 580 | 581 | dq2 := NewDeque[int]() 582 | for i := 0; i < 500; i++ { 583 | expected := make([]int, i) 584 | for j := 0; j < i; j++ { 585 | dq2.PushBack(j) 586 | expected[j] = j 587 | } 588 | invariant(t, dq2) 589 | ret := dq2.DequeueMany(0) 590 | if len(ret) != i { 591 | t.Fatalf("dq2.DequeueMany(0) should return %d values", i) 592 | } 593 | invariant(t, dq2) 594 | checkBufs(nil, ret, expected, fmt.Sprintf("i: %d", i), t) 595 | } 596 | 597 | const total = 100 598 | whole := make([]int, total) 599 | for i := 0; i < total; i++ { 600 | whole[i] = i 601 | } 602 | 603 | dq3 := NewDeque[int](WithChunkSize(16)) 604 | steps := []int{1, 2, 3, 5, 7, 8, 15, 16, 17, 31, 32, 33, 47, 48, 49} 605 | for i := 0; i < total; i++ { 606 | if dq3.Len() != 0 { 607 | t.Fatal(`dq3.Len() != 0`) 608 | } 609 | expected := whole[:i] 610 | for _, step := range steps { 611 | for j := 0; j < i; j++ { 612 | dq3.PushBack(j) 613 | } 614 | invariant(t, dq3) 615 | remaining := i 616 | for remaining > 0 { 617 | c := step 618 | if remaining < step { 619 | c = remaining 620 | } 621 | ret := dq3.DequeueMany(step) 622 | if len(ret) != c { 623 | t.Fatalf("len(ret) != c. len: %d, c: %d, i: %d, step: %d", len(ret), c, i, step) 624 | } 625 | invariant(t, dq3) 626 | start := i - remaining 627 | checkBufs(nil, ret, expected[start:start+c], fmt.Sprintf("i: %d, step: %d", i, step), t) 628 | remaining -= c 629 | } 630 | if dq3.DequeueMany(0) != nil { 631 | t.Fatalf("dq3.DequeueMany(0) != nil") 632 | } 633 | } 634 | } 635 | } 636 | 637 | func checkBufs(bufA, bufB, expected []int, suffix string, t *testing.T) { 638 | t.Helper() 639 | if len(bufB) != len(expected) { 640 | t.Fatal(`len(bufB) != len(expected). ` + suffix) 641 | } 642 | for i, v := range expected { 643 | if bufB[i] != v { 644 | t.Fatal(`bufB[i] != v. ` + suffix) 645 | } 646 | } 647 | if bufA == nil || bufB == nil { 648 | return 649 | } 650 | 651 | ptrA := (*reflect.SliceHeader)(unsafe.Pointer(&bufA)).Data 652 | ptrB := (*reflect.SliceHeader)(unsafe.Pointer(&bufB)).Data 653 | if len(bufB) <= cap(bufA) { 654 | if ptrB != ptrA { 655 | t.Fatal("ptrB != ptrA. " + suffix) 656 | } 657 | } else { 658 | if ptrB == ptrA { 659 | t.Fatal("ptrB != ptrA. " + suffix) 660 | } 661 | } 662 | } 663 | 664 | func TestDeque_DequeueManyWithBuffer(t *testing.T) { 665 | dq1 := NewDeque[int]() 666 | for i := -1; i <= 1; i++ { 667 | if dq1.DequeueManyWithBuffer(i, nil) != nil { 668 | t.Fatalf("dq1.DequeueManyWithBuffer(%d, nil) should return nil while dq1 is empty", i) 669 | } 670 | invariant(t, dq1) 671 | } 672 | 673 | dq2 := NewDeque[int]() 674 | for i := 0; i < 500; i++ { 675 | expected := make([]int, i) 676 | for j := 0; j < i; j++ { 677 | dq2.PushBack(j) 678 | expected[j] = j 679 | } 680 | invariant(t, dq2) 681 | size := dq2.chunkSize * 2 / (i%3 + 1) 682 | bufA := make([]int, size, size) 683 | bufB := dq2.DequeueManyWithBuffer(0, bufA) 684 | if len(bufB) != i { 685 | t.Fatalf("dq2.DequeueManyWithBuffer(0, bufA) should return %d values", i) 686 | } 687 | invariant(t, dq2) 688 | checkBufs(bufA, bufB, expected, fmt.Sprintf("i: %d", i), t) 689 | } 690 | 691 | const total = 100 692 | whole := make([]int, total) 693 | for i := 0; i < total; i++ { 694 | whole[i] = i 695 | } 696 | 697 | dq3 := NewDeque[int](WithChunkSize(16)) 698 | steps := []int{1, 2, 3, 5, 7, 8, 15, 16, 17, 31, 32, 33, 47, 48, 49} 699 | for i := 0; i < total; i++ { 700 | if dq3.Len() != 0 { 701 | t.Fatal(`dq3.Len() != 0`) 702 | } 703 | expected := whole[:i] 704 | for _, step := range steps { 705 | for j := 0; j < i; j++ { 706 | dq3.PushBack(j) 707 | } 708 | invariant(t, dq3) 709 | remaining := i 710 | for remaining > 0 { 711 | c := step 712 | if remaining < step { 713 | c = remaining 714 | } 715 | size := dq2.chunkSize * 2 / (i%3 + 1) 716 | bufA := make([]int, size, size) 717 | bufB := dq3.DequeueManyWithBuffer(step, bufA) 718 | if len(bufB) != c { 719 | t.Fatalf("len(bufB) != c. len: %d, c: %d, i: %d, step: %d", len(bufB), c, i, step) 720 | } 721 | invariant(t, dq3) 722 | start := i - remaining 723 | str := fmt.Sprintf("len: %d, c: %d, i: %d, j: %d", len(bufB), c, i, step) 724 | checkBufs(bufA, bufB, expected[start:start+c], str, t) 725 | remaining -= c 726 | } 727 | if dq3.DequeueMany(0) != nil { 728 | t.Fatalf("dq3.DequeueMany(0) != nil") 729 | } 730 | } 731 | } 732 | } 733 | 734 | func TestDeque_Back(t *testing.T) { 735 | dq := NewDeque[int]() 736 | if _, ok := dq.Back(); ok { 737 | t.Fatal("ok should be false") 738 | } 739 | for i := 0; i < 1000; i++ { 740 | dq.PushBack(i) 741 | if v, ok := dq.Back(); !ok || v != i { 742 | t.Fatal("!ok || v != i") 743 | } 744 | } 745 | 746 | var n2 int 747 | dq.Range(func(i int, v int) bool { 748 | n2++ 749 | return false 750 | }) 751 | if n2 != 1 { 752 | t.Fatal(`n2 != 1`) 753 | } 754 | } 755 | 756 | func TestDeque_Front(t *testing.T) { 757 | dq := NewDeque[int]() 758 | if _, ok := dq.Front(); ok { 759 | t.Fatal("ok should be false") 760 | } 761 | for i := 0; i < 1000; i++ { 762 | dq.PushFront(i) 763 | if v, ok := dq.Front(); !ok || v != i { 764 | t.Fatal("!ok || v != i") 765 | } 766 | } 767 | } 768 | 769 | func TestDeque_Dump(t *testing.T) { 770 | rand.Seed(time.Now().UnixNano()) 771 | var a []int 772 | dq := NewDeque[int](WithChunkSize(16)) 773 | if dq.Dump() != nil { 774 | t.Fatal(`dq.Dump() != nil`) 775 | } 776 | dq.PushBack(1) 777 | dq.PopFront() 778 | if dq.Dump() != nil { 779 | t.Fatal(`dq.Dump() != nil`) 780 | } 781 | 782 | for i := 0; i < 5000; i++ { 783 | invariant(t, dq) 784 | switch rand.Int() % 5 { 785 | case 0, 1: 786 | dq.PushBack(i) 787 | a = append(a, i) 788 | case 2: 789 | dq.PushFront(i) 790 | a = append([]int{i}, a...) 791 | case 3: 792 | if _, ok := dq.TryPopBack(); ok { 793 | a = a[:len(a)-1] 794 | } 795 | case 4: 796 | if _, ok := dq.TryPopFront(); ok { 797 | a = a[1:] 798 | } 799 | } 800 | 801 | b := dq.Dump() 802 | if len(b) != len(a) { 803 | t.Fatal("len(b) != len(a)") 804 | } 805 | for i, v := range a { 806 | if b[i] != v { 807 | t.Fatalf("b[i] != v. i: %d", i) 808 | } 809 | } 810 | } 811 | 812 | for _, idx := range []int{-1, dq.Len()} { 813 | func() { 814 | defer func() { 815 | _ = recover() 816 | }() 817 | dq.Peek(idx) 818 | t.Fatal("Peek should panic") 819 | }() 820 | } 821 | } 822 | 823 | func TestDeque_Replace(t *testing.T) { 824 | chunkSize := NewDeque[int]().chunkSize 825 | for _, n := range []int{1, 100, chunkSize, chunkSize + 1, chunkSize * 2, 1000} { 826 | a := make([]int, n) 827 | dq := NewDeque[int]() 828 | for i := 0; i < n; i++ { 829 | v := rand.Int() 830 | a[i] = v 831 | dq.PushBack(v) 832 | } 833 | for i := 0; i < 100; i++ { 834 | idx := rand.Intn(n) 835 | if dq.Peek(idx) != a[idx] { 836 | t.Fatal("dq.Peek(idx) != a[idx]") 837 | } 838 | 839 | val := rand.Int() 840 | a[idx] = val 841 | dq.Replace(idx, val) 842 | 843 | var n1 int 844 | dq.Range(func(i int, v int) bool { 845 | if a[i] != v { 846 | t.Fatal("a[i] != v") 847 | } 848 | n1++ 849 | return true 850 | }) 851 | if n1 != dq.Len() { 852 | t.Fatal(`n1 != dq.Len()`) 853 | } 854 | } 855 | 856 | for _, idx := range []int{-1, dq.Len()} { 857 | func() { 858 | defer func() { 859 | _ = recover() 860 | }() 861 | dq.Replace(idx, 999) 862 | t.Fatal("Replace should panic") 863 | }() 864 | } 865 | } 866 | } 867 | 868 | func TestDeque_Swap(t *testing.T) { 869 | chunkSize := NewDeque[int]().chunkSize 870 | for _, n := range []int{1, 100, chunkSize, chunkSize + 1, chunkSize * 2, 1000} { 871 | a := make([]int, n) 872 | dq := NewDeque[int]() 873 | for i := 0; i < n; i++ { 874 | v := rand.Int() 875 | a[i] = v 876 | dq.PushBack(v) 877 | } 878 | for i := 0; i < 100; i++ { 879 | idx1 := rand.Intn(n) 880 | if dq.Peek(idx1) != a[idx1] { 881 | t.Fatal("dq.Peek(idx1) != a[idx1]") 882 | } 883 | idx2 := rand.Intn(n) 884 | if dq.Peek(idx2) != a[idx2] { 885 | t.Fatal("dq.Peek(idx2) != a[idx2]") 886 | } 887 | 888 | a[idx1], a[idx2] = a[idx2], a[idx1] 889 | dq.Swap(idx1, idx2) 890 | 891 | var n1 int 892 | dq.Range(func(i int, v int) bool { 893 | if a[i] != v { 894 | t.Fatal("a[i] != v") 895 | } 896 | n1++ 897 | return true 898 | }) 899 | if n1 != dq.Len() { 900 | t.Fatal(`n1 != dq.Len()`) 901 | } 902 | } 903 | 904 | for _, idx := range []int{-1, dq.Len()} { 905 | func() { 906 | defer func() { 907 | _ = recover() 908 | }() 909 | dq.Swap(idx, 0) 910 | t.Fatal("Swap should panic") 911 | }() 912 | func() { 913 | defer func() { 914 | _ = recover() 915 | }() 916 | dq.Swap(0, idx) 917 | t.Fatal("Swap should panic") 918 | }() 919 | } 920 | } 921 | } 922 | 923 | func checkValues(t *testing.T, dq *Deque[int], expected ...int) { 924 | t.Helper() 925 | a := dq.Dump() 926 | for i, v := range a { 927 | if v != expected[i] { 928 | t.Fatal(`v != expected[i]`) 929 | } 930 | } 931 | } 932 | 933 | func TestDeque_Insert(t *testing.T) { 934 | dq := NewDeque[int]() 935 | dq.Insert(0, 100) 936 | invariant(t, dq) 937 | if v, ok := dq.Back(); !ok || v != 100 { 938 | t.Fatal(`v, ok := dq.Back(); !ok || v != 100`) 939 | } 940 | if v, ok := dq.Front(); !ok || v != 100 { 941 | t.Fatal(`v, ok := dq.Front(); !ok || v != 100`) 942 | } 943 | 944 | dq.Insert(-1, 99) 945 | invariant(t, dq) 946 | checkValues(t, dq, 99, 100) 947 | 948 | dq.Insert(0, 98) 949 | invariant(t, dq) 950 | checkValues(t, dq, 98, 99, 100) 951 | 952 | dq.Insert(3, 101) 953 | invariant(t, dq) 954 | checkValues(t, dq, 98, 99, 100, 101) 955 | 956 | dq.Insert(3, 102) 957 | invariant(t, dq) 958 | checkValues(t, dq, 98, 99, 100, 102, 101) 959 | 960 | expected := dq.Dump() 961 | for i := 0; i < dq.chunkSize; i++ { 962 | v := -1000 + i 963 | dq.Insert(0, v) 964 | invariant(t, dq) 965 | expected = append([]int{v}, expected...) 966 | checkValues(t, dq, expected...) 967 | } 968 | for i := 0; i < dq.chunkSize; i++ { 969 | v := 1000 + i 970 | dq.Insert(9999, v) 971 | invariant(t, dq) 972 | expected = append(expected, v) 973 | checkValues(t, dq, expected...) 974 | } 975 | for i := 0; i < dq.chunkSize*3; i++ { 976 | v := 3000 + i 977 | where := dq.Len() / 2 978 | dq.Insert(where, v) 979 | invariant(t, dq) 980 | expected = append(expected[:where], append([]int{v}, expected[where:]...)...) 981 | checkValues(t, dq, expected...) 982 | } 983 | } 984 | 985 | func freeSlots(dq *Deque[int], idx int, sFree, eFree int) { 986 | c := dq.chunks[idx] 987 | for i := 0; i < sFree; i++ { 988 | c.data[i] = 0 989 | } 990 | for i := dq.chunkSize - eFree; i < dq.chunkSize; i++ { 991 | c.data[i] = 0 992 | } 993 | dq.count -= sFree - c.s 994 | dq.count -= eFree - (dq.chunkSize - c.e) 995 | c.s = sFree 996 | c.e = dq.chunkSize - eFree 997 | } 998 | 999 | //gocyclo:ignore 1000 | func TestDeque_insertImpl(t *testing.T) { 1001 | dq := NewDeque[int]() 1002 | for i := 0; i < dq.chunkSize*3; i++ { 1003 | dq.PushBack(i) 1004 | } 1005 | if len(dq.chunks) != 3 { 1006 | t.Fatal(`len(dq.chunks) != 3`) 1007 | } 1008 | freeSlots(dq, 1, 10, 10) 1009 | invariant(t, dq) 1010 | 1011 | dq.Insert(dq.chunkSize, 888) 1012 | if v := dq.Peek(dq.chunkSize); v != 888 { 1013 | t.Fatal(`v := dq.Peek(dq.chunkSize); v != 888`) 1014 | } 1015 | if dq.chunks[1].s != 9 { 1016 | t.Fatal(`dq.chunks[1].s != 9`) 1017 | } 1018 | if dq.chunks[1].e != dq.chunkSize-10 { 1019 | t.Fatal(dq.chunks[1].e != dq.chunkSize-10) 1020 | } 1021 | invariant(t, dq) 1022 | 1023 | var idx int 1024 | idx = dq.Len() - dq.chunkSize - 1 1025 | dq.Insert(idx, 888) 1026 | if v := dq.Peek(idx); v != 888 { 1027 | t.Fatal(`v := dq.Peek(idx); v != 888`) 1028 | } 1029 | if dq.chunks[1].s != 9 { 1030 | t.Fatal(`dq.chunks[1].s != 9`) 1031 | } 1032 | if dq.chunks[1].e != dq.chunkSize-9 { 1033 | t.Fatal(`dq.chunks[1].e != dq.chunkSize-9`) 1034 | } 1035 | invariant(t, dq) 1036 | 1037 | idx = dq.chunkSize - 1 1038 | dq.Insert(idx, 999) 1039 | if v := dq.Peek(idx); v != 999 { 1040 | t.Fatal(`v := dq.Peek(idx); v != 999`) 1041 | } 1042 | if dq.chunks[1].s != 8 { 1043 | t.Fatal(`dq.chunks[1].s != 8`) 1044 | } 1045 | if dq.chunks[1].e != dq.chunkSize-9 { 1046 | t.Fatal(`dq.chunks[1].e != dq.chunkSize-9`) 1047 | } 1048 | invariant(t, dq) 1049 | 1050 | idx = dq.Len() - dq.chunkSize 1051 | dq.Insert(idx, 999) 1052 | if v := dq.Peek(idx); v != 999 { 1053 | t.Fatal(`v := dq.Peek(idx); v != 999`) 1054 | } 1055 | if dq.chunks[1].s != 8 { 1056 | t.Fatal(`dq.chunks[1].s != 8`) 1057 | } 1058 | if dq.chunks[1].e != dq.chunkSize-8 { 1059 | t.Fatal(`dq.chunks[1].e != dq.chunkSize-8`) 1060 | } 1061 | invariant(t, dq) 1062 | 1063 | for i := 0; i < dq.chunkSize*2; i++ { 1064 | dq.PushFront(i) 1065 | } 1066 | invariant(t, dq) 1067 | 1068 | dq.Insert(dq.chunkSize, 777) 1069 | if v := dq.Peek(dq.chunkSize); v != 777 { 1070 | t.Fatal(`v := dq.Peek(dq.chunkSize); v != 777`) 1071 | } 1072 | if dq.chunks[1].s != 0 || dq.chunks[1].e != 1 { 1073 | t.Fatal(`dq.chunks[1].s != 0 || dq.chunks[1].e != 1`) 1074 | } 1075 | invariant(t, dq) 1076 | 1077 | dq.Insert(10, 666) 1078 | if v := dq.Peek(10); v != 666 { 1079 | t.Fatal(`v := dq.Peek(10); v != 666`) 1080 | } 1081 | if dq.chunks[0].s != 0 || dq.chunks[0].e != 10 { 1082 | t.Fatal(`dq.chunks[0].s != 0 || dq.chunks[0].e != 10`) 1083 | } 1084 | if dq.chunks[1].s != 9 { 1085 | t.Fatal(`dq.chunks[1].s != 9`) 1086 | } 1087 | invariant(t, dq) 1088 | 1089 | for i := 0; i < dq.chunkSize*2; i++ { 1090 | dq.PushBack(i) 1091 | } 1092 | invariant(t, dq) 1093 | 1094 | idx = dq.Len() - 10 1095 | dq.Insert(idx, 666) 1096 | if v := dq.Peek(idx); v != 666 { 1097 | t.Fatal(`v := dq.Peek(idx); v != 666`) 1098 | } 1099 | if dq.chunks[len(dq.chunks)-1].s != dq.chunkSize-10 { 1100 | t.Fatal(`dq.chunks[len(dq.chunks)-1].s != dq.chunkSize-10`) 1101 | } 1102 | if dq.chunks[len(dq.chunks)-1].e != dq.chunkSize { 1103 | t.Fatal(`dq.chunks[len(dq.chunks)-1].e != dq.chunkSize`) 1104 | } 1105 | if dq.chunks[len(dq.chunks)-2].e != dq.chunkSize-9 { 1106 | t.Fatal(`dq.chunks[len(dq.chunks)-2].e != dq.chunkSize-9`) 1107 | } 1108 | invariant(t, dq) 1109 | } 1110 | 1111 | func TestDeque_insertNewChunk(t *testing.T) { 1112 | dq := NewDeque[int]() 1113 | for i := 0; i < dq.chunkSize*3; i++ { 1114 | dq.PushBack(i) 1115 | } 1116 | 1117 | dq.chunkPitch = dq.chunkPitch[dq.sFree : dq.sFree+len(dq.chunks)] 1118 | dq.sFree, dq.eFree = 0, 0 1119 | invariant(t, dq) 1120 | 1121 | dq.insertNewChunk(0, true) 1122 | invariant(t, dq) 1123 | 1124 | dq.chunkPitch = dq.chunkPitch[dq.sFree : dq.sFree+len(dq.chunks)] 1125 | dq.sFree, dq.eFree = 0, 0 1126 | 1127 | dq.insertNewChunk(len(dq.chunks)-1, false) 1128 | invariant(t, dq) 1129 | } 1130 | 1131 | //gocyclo:ignore 1132 | func TestDeque_Remove(t *testing.T) { 1133 | dq := NewDeque[int]() 1134 | var a []int 1135 | for i := 0; i < dq.chunkSize*3; i++ { 1136 | dq.PushBack(i) 1137 | a = append(a, i) 1138 | } 1139 | 1140 | for _, idx := range []int{-1, dq.Len()} { 1141 | func() { 1142 | defer func() { 1143 | _ = recover() 1144 | }() 1145 | dq.Remove(idx) 1146 | t.Fatal("Remove should panic") 1147 | }() 1148 | } 1149 | 1150 | for i := 0; i < dq.chunkSize; i++ { 1151 | idx := dq.chunkSize + (dq.chunkSize-i)/2 1152 | dq.Remove(idx) 1153 | a = append(a[:idx], a[idx+1:]...) 1154 | } 1155 | if len(dq.chunks) != 2 { 1156 | t.Fatal(`len(dq.chunks) != 2`) 1157 | } 1158 | invariant(t, dq) 1159 | checkValues(t, dq, a...) 1160 | 1161 | for i := 0; i < dq.chunkSize; i++ { 1162 | idx := (dq.chunkSize - i) / 2 1163 | dq.Remove(idx) 1164 | a = append(a[:idx], a[idx+1:]...) 1165 | } 1166 | if len(dq.chunks) != 1 { 1167 | t.Fatal(`len(dq.chunks) != 1`) 1168 | } 1169 | invariant(t, dq) 1170 | checkValues(t, dq, a...) 1171 | 1172 | for i := 0; i < 10; i++ { 1173 | idx := dq.chunkSize - i - 1 1174 | dq.Remove(idx) 1175 | a = append(a[:idx], a[idx+1:]...) 1176 | if dq.chunks[0].s != 0 || dq.chunks[0].e != idx { 1177 | t.Fatal(`dq.chunks[0].s != 0 || dq.chunks[0].e != idx`) 1178 | } 1179 | } 1180 | invariant(t, dq) 1181 | checkValues(t, dq, a...) 1182 | 1183 | n := dq.Len() 1184 | for i := 0; i < n; i++ { 1185 | dq.Remove(0) 1186 | } 1187 | if len(dq.chunks) != 0 { 1188 | t.Fatal(`len(dq.chunks) != 0`) 1189 | } 1190 | invariant(t, dq) 1191 | 1192 | var idx int 1193 | for i := 0; i < dq.chunkSize; i++ { 1194 | dq.PushBack(i) 1195 | } 1196 | idx = dq.chunkSize / 2 1197 | dq.Insert(idx, 999) 1198 | dq.Remove(idx) 1199 | if len(dq.chunks) != 2 { 1200 | t.Fatal(`len(dq.chunks) != 2`) 1201 | } 1202 | if dq.chunks[0].e != dq.chunks[1].s { 1203 | t.Fatal(`dq.chunks[0].e != dq.chunks[1].s`) 1204 | } 1205 | for i := 0; i < dq.chunkSize/4-1; i++ { 1206 | dq.Remove(idx) 1207 | } 1208 | if len(dq.chunks) != 2 { 1209 | t.Fatal(`len(dq.chunks) != 2`) 1210 | } 1211 | dq.Remove(idx) 1212 | if len(dq.chunks) != 1 { 1213 | t.Fatal(`len(dq.chunks) != 1`) 1214 | } 1215 | if dq.chunks[0].s != dq.chunkSize/4 { 1216 | t.Fatal(`dq.chunks[0].s != dq.chunkSize/4`) 1217 | } 1218 | invariant(t, dq) 1219 | 1220 | dq.Clear() 1221 | invariant(t, dq) 1222 | 1223 | for i := 0; i < dq.chunkSize*3/2; i++ { 1224 | dq.PushBack(i) 1225 | } 1226 | idx = dq.chunkSize / 2 1227 | for i := 0; i < dq.chunkSize/2; i++ { 1228 | dq.Remove(idx) 1229 | } 1230 | if len(dq.chunks) != 2 { 1231 | t.Fatal(`len(dq.chunks) != 2`) 1232 | } 1233 | if dq.chunks[0].e != dq.chunkSize/2 { 1234 | t.Fatal(`dq.chunks[0].e != dq.chunkSize/2`) 1235 | } 1236 | idx = dq.chunkSize / 4 1237 | for i := 0; i < dq.chunkSize/4-1; i++ { 1238 | dq.Remove(idx) 1239 | } 1240 | if len(dq.chunks) != 2 { 1241 | t.Fatal(`len(dq.chunks) != 2`) 1242 | } 1243 | dq.Remove(idx) 1244 | if len(dq.chunks) != 1 { 1245 | t.Fatal(`len(dq.chunks) != 1`) 1246 | } 1247 | if dq.chunks[0].e != dq.chunkSize*3/4 { 1248 | t.Fatal(`dq.chunks[0].e != dq.chunkSize*3/4`) 1249 | } 1250 | invariant(t, dq) 1251 | 1252 | for i := 0; i < 1000; i++ { 1253 | dq.PushFront(i) 1254 | } 1255 | dq.Clear() 1256 | invariant(t, dq) 1257 | dq.Clear() 1258 | invariant(t, dq) 1259 | } 1260 | 1261 | //gocyclo:ignore 1262 | func TestDeque_Random(t *testing.T) { 1263 | cfg1 := map[string]int{ 1264 | "Clear": 1, 1265 | "Dequeue": 100, 1266 | "DequeueMany": 10, 1267 | "Enqueue": 300, 1268 | "Insert": 300, 1269 | "PopBack": 100, 1270 | "PopFront": 100, 1271 | "PushBack": 300, 1272 | "PushFront": 300, 1273 | "Remove": 100, 1274 | "Replace": 10, 1275 | "Swap": 10, 1276 | "TryDequeue": 100, 1277 | "TryPopBack": 100, 1278 | "TryPopFront": 100, 1279 | } 1280 | 1281 | var keys []string 1282 | for k := range cfg1 { 1283 | keys = append(keys, k) 1284 | } 1285 | sort.Strings(keys) 1286 | 1287 | cfg2 := make(map[int]string) 1288 | var sum int 1289 | for _, k := range keys { 1290 | tmp := sum 1291 | sum += cfg1[k] 1292 | for i := tmp; i < sum; i++ { 1293 | cfg2[i] = k 1294 | } 1295 | } 1296 | 1297 | const total = 1000000 1298 | seed := time.Now().UnixMilli() 1299 | t.Logf("seed: %d", seed) 1300 | r := rand.New(rand.NewSource(seed)) 1301 | dq := NewDeque[int](WithChunkSize(8)) 1302 | var a []int 1303 | var i int 1304 | 1305 | checkValues := func() { 1306 | t.Helper() 1307 | if dq.Len() != len(a) { 1308 | t.Fatal(`dq.Len() != len(a)`) 1309 | } 1310 | var idx int 1311 | for _, c := range dq.chunks { 1312 | for j := c.s; j < c.e; j++ { 1313 | if c.data[j] != a[idx] { 1314 | t.Fatalf(`c.data[j] != a[idx]. i: %d`, i) 1315 | } 1316 | idx++ 1317 | } 1318 | } 1319 | } 1320 | 1321 | for i = 1; i <= total; i++ { 1322 | action := cfg2[r.Intn(sum)] 1323 | switch action { 1324 | case "Clear": 1325 | dq.Clear() 1326 | a = nil 1327 | case "Dequeue": 1328 | if len(a) > 0 { 1329 | dq.Dequeue() 1330 | a = a[1:] 1331 | } 1332 | case "DequeueMany": 1333 | count := r.Intn(20) + 1 1334 | dq.DequeueMany(count) 1335 | if len(a) > count { 1336 | a = a[count:] 1337 | } else { 1338 | a = a[:0] 1339 | } 1340 | case "Enqueue": 1341 | dq.Enqueue(i) 1342 | a = append(a, i) 1343 | case "Insert": 1344 | var idx int 1345 | if len(a) > 0 { 1346 | idx = r.Intn(len(a)) 1347 | } 1348 | dq.Insert(idx, i) 1349 | a = append(a, 0) 1350 | copy(a[idx+1:], a[idx:len(a)-1]) 1351 | a[idx] = i 1352 | case "PopBack": 1353 | if len(a) > 0 { 1354 | dq.PopBack() 1355 | a = a[:len(a)-1] 1356 | } 1357 | case "PopFront": 1358 | if len(a) > 0 { 1359 | dq.PopFront() 1360 | a = a[1:] 1361 | } 1362 | case "PushBack": 1363 | dq.PushBack(i) 1364 | a = append(a, i) 1365 | case "PushFront": 1366 | dq.PushFront(i) 1367 | a = append(a, i) 1368 | copy(a[1:], a[:len(a)-1]) 1369 | a[0] = i 1370 | case "Remove": 1371 | if len(a) > 0 { 1372 | idx := r.Intn(len(a)) 1373 | dq.Remove(idx) 1374 | a = append(a[:idx], a[idx+1:]...) 1375 | } 1376 | case "Replace": 1377 | if len(a) > 0 { 1378 | idx := r.Intn(len(a)) 1379 | dq.Replace(idx, i) 1380 | a[idx] = i 1381 | } 1382 | case "Swap": 1383 | if len(a) > 0 { 1384 | idx1 := r.Intn(len(a)) 1385 | idx2 := r.Intn(len(a)) 1386 | dq.Swap(idx1, idx2) 1387 | a[idx1], a[idx2] = a[idx2], a[idx1] 1388 | } 1389 | case "TryDequeue": 1390 | dq.TryDequeue() 1391 | if len(a) > 0 { 1392 | a = a[1:] 1393 | } 1394 | case "TryPopBack": 1395 | dq.TryPopBack() 1396 | if len(a) > 0 { 1397 | a = a[:len(a)-1] 1398 | } 1399 | case "TryPopFront": 1400 | dq.TryPopFront() 1401 | if len(a) > 0 { 1402 | a = a[1:] 1403 | } 1404 | default: 1405 | panic("impossible") 1406 | } 1407 | 1408 | checkValues() 1409 | invariant(t, dq, skipChunkMerge()) 1410 | 1411 | if testing.Verbose() { 1412 | if i%10000 == 0 { 1413 | fmt.Printf("Progress: %d\n", i) 1414 | } 1415 | } 1416 | } 1417 | } 1418 | --------------------------------------------------------------------------------