fifo.gno

5.69 Kb ยท 250 lines
  1// Package fifo implements a fixed-size FIFO (First-In-First-Out) list data structure
  2// using a singly-linked list. The implementation prioritizes storage efficiency by minimizing
  3// storage operations - each add/remove operation only updates 1-2 pointers, regardless of
  4// list size.
  5//
  6// Key features:
  7// - Fixed-size with automatic removal of oldest entries when full
  8// - Support for both prepend (add at start) and append (add at end) operations
  9// - Constant storage usage through automatic pruning
 10// - O(1) append operations and latest element access
 11// - Iterator support for sequential access
 12// - Dynamic size adjustment via SetMaxSize
 13//
 14// This implementation is optimized for frequent updates, as insertions and deletions only
 15// require updating 1-2 pointers. However, random access operations are O(n) as they require
 16// traversing the list. For use cases where writes are rare, a slice-based
 17// implementation might be more suitable.
 18//
 19// The linked list structure is equally efficient for storing both small values (like pointers)
 20// and larger data structures, as each node maintains a single next-pointer regardless of the
 21// stored value's size.
 22//
 23// Example usage:
 24//
 25//	list := fifo.New(3)        // Create a new list with max size 3
 26//	list.Append("a")           // List: [a]
 27//	list.Append("b")           // List: [a b]
 28//	list.Append("c")           // List: [a b c]
 29//	list.Append("d")           // List: [b c d] (oldest element "a" was removed)
 30//	latest := list.Latest()    // Returns "d"
 31//	all := list.Entries()      // Returns ["b", "c", "d"]
 32package fifo
 33
 34// node represents a single element in the linked list
 35type node struct {
 36	value any
 37	next  *node
 38}
 39
 40// List represents a fixed-size FIFO list
 41type List struct {
 42	head    *node
 43	tail    *node
 44	size    int
 45	maxSize int
 46}
 47
 48// New creates a new FIFO list with the specified maximum size
 49func New(maxSize int) *List {
 50	return &List{
 51		maxSize: maxSize,
 52	}
 53}
 54
 55// Prepend adds a new entry at the start of the list. If the list exceeds maxSize,
 56// the last entry is automatically removed.
 57func (l *List) Prepend(entry any) {
 58	if l.maxSize == 0 {
 59		return
 60	}
 61
 62	newNode := &node{value: entry}
 63
 64	if l.head == nil {
 65		l.head = newNode
 66		l.tail = newNode
 67		l.size = 1
 68		return
 69	}
 70
 71	newNode.next = l.head
 72	l.head = newNode
 73
 74	if l.size < l.maxSize {
 75		l.size++
 76		return
 77	}
 78
 79	// Remove last element by traversing to second-to-last
 80	if l.size == 1 {
 81		// Special case: if size is 1, just update both pointers
 82		l.head = newNode
 83		l.tail = newNode
 84		newNode.next = nil
 85		return
 86
 87	}
 88
 89	// Find second-to-last node
 90	current := l.head
 91	for current.next != l.tail {
 92		current = current.next
 93	}
 94	current.next = nil
 95	l.tail = current
 96
 97}
 98
 99// Append adds a new entry at the end of the list. If the list exceeds maxSize,
100// the first entry is automatically removed.
101func (l *List) Append(entry any) {
102	if l.maxSize == 0 {
103		return
104	}
105
106	newNode := &node{value: entry}
107
108	if l.head == nil {
109		l.head = newNode
110		l.tail = newNode
111		l.size = 1
112		return
113	}
114
115	l.tail.next = newNode
116	l.tail = newNode
117
118	if l.size < l.maxSize {
119		l.size++
120	} else {
121		l.head = l.head.next
122	}
123}
124
125// Get returns the entry at the specified index.
126// Index 0 is the oldest entry, Size()-1 is the newest.
127func (l *List) Get(index int) any {
128	if index < 0 || index >= l.size {
129		return nil
130	}
131
132	current := l.head
133	for i := 0; i < index; i++ {
134		current = current.next
135	}
136	return current.value
137}
138
139// Size returns the current number of entries in the list
140func (l *List) Size() int {
141	return l.size
142}
143
144// MaxSize returns the maximum size configured for this list
145func (l *List) MaxSize() int {
146	return l.maxSize
147}
148
149// Entries returns all current entries as a slice
150func (l *List) Entries() []any {
151	entries := make([]any, l.size)
152	current := l.head
153	for i := 0; i < l.size; i++ {
154		entries[i] = current.value
155		current = current.next
156	}
157	return entries
158}
159
160// Iterator returns a function that can be used to iterate over the entries
161// from oldest to newest. Returns nil when there are no more entries.
162func (l *List) Iterator() func() any {
163	current := l.head
164	return func() any {
165		if current == nil {
166			return nil
167		}
168		value := current.value
169		current = current.next
170		return value
171	}
172}
173
174// Latest returns the most recent entry.
175// Returns nil if the list is empty.
176func (l *List) Latest() any {
177	if l.tail == nil {
178		return nil
179	}
180	return l.tail.value
181}
182
183// SetMaxSize updates the maximum size of the list.
184// If the new maxSize is smaller than the current size,
185// the oldest entries are removed to fit the new size.
186func (l *List) SetMaxSize(maxSize int) {
187	if maxSize < 0 {
188		maxSize = 0
189	}
190
191	// If new maxSize is smaller than current size,
192	// remove oldest entries until we fit
193	if maxSize < l.size {
194		// Special case: if new maxSize is 0, clear the list
195		if maxSize == 0 {
196			l.head = nil
197			l.tail = nil
198			l.size = 0
199		} else {
200			// Keep the newest entries by moving head forward
201			diff := l.size - maxSize
202			for i := 0; i < diff; i++ {
203				l.head = l.head.next
204			}
205			l.size = maxSize
206		}
207	}
208
209	l.maxSize = maxSize
210}
211
212// Delete removes the element at the specified index.
213// Returns true if an element was removed, false if the index was invalid.
214func (l *List) Delete(index int) bool {
215	if index < 0 || index >= l.size {
216		return false
217	}
218
219	// Special case: deleting the only element
220	if l.size == 1 {
221		l.head = nil
222		l.tail = nil
223		l.size = 0
224		return true
225	}
226
227	// Special case: deleting first element
228	if index == 0 {
229		l.head = l.head.next
230		l.size--
231		return true
232	}
233
234	// Find the node before the one to delete
235	current := l.head
236	for i := 0; i < index-1; i++ {
237		current = current.next
238	}
239
240	// Special case: deleting last element
241	if index == l.size-1 {
242		l.tail = current
243		current.next = nil
244	} else {
245		current.next = current.next.next
246	}
247
248	l.size--
249	return true
250}