mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-05-29 13:26:34 +00:00
172 lines
3.6 KiB
Go
172 lines
3.6 KiB
Go
package skip
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
type (
|
|
node struct {
|
|
next []*node
|
|
key interface{}
|
|
value interface{}
|
|
}
|
|
SkipList struct {
|
|
root *node
|
|
size int
|
|
less func(interface{},interface{})bool
|
|
gen *rand.Rand
|
|
probability float64
|
|
}
|
|
)
|
|
// Create a new skip list
|
|
func New(less func(interface{},interface{})bool) *SkipList {
|
|
gen := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
n := &node{make([]*node, 0),nil,nil}
|
|
return &SkipList{n, 0, less, gen, 0.75}
|
|
}
|
|
func (this *SkipList) Do(f func(interface{}, interface{})bool) {
|
|
if this.size == 0 {
|
|
return
|
|
}
|
|
cur := this.root.next[0]
|
|
for cur != nil {
|
|
if !f(cur.key, cur.value) {
|
|
break
|
|
}
|
|
cur = cur.next[0]
|
|
}
|
|
}
|
|
// Get an item from the skip list
|
|
func (this *SkipList) Get(key interface{}) interface{} {
|
|
if this.size == 0 {
|
|
return nil
|
|
}
|
|
|
|
cur := this.root
|
|
// Start at the top
|
|
for i := len(cur.next)-1; i >= 0; i-- {
|
|
for this.less(cur.next[i].key, key) {
|
|
cur = cur.next[i]
|
|
}
|
|
}
|
|
cur = cur.next[0]
|
|
|
|
if this.equals(cur.key, key) {
|
|
return cur.value
|
|
}
|
|
|
|
return nil
|
|
}
|
|
// Insert a new item into the skip list
|
|
func (this *SkipList) Insert(key interface{}, value interface{}) {
|
|
prev := this.getPrevious(key)
|
|
|
|
// Already in the list so just update the value
|
|
if len(prev) > 0 && prev[0].next[0] != nil && this.equals(prev[0].next[0].key, key) {
|
|
prev[0].next[0].value = value
|
|
return
|
|
}
|
|
|
|
h := len(this.root.next)
|
|
nh := this.pickHeight()
|
|
n := &node{make([]*node, nh),key,value}
|
|
|
|
// Higher than anything seen before, so tack it on top
|
|
if nh > h {
|
|
this.root.next = append(this.root.next, n)
|
|
}
|
|
|
|
// Update the previous nodes
|
|
for i := 0; i < h && i < nh; i++ {
|
|
n.next[i] = prev[i].next[i]
|
|
prev[i].next[i] = n
|
|
}
|
|
|
|
this.size++
|
|
}
|
|
// Get the length of the skip list
|
|
func (this *SkipList) Len() int {
|
|
return this.size
|
|
}
|
|
// Remove an item from the skip list
|
|
func (this *SkipList) Remove(key interface{}) interface{} {
|
|
prev := this.getPrevious(key)
|
|
if len(prev) == 0 {
|
|
return nil
|
|
}
|
|
cur := prev[0].next[0]
|
|
|
|
// If we found it
|
|
if cur != nil && this.equals(key, cur.key) {
|
|
// Change all the linked lists
|
|
for i := 0; i < len(prev); i++ {
|
|
if prev[i] != nil && prev[i].next[i] != nil {
|
|
prev[i].next[i] = cur.next[i]
|
|
}
|
|
}
|
|
|
|
// Kill off the upper links if they're nil
|
|
for i := len(this.root.next)-1; i>=0; i-- {
|
|
if this.root.next[i] == nil {
|
|
this.root.next = this.root.next[:i]
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
this.size--
|
|
|
|
return cur.value
|
|
}
|
|
|
|
return nil
|
|
}
|
|
// String representation of the list
|
|
func (this *SkipList) String() string {
|
|
str := "{"
|
|
if len(this.root.next) > 0 {
|
|
cur := this.root.next[0]
|
|
for cur != nil {
|
|
str += fmt.Sprint(cur.key)
|
|
str += ":"
|
|
str += fmt.Sprint(cur.value)
|
|
str += " "
|
|
cur = cur.next[0]
|
|
}
|
|
}
|
|
str += "}"
|
|
|
|
return str
|
|
}
|
|
// Get a vertical list of nodes of all the things that occur
|
|
// immediately before "key"
|
|
func (this *SkipList) getPrevious(key interface{}) []*node {
|
|
cur := this.root
|
|
h := len(cur.next)
|
|
nodes := make([]*node, h)
|
|
for i := h-1; i >= 0; i-- {
|
|
for cur.next[i] != nil && this.less(cur.next[i].key, key) {
|
|
cur = cur.next[i]
|
|
}
|
|
nodes[i] = cur
|
|
}
|
|
return nodes
|
|
}
|
|
// Defines an equals method in terms of "less"
|
|
func (this *SkipList) equals(a, b interface{}) bool {
|
|
return !this.less(a,b) && !this.less(b,a)
|
|
}
|
|
// Pick a random height
|
|
func (this *SkipList) pickHeight() int {
|
|
h := 1
|
|
for this.gen.Float64() > this.probability {
|
|
h++
|
|
}
|
|
if h > len(this.root.next) {
|
|
return h + 1
|
|
}
|
|
return h
|
|
}
|