Skip to content

Commit 892ae5d

Browse files
Merge pull request #17617 from enj/enj/f/ranked_set
Automatic merge from submit-queue. Implement a ranked set based on key and rank This change adds a ranked set that uses a key (string) to determine uniqueness and a rank (int64) to determine ordering. For example, a structure with a name and time could be ranked based on the time and deduped based on the name. Signed-off-by: Monis Khan <[email protected]> --- /assign @simo5 In regards to using this in the token timeout PR: 1. Implement Key using token.Name and Rank using token.timeout().Unix() 2. Use RankedSet.Insert to add tokens 3. Use RankedSet.LessThan(flushInterval.Unix(), true) to get all tokens that are about to expire (and remove them from the set) 4. Process the tokens as needed
2 parents 713ad46 + 761a483 commit 892ae5d

File tree

2 files changed

+435
-0
lines changed

2 files changed

+435
-0
lines changed

pkg/util/rankedset/rankedset.go

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package rankedset
2+
3+
import "github.com/google/btree"
4+
5+
// Item represents a single object in a RankedSet.
6+
type Item interface {
7+
// Key returns the unique identifier for this item.
8+
Key() string
9+
// Rank is used to sort items.
10+
// Items with the same rank are sorted lexicographically based on Key.
11+
Rank() int64
12+
}
13+
14+
// RankedSet stores Items based on Key (uniqueness) and Rank (sorting).
15+
type RankedSet struct {
16+
rank *btree.BTree
17+
set map[string]*treeItem
18+
}
19+
20+
// StringItem implements Item using a string.
21+
// It has two main uses:
22+
// 1. If all items in a RankedSet are StringItems, the set becomes a store of unique strings sorted lexicographically.
23+
// 2. It serves as a Key item that can be passed into methods that ignore Rank such as RankedSet.Delete.
24+
type StringItem string
25+
26+
func (s StringItem) Key() string {
27+
return string(s)
28+
}
29+
30+
func (s StringItem) Rank() int64 {
31+
return 0
32+
}
33+
34+
func New() *RankedSet {
35+
return &RankedSet{
36+
rank: btree.New(32),
37+
set: make(map[string]*treeItem),
38+
}
39+
}
40+
41+
// Insert adds the item into the set.
42+
// If an item with the same Key existed in the set, it is deleted and returned.
43+
func (s *RankedSet) Insert(item Item) Item {
44+
old := s.Delete(item)
45+
46+
key := item.Key()
47+
value := &treeItem{item: item}
48+
49+
s.rank.ReplaceOrInsert(value) // should always return nil because we call Delete first
50+
s.set[key] = value
51+
52+
return old
53+
}
54+
55+
// Delete removes the item from the set based on Key (Rank is ignored).
56+
// The removed item is returned if it existed in the set.
57+
func (s *RankedSet) Delete(item Item) Item {
58+
key := item.Key()
59+
value, ok := s.set[key]
60+
if !ok {
61+
return nil
62+
}
63+
64+
s.rank.Delete(value) // should always return the same data as value (non-nil)
65+
delete(s.set, key)
66+
67+
return value.item
68+
}
69+
70+
func (s *RankedSet) Min() Item {
71+
if min := s.rank.Min(); min != nil {
72+
return min.(*treeItem).item
73+
}
74+
return nil
75+
}
76+
77+
func (s *RankedSet) Max() Item {
78+
if max := s.rank.Max(); max != nil {
79+
return max.(*treeItem).item
80+
}
81+
return nil
82+
}
83+
84+
func (s *RankedSet) Len() int {
85+
return len(s.set)
86+
}
87+
88+
func (s *RankedSet) Get(item Item) Item {
89+
if value, ok := s.set[item.Key()]; ok {
90+
return value.item
91+
}
92+
return nil
93+
}
94+
95+
func (s *RankedSet) Has(item Item) bool {
96+
_, ok := s.set[item.Key()]
97+
return ok
98+
}
99+
100+
// List returns all items in the set in ranked order.
101+
// If delete is set to true, the returned items are removed from the set.
102+
func (s *RankedSet) List(delete bool) []Item {
103+
return s.ascend(
104+
func(item Item) bool {
105+
return true
106+
},
107+
delete,
108+
)
109+
}
110+
111+
// LessThan returns all items less than the given rank in ranked order.
112+
// If delete is set to true, the returned items are removed from the set.
113+
func (s *RankedSet) LessThan(rank int64, delete bool) []Item {
114+
return s.ascend(
115+
func(item Item) bool {
116+
return item.Rank() < rank
117+
},
118+
delete,
119+
)
120+
}
121+
122+
// setItemIterator allows callers of ascend to iterate in-order over the set.
123+
// When this function returns false, iteration will stop.
124+
type setItemIterator func(item Item) bool
125+
126+
func (s *RankedSet) ascend(iterator setItemIterator, delete bool) []Item {
127+
var items []Item
128+
s.rank.Ascend(func(i btree.Item) bool {
129+
item := i.(*treeItem).item
130+
if !iterator(item) {
131+
return false
132+
}
133+
items = append(items, item)
134+
return true
135+
})
136+
// delete after Ascend since it is probably not safe to remove while iterating
137+
if delete {
138+
for _, item := range items {
139+
s.Delete(item)
140+
}
141+
}
142+
return items
143+
}
144+
145+
var _ btree.Item = &treeItem{}
146+
147+
type treeItem struct {
148+
item Item
149+
}
150+
151+
func (i *treeItem) Less(than btree.Item) bool {
152+
other := than.(*treeItem).item
153+
154+
selfRank := i.item.Rank()
155+
otherRank := other.Rank()
156+
157+
if selfRank == otherRank {
158+
return i.item.Key() < other.Key()
159+
}
160+
161+
return selfRank < otherRank
162+
}

0 commit comments

Comments
 (0)