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 }