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  }