github.com/blend/go-sdk@v1.20220411.3/collections/ring_buffer.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package collections 9 10 import ( 11 "fmt" 12 "strings" 13 ) 14 15 const ( 16 ringBufferMinimumGrow = 4 17 ringBufferGrowFactor = 200 18 ringBufferDefaultCapacity = 4 19 ) 20 21 // NewRingBuffer creates a new, empty, RingBuffer. 22 func NewRingBuffer() *RingBuffer { 23 return &RingBuffer{ 24 array: make([]interface{}, ringBufferDefaultCapacity), 25 head: 0, 26 tail: 0, 27 size: 0, 28 } 29 } 30 31 // NewRingBufferWithCapacity creates a new ringbuffer with a given capacity. 32 func NewRingBufferWithCapacity(capacity int) *RingBuffer { 33 return &RingBuffer{ 34 array: make([]interface{}, capacity), 35 head: 0, 36 tail: 0, 37 size: 0, 38 } 39 } 40 41 // NewRingBufferFromValues creates a new ring buffer out of a slice. 42 func NewRingBufferFromValues(values []interface{}) *RingBuffer { 43 return &RingBuffer{ 44 array: values, 45 head: 0, 46 tail: len(values) - 1, 47 size: len(values), 48 } 49 } 50 51 var ( 52 _ Queue = (*RingBuffer)(nil) 53 ) 54 55 // RingBuffer is a fifo buffer that is backed by a pre-allocated array, instead of allocating 56 // a whole new node object for each element (which saves GC churn). 57 // Enqueue can be O(n), Dequeue can be O(1). 58 type RingBuffer struct { 59 array []interface{} 60 head int 61 tail int 62 size int 63 } 64 65 // Len returns the length of the ring buffer (as it is currently populated). 66 // Actual memory footprint may be different. 67 func (rb *RingBuffer) Len() (len int) { 68 return rb.size 69 } 70 71 // Capacity returns the total size of the ring bufffer, including empty elements. 72 func (rb *RingBuffer) Capacity() int { 73 return len(rb.array) 74 } 75 76 // Clear removes all objects from the RingBuffer. 77 func (rb *RingBuffer) Clear() { 78 if rb.head < rb.tail { 79 arrayClear(rb.array, rb.head, rb.size) 80 } else { 81 arrayClear(rb.array, rb.head, len(rb.array)-rb.head) 82 arrayClear(rb.array, 0, rb.tail) 83 } 84 85 rb.head = 0 86 rb.tail = 0 87 rb.size = 0 88 } 89 90 // Enqueue adds an element to the "back" of the RingBuffer. 91 func (rb *RingBuffer) Enqueue(object interface{}) { 92 if rb.size == len(rb.array) { 93 newCapacity := int(len(rb.array) * int(ringBufferGrowFactor/100)) 94 if newCapacity < (len(rb.array) + ringBufferMinimumGrow) { 95 newCapacity = len(rb.array) + ringBufferMinimumGrow 96 } 97 rb.setCapacity(newCapacity) 98 } 99 100 rb.array[rb.tail] = object 101 rb.tail = (rb.tail + 1) % len(rb.array) 102 rb.size++ 103 } 104 105 // Dequeue removes the first (oldest) element from the RingBuffer. 106 func (rb *RingBuffer) Dequeue() interface{} { 107 if rb.size == 0 { 108 return nil 109 } 110 111 removed := rb.array[rb.head] 112 rb.head = (rb.head + 1) % len(rb.array) 113 rb.size-- 114 115 return removed 116 } 117 118 // DequeueBack removes the last (newest) element from the RingBuffer. 119 func (rb *RingBuffer) DequeueBack() interface{} { 120 if rb.size == 0 { 121 return nil 122 } 123 124 // tail is the 125 var removed interface{} 126 if rb.tail == 0 { 127 removed = rb.array[len(rb.array)-1] 128 rb.tail = len(rb.array) - 1 129 } else { 130 removed = rb.array[rb.tail-1] 131 rb.tail = rb.tail - 1 132 } 133 rb.size-- 134 return removed 135 } 136 137 // Peek returns but does not remove the first element. 138 func (rb *RingBuffer) Peek() interface{} { 139 if rb.size == 0 { 140 return nil 141 } 142 return rb.array[rb.head] 143 } 144 145 // PeekBack returns but does not remove the last element. 146 func (rb *RingBuffer) PeekBack() interface{} { 147 if rb.size == 0 { 148 return nil 149 } 150 if rb.tail == 0 { 151 return rb.array[len(rb.array)-1] 152 } 153 return rb.array[rb.tail-1] 154 } 155 156 func (rb *RingBuffer) setCapacity(capacity int) { 157 newArray := make([]interface{}, capacity) 158 if rb.size > 0 { 159 if rb.head < rb.tail { 160 arrayCopy(rb.array, rb.head, newArray, 0, rb.size) 161 } else { 162 arrayCopy(rb.array, rb.head, newArray, 0, len(rb.array)-rb.head) 163 arrayCopy(rb.array, 0, newArray, len(rb.array)-rb.head, rb.tail) 164 } 165 } 166 rb.array = newArray 167 rb.head = 0 168 if rb.size == capacity { 169 rb.tail = 0 170 } else { 171 rb.tail = rb.size 172 } 173 } 174 175 // trimExcess resizes the buffer to better fit the contents. 176 func (rb *RingBuffer) trimExcess() { 177 threshold := float64(len(rb.array)) * 0.9 178 if rb.size < int(threshold) { 179 rb.setCapacity(rb.size) 180 } 181 } 182 183 // Contents returns the ring buffer, in order, as a slice. 184 func (rb *RingBuffer) Contents() []interface{} { 185 newArray := make([]interface{}, rb.size) 186 187 if rb.size == 0 { 188 return newArray 189 } 190 191 if rb.head < rb.tail { 192 arrayCopy(rb.array, rb.head, newArray, 0, rb.size) 193 arrayClear(rb.array, rb.head, rb.size) 194 } else { 195 arrayCopy(rb.array, rb.head, newArray, 0, len(rb.array)-rb.head) 196 arrayClear(rb.array, rb.head, len(rb.array)-rb.head) 197 arrayCopy(rb.array, 0, newArray, len(rb.array)-rb.head, rb.tail) 198 arrayClear(rb.array, 0, rb.tail) 199 } 200 201 return newArray 202 } 203 204 // Drain clears the buffer and removes the contents. 205 func (rb *RingBuffer) Drain() []interface{} { 206 newArray := make([]interface{}, rb.size) 207 208 if rb.size == 0 { 209 return newArray 210 } 211 212 if rb.head < rb.tail { 213 arrayCopy(rb.array, rb.head, newArray, 0, rb.size) 214 } else { 215 arrayCopy(rb.array, rb.head, newArray, 0, len(rb.array)-rb.head) 216 arrayCopy(rb.array, 0, newArray, len(rb.array)-rb.head, rb.tail) 217 } 218 219 rb.head = 0 220 rb.tail = 0 221 rb.size = 0 222 223 return newArray 224 } 225 226 // Each calls the consumer for each element in the buffer. 227 func (rb *RingBuffer) Each(consumer func(value interface{})) { 228 if rb.size == 0 { 229 return 230 } 231 232 if rb.head < rb.tail { 233 for cursor := rb.head; cursor < rb.tail; cursor++ { 234 consumer(rb.array[cursor]) 235 } 236 } else { 237 for cursor := rb.head; cursor < len(rb.array); cursor++ { 238 consumer(rb.array[cursor]) 239 } 240 for cursor := 0; cursor < rb.tail; cursor++ { 241 consumer(rb.array[cursor]) 242 } 243 } 244 } 245 246 // Consume calls the consumer for each element in the buffer, while also dequeueing that entry. 247 func (rb *RingBuffer) Consume(consumer func(value interface{})) { 248 if rb.size == 0 { 249 return 250 } 251 252 len := rb.Len() 253 for i := 0; i < len; i++ { 254 consumer(rb.Dequeue()) 255 } 256 } 257 258 // EachUntil calls the consumer for each element in the buffer with a stopping condition in head=>tail order. 259 func (rb *RingBuffer) EachUntil(consumer func(value interface{}) bool) { 260 if rb.size == 0 { 261 return 262 } 263 264 if rb.head < rb.tail { 265 for cursor := rb.head; cursor < rb.tail; cursor++ { 266 if !consumer(rb.array[cursor]) { 267 return 268 } 269 } 270 } else { 271 for cursor := rb.head; cursor < len(rb.array); cursor++ { 272 if !consumer(rb.array[cursor]) { 273 return 274 } 275 } 276 for cursor := 0; cursor < rb.tail; cursor++ { 277 if !consumer(rb.array[cursor]) { 278 return 279 } 280 } 281 } 282 } 283 284 // ReverseEachUntil calls the consumer for each element in the buffer with a stopping condition in tail=>head order. 285 func (rb *RingBuffer) ReverseEachUntil(consumer func(value interface{}) bool) { 286 if rb.size == 0 { 287 return 288 } 289 290 if rb.head < rb.tail { 291 for cursor := rb.tail - 1; cursor >= rb.head; cursor-- { 292 if !consumer(rb.array[cursor]) { 293 return 294 } 295 } 296 } else { 297 for cursor := rb.tail; cursor > 0; cursor-- { 298 if !consumer(rb.array[cursor]) { 299 return 300 } 301 } 302 for cursor := len(rb.array) - 1; cursor >= rb.head; cursor-- { 303 if !consumer(rb.array[cursor]) { 304 return 305 } 306 } 307 } 308 } 309 310 func (rb *RingBuffer) String() string { 311 var values []string 312 for _, elem := range rb.Contents() { 313 values = append(values, fmt.Sprintf("%v", elem)) 314 } 315 return strings.Join(values, " <= ") 316 } 317 318 func arrayClear(source []interface{}, index, length int) { 319 for x := 0; x < length; x++ { 320 absoluteIndex := x + index 321 source[absoluteIndex] = nil 322 } 323 } 324 325 func arrayCopy(source []interface{}, sourceIndex int, destination []interface{}, destinationIndex, length int) { 326 for x := 0; x < length; x++ { 327 from := sourceIndex + x 328 to := destinationIndex + x 329 330 destination[to] = source[from] 331 } 332 }