-
Notifications
You must be signed in to change notification settings - Fork 8
/
skipList.go
202 lines (166 loc) · 5.64 KB
/
skipList.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package ConcurrentSkipList
import (
"math/rand"
"sync"
"sync/atomic"
)
type skipList struct {
level int
length int32
head *Node
tail *Node
mutex sync.RWMutex
}
// newSkipList will create a concurrent skip list with given level.
func newSkipList(level int) *skipList {
head := newNode(0, nil, level)
var tail *Node
for i := 0; i < len(head.nextNodes); i++ {
head.nextNodes[i] = tail
}
return &skipList{
level: level,
length: 0,
head: head,
tail: tail,
}
}
// searchWithPreviousNode will search given index in skip list.
// The first return value represents the previous nodes need to update when call Insert function.
// The second return value represents the value with given index or the closet value whose index is larger than given index.
func (s *skipList) searchWithPreviousNodes(index uint64) ([]*Node, *Node) {
// Store all previous value whose index is less than index and whose next value's index is larger than index.
previousNodes := make([]*Node, s.level)
// fmt.Printf("start doSearch:%v\n", index)
currentNode := s.head
// Iterate from top level to bottom level.
for l := s.level - 1; l >= 0; l-- {
// Iterate value util value's index is >= given index.
// The max iterate count is skip list's length. So the worst O(n) is N.
for currentNode.nextNodes[l] != s.tail && currentNode.nextNodes[l].index < index {
currentNode = currentNode.nextNodes[l]
}
// When next value's index is >= given index, add current value whose index < given index.
previousNodes[l] = currentNode
}
// Avoid point to tail which will occur panic in Insert and Delete function.
// When the next value is tail.
// The index is larger than the maximum index in the skip list or skip list's length is 0. Don't point to tail.
// When the next value isn't tail.
// Next value's index must >= given index. Point to it.
if currentNode.nextNodes[0] != s.tail {
currentNode = currentNode.nextNodes[0]
}
// fmt.Printf("previous value:\n")
// for _, n := range previousNodes {
// fmt.Printf("%p\t", n)
// }
// fmt.Println()
// fmt.Printf("end doSearch %v\n", index)
return previousNodes, currentNode
}
// searchWithoutPreviousNodes will return the value whose index is given index.
// If can not find the given index, return nil.
// This function is faster than searchWithPreviousNodes and it used to only searching index.
func (s *skipList) searchWithoutPreviousNodes(index uint64) *Node {
currentNode := s.head
// Read lock and unlock.
s.mutex.RLock()
defer s.mutex.RUnlock()
// Iterate from top level to bottom level.
for l := s.level - 1; l >= 0; l-- {
// Iterate value util value's index is >= given index.
// The max iterate count is skip list's length. So the worst O(n) is N.
for currentNode.nextNodes[l] != s.tail && currentNode.nextNodes[l].index < index {
currentNode = currentNode.nextNodes[l]
}
}
currentNode = currentNode.nextNodes[0]
if currentNode == s.tail || currentNode.index > index {
return nil
} else if currentNode.index == index {
return currentNode
} else {
return nil
}
}
// insert will insert a value into skip list and update the length.
// If skip has these this index, overwrite the value, otherwise add it.
func (s *skipList) insert(index uint64, value interface{}) {
// Write lock and unlock.
s.mutex.Lock()
defer s.mutex.Unlock()
previousNodes, currentNode := s.searchWithPreviousNodes(index)
if currentNode != s.head && currentNode.index == index {
currentNode.value = value
return
}
// Make a new value.
newNode := newNode(index, value, s.randomLevel())
// Adjust pointer. Similar to update linked list.
for i := len(newNode.nextNodes) - 1; i >= 0; i-- {
// Firstly, new value point to next value.
newNode.nextNodes[i] = previousNodes[i].nextNodes[i]
// Secondly, previous nodes point to new value.
previousNodes[i].nextNodes[i] = newNode
// Finally, in order to release the slice, point to nil.
previousNodes[i] = nil
}
atomic.AddInt32(&s.length, 1)
for i := len(newNode.nextNodes); i < len(previousNodes); i++ {
previousNodes[i] = nil
}
}
// delete will find the index is existed or not firstly.
// If existed, delete it and update length, otherwise do nothing.
func (s *skipList) delete(index uint64) {
// Write lock and unlock.
s.mutex.Lock()
defer s.mutex.Unlock()
previousNodes, currentNode := s.searchWithPreviousNodes(index)
// If skip list length is 0 or could not find value with the given index.
if currentNode != s.head && currentNode.index == index {
// Adjust pointer. Similar to update linked list.
for i := 0; i < len(currentNode.nextNodes); i++ {
previousNodes[i].nextNodes[i] = currentNode.nextNodes[i]
currentNode.nextNodes[i] = nil
previousNodes[i] = nil
}
atomic.AddInt32(&s.length, -1)
}
for i := len(currentNode.nextNodes); i < len(previousNodes); i++ {
previousNodes[i] = nil
}
}
// snapshot will create a snapshot of the skip list and return a slice of the nodes.
func (s *skipList) snapshot() []*Node {
s.mutex.RLock()
defer s.mutex.RUnlock()
result := make([]*Node, s.length)
i := 0
currentNode := s.head.nextNodes[0]
for currentNode != s.tail {
node := &Node{
index: currentNode.index,
value: currentNode.value,
nextNodes: nil,
}
result[i] = node
currentNode = currentNode.nextNodes[0]
i++
}
return result
}
// getLength will return the length of skip list.
func (s *skipList) getLength() int32 {
return atomic.LoadInt32(&s.length)
}
// randomLevel will generate and random level that level > 0 and level < skip list's level
// This comes from redis's implementation.
func (s *skipList) randomLevel() int {
level := 1
for rand.Float64() < PROBABILITY && level < s.level {
level++
}
return level
}