github.com/decred/dcrlnd@v0.7.6/queue/circular_buf.go (about) 1 package queue 2 3 import ( 4 "errors" 5 ) 6 7 // errInvalidSize is returned when an invalid size for a buffer is provided. 8 var errInvalidSize = errors.New("buffer size must be > 0") 9 10 // CircularBuffer is a buffer which retains a set of values in memory, and 11 // overwrites the oldest item in the buffer when a new item needs to be 12 // written. 13 type CircularBuffer struct { 14 // total is the total number of items that have been added to the 15 // buffer. 16 total int 17 18 // items is the set of buffered items. 19 items []interface{} 20 } 21 22 // NewCircularBuffer returns a new circular buffer with the size provided. It 23 // will fail if a zero or negative size parameter is provided. 24 func NewCircularBuffer(size int) (*CircularBuffer, error) { 25 if size <= 0 { 26 return nil, errInvalidSize 27 } 28 29 return &CircularBuffer{ 30 total: 0, 31 32 // Create a slice with length and capacity equal to the size of 33 // the buffer so that we do not need to resize the underlying 34 // array when we add items. 35 items: make([]interface{}, size), 36 }, nil 37 } 38 39 // index returns the index that should be written to next. 40 func (c *CircularBuffer) index() int { 41 return c.total % len(c.items) 42 } 43 44 // Add adds an item to the buffer, overwriting the oldest item if the buffer 45 // is full. 46 func (c *CircularBuffer) Add(item interface{}) { 47 // Set the item in the next free index in the items array. 48 c.items[c.index()] = item 49 50 // Increment the total number of items that we have stored. 51 c.total++ 52 } 53 54 // List returns a copy of the items in the buffer ordered from the oldest to 55 // newest item. 56 func (c *CircularBuffer) List() []interface{} { 57 size := cap(c.items) 58 index := c.index() 59 60 switch { 61 // If no items have been stored yet, we can just return a nil list. 62 case c.total == 0: 63 return nil 64 65 // If we have added fewer items than the buffer size, we can simply 66 // return the total number of items from the beginning of the list 67 // to the index. This special case is added because the oldest item 68 // is at the beginning of the underlying array, not at the index when 69 // we have not filled the array yet. 70 case c.total < size: 71 resp := make([]interface{}, c.total) 72 copy(resp, c.items[:c.index()]) 73 return resp 74 } 75 76 resp := make([]interface{}, size) 77 78 // Get the items in the underlying array from index to end, the first 79 // item in this slice will be the oldest item in the list. 80 firstHalf := c.items[index:] 81 82 // Copy the first set into our response slice from index 0, so that 83 // the response returned is from oldest to newest. 84 copy(resp, firstHalf) 85 86 // Get the items in the underlying array from beginning until the write 87 // index, the last item in this slice will be the newest item in the 88 // list. 89 secondHalf := c.items[:index] 90 91 // Copy the second set of items into the response slice offset by the 92 // length of the first set of items so that we return a response which 93 // is ordered from oldest to newest entry. 94 copy(resp[len(firstHalf):], secondHalf) 95 96 return resp 97 } 98 99 // Total returns the total number of items that have been added to the buffer. 100 func (c *CircularBuffer) Total() int { 101 return c.total 102 } 103 104 // Latest returns the item that was most recently added to the buffer. 105 func (c *CircularBuffer) Latest() interface{} { 106 // If no items have been added yet, return nil. 107 if c.total == 0 { 108 return nil 109 } 110 111 // The latest item is one before our total, mod by length. 112 latest := (c.total - 1) % len(c.items) 113 114 // Return the latest item added. 115 return c.items[latest] 116 }