github.com/MetalBlockchain/metalgo@v1.11.9/utils/buffer/unbounded_deque.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package buffer 5 6 import "github.com/MetalBlockchain/metalgo/utils" 7 8 const defaultInitSize = 32 9 10 // An unbounded deque (double-ended queue). 11 // See https://en.wikipedia.org/wiki/Double-ended_queue 12 // Not safe for concurrent access. 13 type Deque[T any] interface { 14 // Place an element at the leftmost end of the deque. 15 // Returns true if the element was placed in the deque. 16 PushLeft(T) bool 17 // Place an element at the rightmost end of the deque. 18 // Returns true if the element was placed in the deque. 19 PushRight(T) bool 20 // Remove and return the leftmost element of the deque. 21 // Returns false if the deque is empty. 22 PopLeft() (T, bool) 23 // Remove and return the rightmost element of the deque. 24 // Returns false if the deque is empty. 25 PopRight() (T, bool) 26 // Return the leftmost element of the deque without removing it. 27 // Returns false if the deque is empty. 28 PeekLeft() (T, bool) 29 // Return the rightmost element of the deque without removing it. 30 // Returns false if the deque is empty. 31 PeekRight() (T, bool) 32 // Returns the element at the given index. 33 // Returns false if the index is out of bounds. 34 // The leftmost element is at index 0. 35 Index(int) (T, bool) 36 // Returns the number of elements in the deque. 37 Len() int 38 // Returns the elements in the deque from left to right. 39 List() []T 40 } 41 42 // Returns a new unbounded deque with the given initial slice size. 43 // Note that the returned deque is always empty -- [initSize] is just 44 // a hint to prevent unnecessary resizing. 45 func NewUnboundedDeque[T any](initSize int) Deque[T] { 46 if initSize < 2 { 47 initSize = defaultInitSize 48 } 49 return &unboundedSliceDeque[T]{ 50 // Note that [initSize] must be >= 2 to satisfy invariants (1) and (2). 51 data: make([]T, initSize), 52 right: 1, 53 } 54 } 55 56 // Invariants after each function call and before the first call: 57 // (1) The next element pushed left will be placed at data[left] 58 // (2) The next element pushed right will be placed at data[right] 59 // (3) There are [size] elements in the deque. 60 type unboundedSliceDeque[T any] struct { 61 size, left, right int 62 data []T 63 } 64 65 func (b *unboundedSliceDeque[T]) PushRight(elt T) bool { 66 // Invariant (2) says it's safe to place the element without resizing. 67 b.data[b.right] = elt 68 b.size++ 69 b.right++ 70 b.right %= len(b.data) 71 72 b.resize() 73 return true 74 } 75 76 func (b *unboundedSliceDeque[T]) PushLeft(elt T) bool { 77 // Invariant (1) says it's safe to place the element without resizing. 78 b.data[b.left] = elt 79 b.size++ 80 b.left-- 81 if b.left < 0 { 82 b.left = len(b.data) - 1 // Wrap around 83 } 84 85 b.resize() 86 return true 87 } 88 89 func (b *unboundedSliceDeque[T]) PopLeft() (T, bool) { 90 if b.size == 0 { 91 return utils.Zero[T](), false 92 } 93 idx := b.leftmostEltIdx() 94 elt := b.data[idx] 95 // Zero out to prevent memory leak. 96 b.data[idx] = utils.Zero[T]() 97 b.size-- 98 b.left++ 99 b.left %= len(b.data) 100 return elt, true 101 } 102 103 func (b *unboundedSliceDeque[T]) PeekLeft() (T, bool) { 104 if b.size == 0 { 105 return utils.Zero[T](), false 106 } 107 idx := b.leftmostEltIdx() 108 return b.data[idx], true 109 } 110 111 func (b *unboundedSliceDeque[T]) PopRight() (T, bool) { 112 if b.size == 0 { 113 return utils.Zero[T](), false 114 } 115 idx := b.rightmostEltIdx() 116 elt := b.data[idx] 117 // Zero out to prevent memory leak. 118 b.data[idx] = utils.Zero[T]() 119 b.size-- 120 b.right-- 121 if b.right < 0 { 122 b.right = len(b.data) - 1 // Wrap around 123 } 124 return elt, true 125 } 126 127 func (b *unboundedSliceDeque[T]) PeekRight() (T, bool) { 128 if b.size == 0 { 129 return utils.Zero[T](), false 130 } 131 idx := b.rightmostEltIdx() 132 return b.data[idx], true 133 } 134 135 func (b *unboundedSliceDeque[T]) Index(idx int) (T, bool) { 136 if idx < 0 || idx >= b.size { 137 return utils.Zero[T](), false 138 } 139 leftmostIdx := b.leftmostEltIdx() 140 idx = (leftmostIdx + idx) % len(b.data) 141 return b.data[idx], true 142 } 143 144 func (b *unboundedSliceDeque[T]) Len() int { 145 return b.size 146 } 147 148 func (b *unboundedSliceDeque[T]) List() []T { 149 if b.size == 0 { 150 return nil 151 } 152 153 list := make([]T, b.size) 154 leftmostIdx := b.leftmostEltIdx() 155 if numCopied := copy(list, b.data[leftmostIdx:]); numCopied < b.size { 156 // We copied all of the elements from the leftmost element index 157 // to the end of the underlying slice, but we still haven't copied 158 // all of the elements, so wrap around and copy the rest. 159 copy(list[numCopied:], b.data[:b.right]) 160 } 161 return list 162 } 163 164 func (b *unboundedSliceDeque[T]) leftmostEltIdx() int { 165 if b.left == len(b.data)-1 { // Wrap around case 166 return 0 167 } 168 return b.left + 1 // Normal case 169 } 170 171 func (b *unboundedSliceDeque[T]) rightmostEltIdx() int { 172 if b.right == 0 { 173 return len(b.data) - 1 // Wrap around case 174 } 175 return b.right - 1 // Normal case 176 } 177 178 func (b *unboundedSliceDeque[T]) resize() { 179 if b.size != len(b.data) { 180 return 181 } 182 newData := make([]T, b.size*2) 183 leftmostIdx := b.leftmostEltIdx() 184 copy(newData, b.data[leftmostIdx:]) 185 numCopied := len(b.data) - leftmostIdx 186 copy(newData[numCopied:], b.data[:b.right]) 187 b.data = newData 188 b.left = len(b.data) - 1 189 b.right = b.size 190 }