Skip to content

Commit 941bf46

Browse files
Merge pull request #53 from Workiva/skiplist_compare
Skiplist compare
2 parents 4dab05b + c368470 commit 941bf46

File tree

12 files changed

+195
-579
lines changed

12 files changed

+195
-579
lines changed

rangetree/skiplist/skiplist.go

Lines changed: 82 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,33 @@ import (
3737
"github.com/Workiva/go-datastructures/slice/skip"
3838
)
3939

40+
// keyed is required as in the rangetree code we often want to compare
41+
// two different types of bundles and this allows us to do so without
42+
// checking for each one.
43+
type keyed interface {
44+
key() uint64
45+
}
46+
47+
type skipEntry uint64
48+
49+
// Compare is required by the skip.Entry interface.
50+
func (se skipEntry) Compare(other skip.Entry) int {
51+
otherSe := other.(skipEntry)
52+
if se == otherSe {
53+
return 0
54+
}
55+
56+
if se > otherSe {
57+
return 1
58+
}
59+
60+
return -1
61+
}
62+
63+
func (se skipEntry) key() uint64 {
64+
return uint64(se)
65+
}
66+
4067
// isLastDimension simply returns dimension == lastDimension-1.
4168
// This panics if dimension >= lastDimension.
4269
func isLastDimension(dimension, lastDimension uint64) bool {
@@ -63,25 +90,55 @@ func needsDeletion(value, index, number int64) bool {
6390
// dimensionalBundle is an intermediate holder up to the last
6491
// dimension and represents a wrapper around a skiplist.
6592
type dimensionalBundle struct {
66-
key uint64
67-
sl *skip.SkipList
93+
id uint64
94+
sl *skip.SkipList
6895
}
6996

70-
// Key returns the key for this bundle. Fulfills skip.Entry interface.
71-
func (db *dimensionalBundle) Key() uint64 {
72-
return db.key
97+
// Compare returns a value indicating the relative relationship and the
98+
// provided bundle.
99+
func (db *dimensionalBundle) Compare(e skip.Entry) int {
100+
keyed := e.(keyed)
101+
if db.id == keyed.key() {
102+
return 0
103+
}
104+
105+
if db.id > keyed.key() {
106+
return 1
107+
}
108+
109+
return -1
110+
}
111+
112+
// key returns the key for this bundle.
113+
func (db *dimensionalBundle) key() uint64 {
114+
return db.id
73115
}
74116

75117
// lastBundle represents a bundle living at the last dimension
76118
// of the tree.
77119
type lastBundle struct {
78-
key uint64
120+
id uint64
79121
entry rangetree.Entry
80122
}
81123

82-
// Key returns the key for this bundle. Fulfills skip.Entry interface.
83-
func (lb *lastBundle) Key() uint64 {
84-
return lb.key
124+
// Compare returns a value indicating the relative relationship and the
125+
// provided bundle.
126+
func (lb *lastBundle) Compare(e skip.Entry) int {
127+
keyed := e.(keyed)
128+
if lb.id == keyed.key() {
129+
return 0
130+
}
131+
132+
if lb.id > keyed.key() {
133+
return 1
134+
}
135+
136+
return -1
137+
}
138+
139+
// Key returns the key for this bundle.
140+
func (lb *lastBundle) key() uint64 {
141+
return lb.id
85142
}
86143

87144
type skipListRT struct {
@@ -105,7 +162,7 @@ func (rt *skipListRT) add(entry rangetree.Entry) rangetree.Entry {
105162

106163
for i := uint64(0); i < rt.dimensions; i++ {
107164
value = entry.ValueAtDimension(i)
108-
e = sl.Get(uint64(value))[0]
165+
e = sl.Get(skipEntry(value))[0]
109166
if isLastDimension(i, rt.dimensions) {
110167
if e != nil { // this is an overwrite
111168
lb = e.(*lastBundle)
@@ -115,14 +172,14 @@ func (rt *skipListRT) add(entry rangetree.Entry) rangetree.Entry {
115172
}
116173

117174
// need to add new sl entry
118-
lb = &lastBundle{key: uint64(value), entry: entry}
175+
lb = &lastBundle{id: uint64(value), entry: entry}
119176
rt.number++
120177
sl.Insert(lb)
121178
return nil
122179
}
123180

124181
if e == nil { // we need the intermediate dimension
125-
db = &dimensionalBundle{key: uint64(value), sl: skip.New(uint64(0))}
182+
db = &dimensionalBundle{id: uint64(value), sl: skip.New(uint64(0))}
126183
sl.Insert(db)
127184
} else {
128185
db = e.(*dimensionalBundle)
@@ -155,7 +212,7 @@ func (rt *skipListRT) get(entry rangetree.Entry) rangetree.Entry {
155212
)
156213
for i := uint64(0); i < rt.dimensions; i++ {
157214
value = uint64(entry.ValueAtDimension(i))
158-
e = sl.Get(value)[0]
215+
e = sl.Get(skipEntry(value))[0]
159216
if e == nil {
160217
return nil
161218
}
@@ -195,7 +252,7 @@ func (rt *skipListRT) deleteRecursive(sl *skip.SkipList, dimension uint64,
195252

196253
value := entry.ValueAtDimension(dimension)
197254
if isLastDimension(dimension, rt.dimensions) {
198-
entries := sl.Delete(uint64(value))
255+
entries := sl.Delete(skipEntry(value))
199256
if entries[0] == nil {
200257
return nil
201258
}
@@ -204,7 +261,7 @@ func (rt *skipListRT) deleteRecursive(sl *skip.SkipList, dimension uint64,
204261
return entries[0].(*lastBundle).entry
205262
}
206263

207-
db, ok := sl.Get(uint64(value))[0].(*dimensionalBundle)
264+
db, ok := sl.Get(skipEntry(value))[0].(*dimensionalBundle)
208265
if !ok { // value was not found
209266
return nil
210267
}
@@ -215,7 +272,7 @@ func (rt *skipListRT) deleteRecursive(sl *skip.SkipList, dimension uint64,
215272
}
216273

217274
if db.sl.Len() == 0 {
218-
sl.Delete(db.key)
275+
sl.Delete(db)
219276
}
220277

221278
return result
@@ -239,9 +296,9 @@ func (rt *skipListRT) apply(sl *skip.SkipList, dimension uint64,
239296

240297
var e skip.Entry
241298

242-
for iter := sl.Iter(uint64(lowValue)); iter.Next(); {
299+
for iter := sl.Iter(skipEntry(lowValue)); iter.Next(); {
243300
e = iter.Value()
244-
if int64(e.Key()) >= highValue {
301+
if int64(e.(keyed).key()) >= highValue {
245302
break
246303
}
247304

@@ -282,7 +339,7 @@ func (rt *skipListRT) Query(interval rangetree.Interval) rangetree.Entries {
282339

283340
func (rt *skipListRT) flatten(sl *skip.SkipList, dimension uint64, entries *rangetree.Entries) {
284341
lastDimension := isLastDimension(dimension, rt.dimensions)
285-
for iter := sl.Iter(0); iter.Next(); {
342+
for iter := sl.Iter(skipEntry(0)); iter.Next(); {
286343
if lastDimension {
287344
*entries = append(*entries, iter.Value().(*lastBundle).entry)
288345
} else {
@@ -299,9 +356,9 @@ func (rt *skipListRT) insert(sl *skip.SkipList, dimension, insertDimension uint6
299356
affectedDimension := dimension == insertDimension
300357
var iter skip.Iterator
301358
if dimension == insertDimension {
302-
iter = sl.Iter(uint64(index))
359+
iter = sl.Iter(skipEntry(index))
303360
} else {
304-
iter = sl.Iter(0)
361+
iter = sl.Iter(skipEntry(0))
305362
}
306363

307364
var toDelete skip.Entries
@@ -317,32 +374,30 @@ func (rt *skipListRT) insert(sl *skip.SkipList, dimension, insertDimension uint6
317374
)
318375
continue
319376
}
320-
if needsDeletion(int64(e.Key()), index, number) {
377+
if needsDeletion(int64(e.(keyed).key()), index, number) {
321378
toDelete = append(toDelete, e)
322379
continue
323380
}
324381

325382
if lastDimension {
326-
e.(*lastBundle).key += uint64(number)
383+
e.(*lastBundle).id += uint64(number)
327384
*affected = append(*affected, e.(*lastBundle).entry)
328385
} else {
329-
e.(*dimensionalBundle).key += uint64(number)
386+
e.(*dimensionalBundle).id += uint64(number)
330387
rt.flatten(e.(*dimensionalBundle).sl, dimension+1, affected)
331388
}
332389
}
333390

334391
if len(toDelete) > 0 {
335-
keys := make([]uint64, 0, len(toDelete))
336392
for _, e := range toDelete {
337393
if lastDimension {
338394
*deleted = append(*deleted, e.(*lastBundle).entry)
339395
} else {
340396
rt.flatten(e.(*dimensionalBundle).sl, dimension+1, deleted)
341397
}
342-
keys = append(keys, e.Key())
343398
}
344399

345-
sl.Delete(keys...)
400+
sl.Delete(toDelete...)
346401
}
347402
}
348403

slice/skip/entries.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,21 @@ import "sort"
2222
// where that key would be inserted in this list. This could
2323
// be equal to the length of the list which means no suitable entry
2424
// point was found.
25-
func (entries Entries) search(key uint64) int {
25+
func (entries Entries) search(e Entry) int {
2626
return sort.Search(len(entries), func(i int) bool {
27-
return entries[i].Key() >= key
27+
return entries[i].Compare(e) > -1
2828
})
2929
}
3030

3131
// insert will insert the provided entry into this list.
3232
func (entries *Entries) insert(entry Entry) Entry {
33-
i := entries.search(entry.Key())
33+
i := entries.search(entry)
3434
if i >= len(*entries) {
3535
*entries = append(*entries, entry)
3636
return nil
3737
}
3838

39-
if (*entries)[i].Key() == entry.Key() {
39+
if (*entries)[i].Compare(entry) == 0 {
4040
oldEntry := (*entries)[i]
4141
(*entries)[i] = entry
4242
return oldEntry
@@ -49,13 +49,13 @@ func (entries *Entries) insert(entry Entry) Entry {
4949
}
5050

5151
// delete will delete the provided key from this list.
52-
func (entries *Entries) delete(key uint64) Entry {
53-
i := entries.search(key)
52+
func (entries *Entries) delete(e Entry) Entry {
53+
i := entries.search(e)
5454
if i >= len(*entries) {
5555
return nil
5656
}
5757

58-
if (*entries)[i].Key() != key {
58+
if (*entries)[i].Compare(e) != 0 {
5959
return nil
6060
}
6161

@@ -68,13 +68,13 @@ func (entries *Entries) delete(key uint64) Entry {
6868

6969
// get will return the entry associated with the provided key.
7070
// If no such Entry exists, this returns nil.
71-
func (entries Entries) get(key uint64) Entry {
72-
i := entries.search(key)
71+
func (entries Entries) get(e Entry) Entry {
72+
i := entries.search(e)
7373
if i >= len(entries) {
7474
return nil
7575
}
7676

77-
if entries[i].Key() == key {
77+
if entries[i].Compare(e) == 0 {
7878
return entries[i]
7979
}
8080

slice/skip/entries_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ func TestEntriesDelete(t *testing.T) {
5252

5353
entries := Entries{e1, e2}
5454

55-
result := entries.delete(10)
55+
result := entries.delete(e2)
5656
assert.Equal(t, Entries{e1}, entries)
5757
assert.Equal(t, e2, result)
5858

59-
result = entries.delete(5)
59+
result = entries.delete(e1)
6060
assert.Equal(t, Entries{}, entries)
6161
assert.Equal(t, e1, result)
6262
}

slice/skip/interface.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ package skip
1919
// Entry defines items that can be inserted into the skip list.
2020
// This will also be the type returned from a query.
2121
type Entry interface {
22-
// Key defines this entry's place in the skip list.
23-
Key() uint64
22+
// Compare this entry to the provided entry. Return a positive
23+
// number if this entry is greater than, 0 if equal, negative
24+
// number if less than.
25+
Compare(Entry) int
2426
}
2527

2628
// Entries is a typed list of interface Entry.

slice/skip/iterator.go

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -68,61 +68,3 @@ func (iter *iterator) exhaust() Entries {
6868
func nilIterator() *iterator {
6969
return &iterator{}
7070
}
71-
72-
// starIterator is used as an iterator by the SkipList*.
73-
type starIterator struct {
74-
entries Entries
75-
iter Iterator
76-
index int
77-
}
78-
79-
// isExhausted returns a bool indicating if all values have been
80-
// iterated.
81-
func (si *starIterator) isExhausted() bool {
82-
return si.index == iteratorExhausted
83-
}
84-
85-
// Next returns a bool indicating if there are any further values
86-
// in this iterator.
87-
func (si *starIterator) Next() bool {
88-
if si.isExhausted() {
89-
return false
90-
}
91-
92-
si.index++
93-
if si.index >= len(si.entries) {
94-
canNext := si.iter.Next()
95-
if !canNext {
96-
si.index = iteratorExhausted
97-
return false
98-
}
99-
100-
si.entries = si.iter.Value().(*entryBundle).entries
101-
si.index = 0
102-
}
103-
104-
return true
105-
}
106-
107-
// Value returns an Entry representing the iterator's present
108-
// position in the query. Returns nil if no values remain to iterate.
109-
func (si *starIterator) Value() Entry {
110-
if si.isExhausted() {
111-
return nil
112-
}
113-
114-
if si.entries == nil || si.index < 0 || si.index >= len(si.entries) {
115-
return nil
116-
}
117-
118-
return si.entries[si.index]
119-
}
120-
121-
func (si *starIterator) exhaust() Entries {
122-
entries := make(Entries, 0, 20)
123-
for i := si; i.Next(); {
124-
entries = append(entries, i.Value())
125-
}
126-
127-
return entries
128-
}

0 commit comments

Comments
 (0)