github.com/MetalBlockchain/metalgo@v1.11.9/utils/buffer/bounded_nonblocking_queue.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 "errors"
     7  
     8  var (
     9  	_ Queue[struct{}] = (*boundedQueue[struct{}])(nil)
    10  
    11  	errInvalidMaxSize = errors.New("maxSize must be greater than 0")
    12  )
    13  
    14  // A FIFO queue.
    15  type Queue[T any] interface {
    16  	// Pushes [elt] onto the queue.
    17  	// If the queue is full, the oldest element is evicted to make space.
    18  	Push(T)
    19  	// Pops the oldest element from the queue.
    20  	// Returns false if the queue is empty.
    21  	Pop() (T, bool)
    22  	// Returns the oldest element without removing it.
    23  	// Returns false if the queue is empty.
    24  	Peek() (T, bool)
    25  	// Returns the element at the given index without removing it.
    26  	// Index(0) returns the oldest element.
    27  	// Index(Len() - 1) returns the newest element.
    28  	// Returns false if there is no element at that index.
    29  	Index(int) (T, bool)
    30  	// Returns the number of elements in the queue.
    31  	Len() int
    32  	// Returns the queue elements from oldest to newest.
    33  	// This is an O(n) operation and should be used sparingly.
    34  	List() []T
    35  }
    36  
    37  // Keeps up to [maxSize] entries in an ordered buffer
    38  // and calls [onEvict] on any item that is evicted.
    39  // Not safe for concurrent use.
    40  type boundedQueue[T any] struct {
    41  	deque   Deque[T]
    42  	maxSize int
    43  	onEvict func(T)
    44  }
    45  
    46  // Returns a new bounded, non-blocking queue that holds up to [maxSize] elements.
    47  // When an element is evicted, [onEvict] is called with the evicted element.
    48  // If [onEvict] is nil, this is a no-op.
    49  // [maxSize] must be >= 1.
    50  // Not safe for concurrent use.
    51  func NewBoundedQueue[T any](maxSize int, onEvict func(T)) (Queue[T], error) {
    52  	if maxSize < 1 {
    53  		return nil, errInvalidMaxSize
    54  	}
    55  	return &boundedQueue[T]{
    56  		deque:   NewUnboundedDeque[T](maxSize + 1), // +1 so we never resize
    57  		maxSize: maxSize,
    58  		onEvict: onEvict,
    59  	}, nil
    60  }
    61  
    62  func (b *boundedQueue[T]) Push(elt T) {
    63  	if b.deque.Len() == b.maxSize {
    64  		evicted, _ := b.deque.PopLeft()
    65  		if b.onEvict != nil {
    66  			b.onEvict(evicted)
    67  		}
    68  	}
    69  	_ = b.deque.PushRight(elt)
    70  }
    71  
    72  func (b *boundedQueue[T]) Pop() (T, bool) {
    73  	return b.deque.PopLeft()
    74  }
    75  
    76  func (b *boundedQueue[T]) Peek() (T, bool) {
    77  	return b.deque.PeekLeft()
    78  }
    79  
    80  func (b *boundedQueue[T]) Index(i int) (T, bool) {
    81  	return b.deque.Index(i)
    82  }
    83  
    84  func (b *boundedQueue[T]) Len() int {
    85  	return b.deque.Len()
    86  }
    87  
    88  func (b *boundedQueue[T]) List() []T {
    89  	return b.deque.List()
    90  }