github.com/cilium/cilium@v1.16.2/pkg/container/ring_buffer.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package container
     5  
     6  import (
     7  	"sort"
     8  )
     9  
    10  // RingBuffer is a generic ring buffer implementation that contains
    11  // sequential data (i.e. such as time ordered data).
    12  // RingBuffer is implemented using slices. From testing, this should
    13  // be fast than linked-list implementations, and also allows for efficient
    14  // indexing of ordered data.
    15  type RingBuffer struct {
    16  	buffer  []interface{}
    17  	next    int // index of ring buffer head.
    18  	maxSize int
    19  }
    20  
    21  // NewRingBuffer constructs a new ring buffer for a given buffer size.
    22  func NewRingBuffer(bufferSize int) *RingBuffer {
    23  	return &RingBuffer{
    24  		buffer:  make([]interface{}, 0, bufferSize),
    25  		maxSize: bufferSize,
    26  	}
    27  }
    28  
    29  func (eb *RingBuffer) isFull() bool {
    30  	return len(eb.buffer) >= eb.maxSize
    31  }
    32  
    33  func (eb *RingBuffer) incr() {
    34  	eb.next = (eb.next + 1) % eb.maxSize
    35  }
    36  
    37  // Add adds an element to the buffer.
    38  func (eb *RingBuffer) Add(e interface{}) {
    39  	if eb.maxSize == 0 {
    40  		return
    41  	}
    42  	if eb.isFull() {
    43  		eb.buffer[eb.next] = e
    44  		eb.incr()
    45  		return
    46  	}
    47  	eb.incr()
    48  	eb.buffer = append(eb.buffer, e)
    49  }
    50  
    51  func (eb *RingBuffer) dumpWithCallback(callback func(v interface{})) {
    52  	for i := 0; i < len(eb.buffer); i++ {
    53  		callback(eb.at(i))
    54  	}
    55  }
    56  
    57  func (eb *RingBuffer) at(i int) interface{} {
    58  	return eb.buffer[eb.mapIndex(i)]
    59  }
    60  
    61  // firstValidIndex returns the first **absolute** index in the buffer that satisfies
    62  // isValid.
    63  // note: this value needs to be mapped before indexing the buffer.
    64  func (eb *RingBuffer) firstValidIndex(isValid func(interface{}) bool) int {
    65  	return sort.Search(len(eb.buffer), func(i int) bool {
    66  		return isValid(eb.at(i))
    67  	})
    68  }
    69  
    70  // IterateValid calls the callback on each element of the buffer, starting with
    71  // the first element in the buffer that satisfies "isValid".
    72  func (eb *RingBuffer) IterateValid(isValid func(interface{}) bool, callback func(interface{})) {
    73  	startIndex := eb.firstValidIndex(isValid)
    74  	l := len(eb.buffer) - startIndex
    75  	for i := 0; i < l; i++ {
    76  		index := eb.mapIndex(startIndex + i)
    77  		callback(eb.buffer[index])
    78  	}
    79  }
    80  
    81  // maps index in [0:len(buffer)) to the actual index in buffer.
    82  func (eb *RingBuffer) mapIndex(indexOffset int) int {
    83  	ret := (eb.next + indexOffset) % len(eb.buffer)
    84  	return ret
    85  }
    86  
    87  // Compact clears out invalidated elements in the buffer.
    88  // This may require copying the entire buffer.
    89  // It is assumed that if buffer[i] is invalid then every entry [0...i-1] is also not valid.
    90  func (eb *RingBuffer) Compact(isValid func(interface{}) bool) {
    91  	if len(eb.buffer) == 0 {
    92  		return
    93  	}
    94  	startIndex := eb.firstValidIndex(isValid)
    95  	// In this case, we compact the entire buffer.
    96  	if startIndex >= len(eb.buffer) {
    97  		eb.buffer = []interface{}{}
    98  		eb.next = 0
    99  		return
   100  	}
   101  
   102  	mappedStart := eb.mapIndex(startIndex) // mapped start is the new index 0 of our buffer.
   103  	// new length will be how long the current buffer is, minus the absolute starting index.
   104  	newBufferLength := len(eb.buffer) - startIndex
   105  	// case where the head index is to the left of the tail index.
   106  	// e.x. [... head, tail, ...]
   107  	// mappedStart + newBufferLength is the upper bound of the new buffer list
   108  	// if we don't have to worry about mapping.
   109  	//
   110  	// e.x. [mappedStart:mappedStart+newBufferLength] <- this is our new buffer.
   111  	//
   112  	// If this value is less than or equal to the length then we don't need
   113  	// to worry about any part of the list wrapping around.
   114  	if mappedStart+newBufferLength > len(eb.buffer) {
   115  		// now we can find the actual end index, by offsetting the startIndex
   116  		// by the length and mapping it.
   117  		// [... startIndex+newBufferLen ... startIndex ...]
   118  		end := eb.mapIndex(startIndex + newBufferLength)
   119  		tmp := make([]interface{}, len(eb.buffer[:end]))
   120  		copy(tmp, eb.buffer[:end])
   121  
   122  		eb.buffer = eb.buffer[mappedStart:]
   123  		eb.buffer = append(eb.buffer, tmp...)
   124  
   125  		// at this point the buffer is such that the 0th element
   126  		// maps to the 0th index in the buffer array.
   127  		eb.next = len(eb.buffer)
   128  		if eb.isFull() {
   129  			eb.next = eb.next % eb.maxSize
   130  		}
   131  		return
   132  	}
   133  	// otherwise, the head is to the right of the tail.
   134  	begin := mappedStart
   135  	end := mappedStart + newBufferLength
   136  	eb.buffer = eb.buffer[begin:end]
   137  	eb.next = len(eb.buffer)
   138  	if eb.isFull() {
   139  		eb.next = eb.next % eb.maxSize
   140  	}
   141  }
   142  
   143  // Iterate is a convenience function over IterateValid that iterates
   144  // all elements in the buffer.
   145  func (eb *RingBuffer) Iterate(callback func(interface{})) {
   146  	eb.IterateValid(func(e interface{}) bool { return true }, callback)
   147  }
   148  
   149  // Size returns the size of the buffer.
   150  func (eb *RingBuffer) Size() int {
   151  	return len(eb.buffer)
   152  }