diff options
Diffstat (limited to 'vendor/codeberg.org/gruf/go-structr/queue.go')
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/queue.go | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-structr/queue.go b/vendor/codeberg.org/gruf/go-structr/queue.go new file mode 100644 index 000000000..1e735762f --- /dev/null +++ b/vendor/codeberg.org/gruf/go-structr/queue.go @@ -0,0 +1,306 @@ +package structr + +import ( + "reflect" + "sync" + "unsafe" +) + +// QueueConfig defines config vars +// for initializing a struct queue. +type QueueConfig[StructType any] struct { + + // Indices defines indices to create + // in the Queue for the receiving + // generic struct parameter type. + Indices []IndexConfig + + // Pop is called when queue values + // are popped, during calls to any + // of the Pop___() series of fns. + Pop func(StructType) +} + +// Queue provides a structure model queue with +// automated indexing and popping by any init +// defined lookups of field combinations. +type Queue[StructType any] struct { + + // indices used in storing passed struct + // types by user defined sets of fields. + indices []Index + + // main underlying + // struct item queue. + queue list + + // hook functions. + copy func(StructType) StructType + pop func(StructType) + + // protective mutex, guards: + // - Queue{}.queue + // - Index{}.data + // - Queue{} hook fns + mutex sync.Mutex +} + +// Init initializes the queue with given configuration +// including struct fields to index, and necessary fns. +func (q *Queue[T]) Init(config QueueConfig[T]) { + t := reflect.TypeOf((*T)(nil)).Elem() + + if len(config.Indices) == 0 { + panic("no indices provided") + } + + // Safely copy over + // provided config. + q.mutex.Lock() + q.indices = make([]Index, len(config.Indices)) + for i, cfg := range config.Indices { + q.indices[i].ptr = unsafe.Pointer(q) + q.indices[i].init(t, cfg, 0) + } + q.pop = config.Pop + q.mutex.Unlock() +} + +// Index selects index with given name from queue, else panics. +func (q *Queue[T]) Index(name string) *Index { + for i := range q.indices { + if q.indices[i].name == name { + return &q.indices[i] + } + } + panic("unknown index: " + name) +} + +// PopFront pops the current value at front of the queue. +func (q *Queue[T]) PopFront() (T, bool) { + t := q.PopFrontN(1) + if len(t) == 0 { + var t T + return t, false + } + return t[0], true +} + +// PopBack pops the current value at back of the queue. +func (q *Queue[T]) PopBack() (T, bool) { + t := q.PopBackN(1) + if len(t) == 0 { + var t T + return t, false + } + return t[0], true +} + +// PopFrontN attempts to pop n values from front of the queue. +func (q *Queue[T]) PopFrontN(n int) []T { + return q.pop_n(n, func() *list_elem { + return q.queue.head + }) +} + +// PopBackN attempts to pop n values from back of the queue. +func (q *Queue[T]) PopBackN(n int) []T { + return q.pop_n(n, func() *list_elem { + return q.queue.tail + }) +} + +// Pop attempts to pop values from queue indexed under any of keys. +func (q *Queue[T]) Pop(index *Index, keys ...Key) []T { + if index == nil { + panic("no index given") + } else if index.ptr != unsafe.Pointer(q) { + panic("invalid index for queue") + } + + // Acquire lock. + q.mutex.Lock() + + // Preallocate expected ret slice. + values := make([]T, 0, len(keys)) + + for i := range keys { + // Delete all items under key from index, collecting + // value items and dropping them from all their indices. + index.delete(keys[i], func(item *indexed_item) { + + // Append deleted to values. + value := item.data.(T) + values = append(values, value) + + // Delete queued. + q.delete(item) + }) + } + + // Get func ptrs. + pop := q.pop + + // Done with lock. + q.mutex.Unlock() + + if pop != nil { + // Pass all popped values + // to given user hook (if set). + for _, value := range values { + pop(value) + } + } + + return values +} + +// PushFront pushes values to front of queue. +func (q *Queue[T]) PushFront(values ...T) { + q.mutex.Lock() + for i := range values { + item := q.index(values[i]) + q.queue.push_front(&item.elem) + } + q.mutex.Unlock() +} + +// PushBack pushes values to back of queue. +func (q *Queue[T]) PushBack(values ...T) { + q.mutex.Lock() + for i := range values { + item := q.index(values[i]) + q.queue.push_back(&item.elem) + } + q.mutex.Unlock() +} + +// MoveFront attempts to move values indexed under any of keys to the front of the queue. +func (q *Queue[T]) MoveFront(index *Index, keys ...Key) { + q.mutex.Lock() + for i := range keys { + index.get(keys[i], func(item *indexed_item) { + q.queue.move_front(&item.elem) + }) + } + q.mutex.Unlock() +} + +// MoveBack attempts to move values indexed under any of keys to the back of the queue. +func (q *Queue[T]) MoveBack(index *Index, keys ...Key) { + q.mutex.Lock() + for i := range keys { + index.get(keys[i], func(item *indexed_item) { + q.queue.move_back(&item.elem) + }) + } + q.mutex.Unlock() +} + +// Len returns the current length of queue. +func (q *Queue[T]) Len() int { + q.mutex.Lock() + l := q.queue.len + q.mutex.Unlock() + return l +} + +func (q *Queue[T]) pop_n(n int, next func() *list_elem) []T { + if next == nil { + panic("nil fn") + } + + // Acquire lock. + q.mutex.Lock() + + // Preallocate ret slice. + values := make([]T, 0, n) + + // Iterate over 'n' items. + for i := 0; i < n; i++ { + + // Get next elem. + next := next() + if next == nil { + + // reached + // end. + break + } + + // Cast the indexed item from elem. + item := (*indexed_item)(next.data) + + // Append deleted to values. + value := item.data.(T) + values = append(values, value) + + // Delete queued. + q.delete(item) + } + + // Get func ptrs. + pop := q.pop + + // Done with lock. + q.mutex.Unlock() + + if pop != nil { + // Pass all popped values + // to given user hook (if set). + for _, value := range values { + pop(value) + } + } + + return values +} + +func (q *Queue[T]) index(value T) *indexed_item { + item := new_indexed_item() + + // Set item value. + item.data = value + + // Acquire key buf. + buf := new_buffer() + + for i := range q.indices { + // Get current index ptr. + idx := &(q.indices[i]) + + // Extract fields comprising index key. + parts := extract_fields(value, idx.fields) + + // Calculate index key. + key := idx.key(buf, parts) + if key.Zero() { + continue + } + + // Append item to index. + idx.append(key, item) + } + + // Done with buf. + free_buffer(buf) + + return item +} + +func (q *Queue[T]) delete(item *indexed_item) { + for len(item.indexed) != 0 { + // Pop last indexed entry from list. + entry := item.indexed[len(item.indexed)-1] + item.indexed = item.indexed[:len(item.indexed)-1] + + // Drop index_entry from index. + entry.index.delete_entry(entry) + } + + // Drop entry from queue list. + q.queue.remove(&item.elem) + + // Free now-unused item. + free_indexed_item(item) +} |