package aghalg import ( "github.com/AdguardTeam/golibs/errors" ) // RingBuffer is the implementation of ring buffer data structure. type RingBuffer[T any] struct { buf []T cur int full bool } // NewRingBuffer initializes the new instance of ring buffer. size must be // greater or equal to zero. func NewRingBuffer[T any](size int) (rb *RingBuffer[T]) { if size < 0 { panic(errors.Error("ring buffer: size must be greater or equal to zero")) } return &RingBuffer[T]{ buf: make([]T, size), } } // Append appends an element to the buffer. func (rb *RingBuffer[T]) Append(e T) { if len(rb.buf) == 0 { return } rb.buf[rb.cur] = e rb.cur = (rb.cur + 1) % cap(rb.buf) if rb.cur == 0 { rb.full = true } } // Range calls cb for each element of the buffer. If cb returns false it stops. func (rb *RingBuffer[T]) Range(cb func(T) (cont bool)) { before, after := rb.splitCur() for _, e := range before { if !cb(e) { return } } for _, e := range after { if !cb(e) { return } } } // ReverseRange calls cb for each element of the buffer in reverse order. If // cb returns false it stops. func (rb *RingBuffer[T]) ReverseRange(cb func(T) (cont bool)) { before, after := rb.splitCur() for i := len(after) - 1; i >= 0; i-- { if !cb(after[i]) { return } } for i := len(before) - 1; i >= 0; i-- { if !cb(before[i]) { return } } } // splitCur splits the buffer in two, before and after current position in // chronological order. If buffer is not full, after is nil. func (rb *RingBuffer[T]) splitCur() (before, after []T) { if len(rb.buf) == 0 { return nil, nil } cur := rb.cur if !rb.full { return rb.buf[:cur], nil } return rb.buf[cur:], rb.buf[:cur] } // Len returns a length of the buffer. func (rb *RingBuffer[T]) Len() (l int) { if !rb.full { return rb.cur } return cap(rb.buf) } // Clear clears the buffer. func (rb *RingBuffer[T]) Clear() { rb.full = false rb.cur = 0 }