From 49ac0168218fa2ce9a4e2d57912242c878e20367 Mon Sep 17 00:00:00 2001 From: "Matt T. Proud" Date: Fri, 11 Jan 2013 22:40:25 +0100 Subject: [PATCH] Provide reverse iteration and arbitrary seeking. The ``SkipList`` protocol has been updated to reflect two new features: ``SkipList.Seek(key interface{}) iterator`` This enables seeking to arbitrary positions in the list, similar to the behavior found in ``SkipList.Get`` in _O(log n)_ time. ``Iterator.Prev() bool`` Skip List ``node`` elements are now doubly-linked to enable backtracking to previous elements. --- AUTHORS | 5 +- CONTRIBUTORS | 3 +- README.markdown | 80 ++++++++++++------ skiplist/skiplist.go | 162 ++++++++++++++++++++++++++++------- skiplist/skiplist_test.go | 173 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 360 insertions(+), 63 deletions(-) diff --git a/AUTHORS b/AUTHORS index c01fbc7..0f1991b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# This is the official list of GoMock authors for copyright purposes. +# This is the official list of goskiplist authors for copyright purposes. # This file is distinct from the CONTRIBUTORS files. # See the latter for an explanation. @@ -8,4 +8,5 @@ # Please keep the list sorted. -Google Inc. \ No newline at end of file +Google Inc. +SoundCloud, Ltd. diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0578379..46e2243 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -31,4 +31,5 @@ # Please keep the list sorted. -Ric Szopa (Ryszard) \ No newline at end of file +Matt T. Proud (mtp) +Ric Szopa (Ryszard) diff --git a/README.markdown b/README.markdown index aa5d045..69f7a1e 100644 --- a/README.markdown +++ b/README.markdown @@ -19,7 +19,7 @@ Installing ========== $ go get github.com/ryszard/goskiplist/skiplist - + Example ======= @@ -27,8 +27,8 @@ Example package main import ( - "github.com/ryszard/goskiplist/skiplist" "fmt" + "github.com/ryszard/goskiplist/skiplist" ) func main() { @@ -41,52 +41,80 @@ func main() { s.Set(10, "ten") s.Set(3, "three") - value, ok := s.Get(0) + firstValue, ok := s.Get(0) if ok { - fmt.Println(value) + fmt.Println(firstValue) } - // prints: - // zero - + // prints: + // zero s.Delete(7) - value, ok := s.Get(7) + secondValue, ok := s.Get(7) if ok { - fmt.Println(value) + fmt.Println(secondValue) } // prints: nothing. s.Set(9, "niner") // Iterate through all the elements, in order. - for i := s.Iterator(); i.Next(); { - fmt.Printf("%d: %s\n", i.Key(), i.Value()) + unboundIterator := s.Iterator() + for unboundIterator.Next() { + fmt.Printf("%d: %s\n", unboundIterator.Key(), unboundIterator.Value()) + } + // prints: + // 0: zero + // 1: one + // 3: three + // 5: five + // 9: niner + // 10: ten + + for unboundIterator.Previous() { + fmt.Printf("%d: %s\n", unboundIterator.Key(), unboundIterator.Value()) } - // prints: - // 0: zero - // 1: one - // 3: three - // 5: five - // 9: niner - // 10: ten + // 9: niner + // 5: five + // 3: three + // 1: one + // 0: zero + boundIterator := s.Range(3, 10) // Iterate only through elements in some range. - for i := s.Range(3, 10); i.Next(); { - fmt.Printf("%d: %s\n", i.Key(), i.Value()) + for boundIterator.Next() { + fmt.Printf("%d: %s\n", boundIterator.Key(), boundIterator.Value()) } - // prints: - // 3: three - // 5: five - // 9: niner + // prints: + // 3: three + // 5: five + // 9: niner + + for boundIterator.Previous() { + fmt.Printf("%d: %s\n", boundIterator.Key(), boundIterator.Value()) + } + // prints: + // 5: five + // 3: three + + var iterator skiplist.Iterator + + iterator = s.Seek(3) + fmt.Printf("%d: %s\n", iterator.Key(), iterator.Value()) + // prints: + // 3: three + iterator = s.Seek(2) + fmt.Printf("%d: %s\n", iterator.Key(), iterator.Value()) + // prints: + // 3: three } ``` Full documentation ================== -Read it [online](http://go.pkgdoc.org/github.com/ryszard/goskiplist/skiplist) or run +Read it [online](http://go.pkgdoc.org/github.com/ryszard/goskiplist/skiplist) or run $ go doc github.com/ryszard/goskiplist/skiplist @@ -98,5 +126,5 @@ This list is probably incomplete. * https://github.com/huandu/skiplist * https://bitbucket.org/taruti/go-skip/src - * http://code.google.com/p/leveldb-go/source/browse/leveldb/memdb/memdb.go + * http://code.google.com/p/leveldb-go/source/browse/leveldb/memdb/memdb.go (part of [leveldb-go](http://code.google.com/p/leveldb-go/)) diff --git a/skiplist/skiplist.go b/skiplist/skiplist.go index 8fc97f1..764e80c 100644 --- a/skiplist/skiplist.go +++ b/skiplist/skiplist.go @@ -14,7 +14,9 @@ // trees". Communications of the ACM 33 (6): 668–676 package skiplist -import "math/rand" +import ( + "math/rand" +) // TODO(ryszard): // - A separately seeded source of randomness @@ -31,6 +33,7 @@ const DefaultMaxLevel = 32 // list. type node struct { forward []*node + backward *node key, value interface{} } @@ -42,11 +45,21 @@ func (n *node) next() *node { return n.forward[0] } +// previous returns the previous node in the skip list containing n. +func (n *node) previous() *node { + return n.backward +} + // hasNext returns true if n has a next node. func (n *node) hasNext() bool { return n.next() != nil } +// hasPrevious returns true if n has a previous node. +func (n *node) hasPrevious() bool { + return n.previous() != nil +} + // A SkipList is a map-like data structure that maintains an ordered // collection of key-value pairs. Insertion, lookup, and deletion are // all O(log n) operations. A SkipList can efficiently store up to @@ -88,10 +101,12 @@ func (s *SkipList) Len() int { // // Key and Value return the key and the value of the current node. type Iterator interface { - // Next returns true if the iterator contains more elements - // and andvances its state to the next element if that is - // possible. + // Next returns true if the iterator contains subsequent elements + // and advances its state to the next element if that is possible. Next() bool + // Previous returns true if the iterator contains previous elements + // and rewinds its state to the previous element if that is possible. + Previous() bool // Key returns the current key. Key() interface{} // Value returns the current value. @@ -99,8 +114,9 @@ type Iterator interface { } type iter struct { - key, value interface{} - current *node + key interface{} + value interface{} + current *node } func (i iter) Key() interface{} { @@ -112,38 +128,90 @@ func (i iter) Value() interface{} { } func (i *iter) Next() bool { - if i.current.hasNext() { - i.current = i.current.next() - i.key = i.current.key - i.value = i.current.value - return true + if !i.current.hasNext() { + return false } - return false + + i.current = i.current.next() + i.key = i.current.key + i.value = i.current.value + + return true +} + +func (i *iter) Previous() bool { + if !i.current.hasPrevious() { + return false + } + + i.current = i.current.previous() + i.key = i.current.key + i.value = i.current.value + + return true } type rangeIterator struct { iter - limit interface{} - skipList *SkipList + upperLimit interface{} + lowerLimit interface{} + skipList *SkipList } func (i *rangeIterator) Next() bool { - if i.current.hasNext() { - next := i.current.next() - if !i.skipList.lessThan(next.key, i.limit) { - return false - } - i.current = i.current.next() - i.key = i.current.key - i.value = i.current.value - return true + if !i.current.hasNext() { + return false + } + + next := i.current.next() + + if !i.skipList.lessThan(next.key, i.upperLimit) { + return false + } + + i.current = i.current.next() + i.key = i.current.key + i.value = i.current.value + return true +} + +func (i *rangeIterator) Previous() bool { + if !i.current.hasPrevious() { + return false + } + + previous := i.current.previous() + + if i.skipList.lessThan(previous.key, i.lowerLimit) { + return false } - return false + + i.current = i.current.previous() + i.key = i.current.key + i.value = i.current.value + return true } // Iterator returns an Iterator that will go through all elements s. func (s *SkipList) Iterator() Iterator { - return &iter{current: s.header} + return &iter{ + current: s.header, + } +} + +// Seek returns a bidirectional iterator starting with the first element whose +// key is greater or equal to key; otherwise, a nil iterator is returned. +func (s *SkipList) Seek(key interface{}) Iterator { + current := s.getPath(nil, key) + if current == nil { + return nil + } + + return &iter{ + current: current, + key: current.key, + value: current.value, + } } // Range returns an iterator that will go through all the @@ -151,7 +219,17 @@ func (s *SkipList) Iterator() Iterator { // less than to. func (s *SkipList) Range(from, to interface{}) Iterator { start := s.getPath(nil, from) - return &rangeIterator{iter: iter{current: &node{forward: []*node{start}}}, limit: to, skipList: s} + return &rangeIterator{ + iter: iter{ + current: &node{ + forward: []*node{start}, + backward: start, + }, + }, + upperLimit: to, + lowerLimit: from, + skipList: s, + } } func (s *SkipList) level() int { @@ -245,13 +323,28 @@ func (s *SkipList) Set(key, value interface{}) { } } - newNode := &node{make([]*node, newLevel+1, s.effectiveMaxLevel()+1), key, value} + newNode := &node{ + forward: make([]*node, newLevel+1, s.effectiveMaxLevel()+1), + key: key, + value: value, + } + + if previous := update[0]; previous.key != nil { + newNode.backward = previous + } for i := 0; i <= newLevel; i++ { newNode.forward[i] = update[i].forward[i] update[i].forward[i] = newNode } + s.length++ + + if newNode.forward[0] != nil { + if newNode.forward[0].backward != newNode { + newNode.forward[0].backward = newNode + } + } } // Delete removes the node with the given key. @@ -265,6 +358,12 @@ func (s *SkipList) Delete(key interface{}) (value interface{}, ok bool) { return nil, false } + previous := candidate.backward + next := candidate.next() + if next != nil { + next.backward = previous + } + for i := 0; i <= s.level() && update[i].forward[i] == candidate; i++ { update[i].forward[i] = candidate.forward[i] } @@ -273,6 +372,7 @@ func (s *SkipList) Delete(key interface{}) (value interface{}, ok bool) { s.header.forward = s.header.forward[:s.level()] } s.length-- + return candidate.value, true } @@ -282,7 +382,9 @@ func (s *SkipList) Delete(key interface{}) (value interface{}, ok bool) { func NewCustomMap(lessThan func(l, r interface{}) bool) *SkipList { return &SkipList{ lessThan: lessThan, - header: &node{forward: []*node{nil}}, + header: &node{ + forward: []*node{nil}, + }, MaxLevel: DefaultMaxLevel, } } @@ -349,7 +451,9 @@ func NewSet() *Set { func NewCustomSet(lessThan func(l, r interface{}) bool) *Set { return &Set{skiplist: SkipList{ lessThan: lessThan, - header: &node{forward: []*node{nil}}, + header: &node{ + forward: []*node{nil}, + }, MaxLevel: DefaultMaxLevel, }} } diff --git a/skiplist/skiplist_test.go b/skiplist/skiplist_test.go index 68237f8..65dfd29 100644 --- a/skiplist/skiplist_test.go +++ b/skiplist/skiplist_test.go @@ -53,7 +53,7 @@ func TestInitialization(t *testing.T) { } } -func TestNodeNext(t *testing.T) { +func TestEmptyNodeNext(t *testing.T) { n := new(node) if next := n.next(); next != nil { t.Errorf("Next() should be nil for an empty node.") @@ -64,7 +64,18 @@ func TestNodeNext(t *testing.T) { } } -func TestHasNext(t *testing.T) { +func TestEmptyNodePrev(t *testing.T) { + n := new(node) + if previous := n.previous(); previous != nil { + t.Errorf("Previous() should be nil for an empty node.") + } + + if n.hasPrevious() { + t.Errorf("hasPrevious() should be false for an empty node.") + } +} + +func TestNodeHasNext(t *testing.T) { s := NewIntMap() s.Set(0, 0) node := s.header.next() @@ -77,6 +88,15 @@ func TestHasNext(t *testing.T) { } } +func TestNodeHasPrev(t *testing.T) { + s := NewIntMap() + s.Set(0, 0) + node := s.header.previous() + if node != nil { + t.Fatalf("Expected no previous entry, got %v.", node) + } +} + func (s *SkipList) check(t *testing.T, key, wanted int) { if got, _ := s.Get(key); got != wanted { t.Errorf("For key %v wanted value %v, got %v.", key, wanted, got) @@ -94,7 +114,6 @@ func TestGet(t *testing.T) { if value, present := s.Get(100); value != nil || present { t.Errorf("%v, %v instead of %v, %v", value, present, nil, false) } - } func TestGetGreaterOrEqual(t *testing.T) { @@ -211,7 +230,9 @@ func TestIteration(t *testing.T) { seen := 0 var lastKey int - for i := s.Iterator(); i.Next(); { + i := s.Iterator() + + for i.Next() { seen++ lastKey = i.Key().(int) if i.Key() != i.Value() { @@ -222,6 +243,22 @@ func TestIteration(t *testing.T) { if seen != s.Len() { t.Errorf("Not all the items in s where iterated through (seen %d, should have seen %d). Last one seen was %d.", seen, s.Len(), lastKey) } + + for i.Previous() { + if i.Key() != i.Value() { + t.Errorf("Wrong value for key %v: %v.", i.Key(), i.Value()) + } + + if i.Key().(int) >= lastKey { + t.Errorf("Expected key to descend but ascended from %v to %v.", lastKey, i.Key()) + } + + lastKey = i.Key().(int) + } + + if lastKey != 0 { + t.Errorf("Expected to count back to zero, but stopped at key %v.", lastKey) + } } func TestRangeIteration(t *testing.T) { @@ -246,6 +283,7 @@ func TestRangeIteration(t *testing.T) { t.Errorf("Wrong value for key %v: %v.", i.Key(), i.Value()) } } + if seen != 5 { t.Errorf("The number of items yielded is incorrect (should be 5, was %v)", seen) } @@ -257,6 +295,34 @@ func TestRangeIteration(t *testing.T) { t.Errorf("The largest element should have been 9, not %v", max) } + seen = 0 + min = 100000 + max = -1 + + for i.Previous() { + seen++ + lastKey = i.Key().(int) + if lastKey > max { + max = lastKey + } + if lastKey < min { + min = lastKey + } + if i.Key() != i.Value() { + t.Errorf("Wrong value for key %v: %v.", i.Key(), i.Value()) + } + } + + if seen != 4 { + t.Errorf("The number of items yielded is incorrect (should be 5, was %v)", seen) + } + if min != 5 { + t.Errorf("The smallest element should have been 5, not %v", min) + } + + if max != 8 { + t.Errorf("The largest element should have been 9, not %v", max) + } } func TestSomeMore(t *testing.T) { @@ -311,12 +377,21 @@ func TestSanity(t *testing.T) { } var last int = 0 - for i := s.Iterator(); i.Next(); { + i := s.Iterator() + + for i.Next() { if last != 0 && i.Key().(int) <= last { t.Errorf("Not in order!") } last = i.Key().(int) } + + for i.Previous() { + if last != 0 && i.Key().(int) > last { + t.Errorf("Not in order!") + } + last = i.Key().(int) + } } type MyOrdered struct { @@ -489,6 +564,94 @@ func TestNewStringSet(t *testing.T) { } } +func TestIteratorPrevHoles(t *testing.T) { + m := NewIntMap() + + i := m.Iterator() + + m.Set(0, 0) + m.Set(1, 1) + m.Set(2, 2) + + if !i.Next() { + t.Errorf("Expected iterator to move successfully to the next.") + } + + if !i.Next() { + t.Errorf("Expected iterator to move successfully to the next.") + } + + if !i.Next() { + t.Errorf("Expected iterator to move successfully to the next.") + } + + if i.Key().(int) != 2 && i.Value().(int) != 2 { + t.Errorf("Expected iterator to reach key 2 and value 2, got %v and %v.", i.Key(), i.Value()) + } + + if !i.Previous() { + t.Errorf("Expected iterator to move successfully to the previous.") + } + + if i.Key().(int) != 1 && i.Value().(int) != 1 { + t.Errorf("Expected iterator to reach key 1 and value 1, got %v and %v.", i.Key(), i.Value()) + } + + if !i.Next() { + t.Errorf("Expected iterator to move successfully to the next.") + } + + m.Delete(1) + + if !i.Previous() { + t.Errorf("Expected iterator to move successfully to the previous.") + } + + if i.Key().(int) != 0 && i.Value().(int) != 0 { + t.Errorf("Expected iterator to reach key 0 and value 0, got %v and %v.", i.Key(), i.Value()) + } +} + +func TestIteratorSeek(t *testing.T) { + m := NewIntMap() + + m.Set(0, 0) + m.Set(1, 1) + m.Set(2, 2) + + i := m.Seek(0) + if i.Key().(int) != 0 && i.Value().(int) != 0 { + t.Errorf("Expected iterator to reach key 0 and value 0, got %v and %v.", i.Key(), i.Value()) + } + + i = m.Seek(2) + if i.Key().(int) != 2 && i.Value().(int) != 2 { + t.Errorf("Expected iterator to reach key 2 and value 2, got %v and %v.", i.Key(), i.Value()) + } + + i = m.Seek(1) + if i.Key().(int) != 1 && i.Value().(int) != 1 { + t.Errorf("Expected iterator to reach key 1 and value 1, got %v and %v.", i.Key(), i.Value()) + } + + i = m.Seek(3) + if i != nil { + t.Errorf("Expected to receive nil iterator, got %v.", i) + } + + m.Set(4, 4) + + i = m.Seek(4) + if i.Key().(int) != 4 && i.Value().(int) != 4 { + t.Errorf("Expected iterator to reach key 4 and value 4, got %v and %v.", i.Key(), i.Value()) + } + + i = m.Seek(3) + if i.Key().(int) != 4 && i.Value().(int) != 4 { + t.Errorf("Expected iterator to reach key 4 and value 4, got %v and %v.", i.Key(), i.Value()) + } +} + func BenchmarkLookup16(b *testing.B) { LookupBenchmark(b, 16) }