Files
iroh-tracker/stack/stack.go
T

100 lines
3.3 KiB
Go

package stack
type Stack struct {
items []Keyer
}
// things that are to be in the stack have to implement Key() string
type Keyer interface {
Key() string
}
func NewStack(size int) *Stack {
s := Stack{items: make([]Keyer, 0, size)}
return &s
}
// TODO (2026-10-16): look up how to make a proper enumerable / iterable array in golang
// it is apparently relatively recent (2024): https://go.dev/blog/range-functions
func (s* Stack) Items() []Keyer {
return s.items
}
func (s* Stack) Size() int {
return len(s.items)
}
type emptyKeyer struct{}
func (e *emptyKeyer) Key() string { return "" }
func IsEmpty(k Keyer) bool {
return k.Key() == ""
}
// NOTE (2026-10-30): i actually don't have a concrete-enough use case for PushAndReplace, it is more a nice to have. will pause it for now
// func PushAndReplace(s *stack, newItem *Keyer) {
// itemOnStack, _, _ := s.Push(newItem)
// TODO (2026-10-30): how to correctly make it so that pointer "itemOnStack" reflects the values inherent in newItem)
// basically want newItem to be "force pushed" onto stack instead of maintaining the previous item that matched
// newItem.Key()
// }
// attempt to push newItem on stack. maintains the pointer of the first instance of item being pushed on stack
//
// if newItem WAS on the stack
// * bumps value that was already on stack to top of stack
// * return the value that was already in the stack and false (no eviction)
//
// if newItem WAS NOT on the stack:
// * put newItem at top of stack, and..
// * returns newItem and false (no eviction) if stack was below capacity
// * returns evicted item and true (eviction) if stack was above capacity
// NOTE (2026-10-29): bit complex tho init?
const ITEM_EVICTED = true
// NOTE (2026-10-30): could pursue a signature like the following instead
// Push(newItem keyer) (itemOnStack, itemEvicted, booleanEvictedItem)
//
// item, evictedItem, evicted := exampleStack.Push(anItem)
// item := exampleStack.Push(anItem)
// _, evictedItem, evicted := exampleStack.Push(anItem)
func (s *Stack) Push (newItem Keyer) (Keyer, bool, Keyer) {
// stack below capacity, and newItem was at front of stack - exit early
if len(s.items) > 0 && s.items[0].Key() == newItem.Key() {
return s.items[0], !ITEM_EVICTED, &emptyKeyer{}
}
prev := newItem
for i, elem := range s.items {
// insert newItem at front
if i == 0 {
prev = s.items[0]
s.items[0] = newItem
// newItem was already in stack (therefore we will not evict any items!)
// do in-place swap and exit early
} else if elem.Key() == newItem.Key() {
s.items[i] = prev
// ignore value of newItem and re-use what was already in stack instead
s.items[0] = elem
// return the previously stored value / pointer
return elem, !ITEM_EVICTED, &emptyKeyer{}
// propagate change outwards in stack, moving all items one step further
} else {
temp := s.items[i]
s.items[i] = prev
prev = temp
}
}
/* newItem wasn't in stack */
// below we might need to evict an item already on the stack, depending on the current capacity
// if we still have capacity, append the last item back onto the end of the stack
if len(s.items) < cap(s.items) {
s.items = append(s.items, prev)
} else {
// we evict the list item, forgetting about it and return the evicted item
return newItem, ITEM_EVICTED, prev
}
return newItem, !ITEM_EVICTED, &emptyKeyer{}
}