feed.gno
2.44 Kb · 86 lines
1package nightsky
2
3import (
4 "gno.land/p/nt/avl/v0"
5 "gno.land/p/nt/ufmt/v0"
6)
7
8// CaptureFeed is a bounded, newest-first collection of CaptureResult values
9// backed by an avl.Tree. It is used for both a single telescope's history and
10// the network-wide recent feed.
11//
12// Entries are keyed by a zero-padded, monotonically increasing sequence so the
13// tree iterates in insertion order. Newest-first reads use ReverseIterate, and
14// trimming removes the lowest key (the oldest entry). This avoids the O(n)
15// slice-prepend the previous implementation paid on every insert, and only
16// loads the search path on access instead of the whole collection.
17type CaptureFeed struct {
18 tree *avl.Tree
19 seq int
20 max int
21}
22
23// NewCaptureFeed creates a feed that keeps at most max entries.
24func NewCaptureFeed(max int) *CaptureFeed {
25 if max <= 0 {
26 panic("capture feed size must be positive")
27 }
28 return &CaptureFeed{
29 tree: avl.NewTree(),
30 seq: 0,
31 max: max,
32 }
33}
34
35// feedKey renders a sequence into a fixed-width key so that string ordering
36// matches numeric ordering.
37func feedKey(seq int) string {
38 return ufmt.Sprintf("%020d", seq)
39}
40
41// Add inserts a capture as the newest entry and trims the oldest if the feed is
42// over capacity.
43func (f *CaptureFeed) Add(c CaptureResult) {
44 f.seq++
45 f.tree.Set(feedKey(f.seq), c)
46
47 for f.tree.Size() > f.max {
48 oldest, _ := f.tree.GetByIndex(0)
49 f.tree.Remove(oldest)
50 }
51}
52
53// Len returns the number of stored captures.
54func (f *CaptureFeed) Len() int {
55 return f.tree.Size()
56}
57
58// Latest returns the most recent capture, if any.
59func (f *CaptureFeed) Latest() (CaptureResult, bool) {
60 if f.tree.Size() == 0 {
61 return CaptureResult{}, false
62 }
63 _, value := f.tree.GetByIndex(f.tree.Size() - 1)
64 return value.(CaptureResult), true
65}
66
67// RemoveAt removes the index-th newest capture (0 = most recent). It reports
68// whether a capture was removed. Used for moderation of individual entries.
69func (f *CaptureFeed) RemoveAt(index int) bool {
70 size := f.tree.Size()
71 if index < 0 || index >= size {
72 return false
73 }
74 // Newest-first index i maps to ascending-key index (size-1-i).
75 key, _ := f.tree.GetByIndex(size - 1 - index)
76 _, removed := f.tree.Remove(key)
77 return removed
78}
79
80// IterateNewest calls cb for each capture from newest to oldest. Return true
81// from cb to stop early.
82func (f *CaptureFeed) IterateNewest(cb func(c CaptureResult) bool) {
83 f.tree.ReverseIterate("", "", func(_ string, value any) bool {
84 return cb(value.(CaptureResult))
85 })
86}