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