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  }