package skl
import (
"math"
"sync/atomic"
"unsafe"
"github.com/dgraph-io/badger/v4/y"
"github.com/dgraph-io/ristretto/v2/z"
)
const (
maxHeight = 20
heightIncrease = math .MaxUint32 / 3
)
const MaxNodeSize = int (unsafe .Sizeof (node {}))
type node struct {
value atomic .Uint64
keyOffset uint32
keySize uint16
height uint16
tower [maxHeight ]atomic .Uint32
}
type Skiplist struct {
height atomic .Int32
head *node
ref atomic .Int32
arena *Arena
OnClose func ()
}
func (s *Skiplist ) IncrRef () {
s .ref .Add (1 )
}
func (s *Skiplist ) DecrRef () {
newRef := s .ref .Add (-1 )
if newRef > 0 {
return
}
if s .OnClose != nil {
s .OnClose ()
}
s .arena = nil
s .head = nil
}
func newNode(arena *Arena , key []byte , v y .ValueStruct , height int ) *node {
offset := arena .putNode (height )
node := arena .getNode (offset )
node .keyOffset = arena .putKey (key )
node .keySize = uint16 (len (key ))
node .height = uint16 (height )
node .value .Store (encodeValue (arena .putVal (v ), v .EncodedSize ()))
return node
}
func encodeValue(valOffset uint32 , valSize uint32 ) uint64 {
return uint64 (valSize )<<32 | uint64 (valOffset )
}
func decodeValue(value uint64 ) (valOffset uint32 , valSize uint32 ) {
valOffset = uint32 (value )
valSize = uint32 (value >> 32 )
return
}
func NewSkiplist (arenaSize int64 ) *Skiplist {
arena := newArena (arenaSize )
head := newNode (arena , nil , y .ValueStruct {}, maxHeight )
s := &Skiplist {head : head , arena : arena }
s .height .Store (1 )
s .ref .Store (1 )
return s
}
func (s *node ) getValueOffset () (uint32 , uint32 ) {
value := s .value .Load ()
return decodeValue (value )
}
func (s *node ) key (arena *Arena ) []byte {
return arena .getKey (s .keyOffset , s .keySize )
}
func (s *node ) setValue (arena *Arena , v y .ValueStruct ) {
valOffset := arena .putVal (v )
value := encodeValue (valOffset , v .EncodedSize ())
s .value .Store (value )
}
func (s *node ) getNextOffset (h int ) uint32 {
return s .tower [h ].Load ()
}
func (s *node ) casNextOffset (h int , old , val uint32 ) bool {
return s .tower [h ].CompareAndSwap (old , val )
}
func (s *Skiplist ) randomHeight () int {
h := 1
for h < maxHeight && z .FastRand () <= heightIncrease {
h ++
}
return h
}
func (s *Skiplist ) getNext (nd *node , height int ) *node {
return s .arena .getNode (nd .getNextOffset (height ))
}
func (s *Skiplist ) findNear (key []byte , less bool , allowEqual bool ) (*node , bool ) {
x := s .head
level := int (s .getHeight () - 1 )
for {
next := s .getNext (x , level )
if next == nil {
if level > 0 {
level --
continue
}
if !less {
return nil , false
}
if x == s .head {
return nil , false
}
return x , false
}
nextKey := next .key (s .arena )
cmp := y .CompareKeys (key , nextKey )
if cmp > 0 {
x = next
continue
}
if cmp == 0 {
if allowEqual {
return next , true
}
if !less {
return s .getNext (next , 0 ), false
}
if level > 0 {
level --
continue
}
if x == s .head {
return nil , false
}
return x , false
}
if level > 0 {
level --
continue
}
if !less {
return next , false
}
if x == s .head {
return nil , false
}
return x , false
}
}
func (s *Skiplist ) findSpliceForLevel (key []byte , before *node , level int ) (*node , *node ) {
for {
next := s .getNext (before , level )
if next == nil {
return before , next
}
nextKey := next .key (s .arena )
cmp := y .CompareKeys (key , nextKey )
if cmp == 0 {
return next , next
}
if cmp < 0 {
return before , next
}
before = next
}
}
func (s *Skiplist ) getHeight () int32 {
return s .height .Load ()
}
func (s *Skiplist ) Put (key []byte , v y .ValueStruct ) {
listHeight := s .getHeight ()
var prev [maxHeight + 1 ]*node
var next [maxHeight + 1 ]*node
prev [listHeight ] = s .head
next [listHeight ] = nil
for i := int (listHeight ) - 1 ; i >= 0 ; i -- {
prev [i ], next [i ] = s .findSpliceForLevel (key , prev [i +1 ], i )
if prev [i ] == next [i ] {
prev [i ].setValue (s .arena , v )
return
}
}
height := s .randomHeight ()
x := newNode (s .arena , key , v , height )
listHeight = s .getHeight ()
for height > int (listHeight ) {
if s .height .CompareAndSwap (listHeight , int32 (height )) {
break
}
listHeight = s .getHeight ()
}
for i := 0 ; i < height ; i ++ {
for {
if prev [i ] == nil {
y .AssertTrue (i > 1 )
prev [i ], next [i ] = s .findSpliceForLevel (key , s .head , i )
y .AssertTrue (prev [i ] != next [i ])
}
nextOffset := s .arena .getNodeOffset (next [i ])
x .tower [i ].Store (nextOffset )
if prev [i ].casNextOffset (i , nextOffset , s .arena .getNodeOffset (x )) {
break
}
prev [i ], next [i ] = s .findSpliceForLevel (key , prev [i ], i )
if prev [i ] == next [i ] {
y .AssertTruef (i == 0 , "Equality can happen only on base level: %d" , i )
prev [i ].setValue (s .arena , v )
return
}
}
}
}
func (s *Skiplist ) Empty () bool {
return s .findLast () == nil
}
func (s *Skiplist ) findLast () *node {
n := s .head
level := int (s .getHeight ()) - 1
for {
next := s .getNext (n , level )
if next != nil {
n = next
continue
}
if level == 0 {
if n == s .head {
return nil
}
return n
}
level --
}
}
func (s *Skiplist ) Get (key []byte ) y .ValueStruct {
n , _ := s .findNear (key , false , true )
if n == nil {
return y .ValueStruct {}
}
nextKey := s .arena .getKey (n .keyOffset , n .keySize )
if !y .SameKey (key , nextKey ) {
return y .ValueStruct {}
}
valOffset , valSize := n .getValueOffset ()
vs := s .arena .getVal (valOffset , valSize )
vs .Version = y .ParseTs (nextKey )
return vs
}
func (s *Skiplist ) NewIterator () *Iterator {
s .IncrRef ()
return &Iterator {list : s }
}
func (s *Skiplist ) MemSize () int64 { return s .arena .size () }
type Iterator struct {
list *Skiplist
n *node
}
func (s *Iterator ) Close () error {
s .list .DecrRef ()
return nil
}
func (s *Iterator ) Valid () bool { return s .n != nil }
func (s *Iterator ) Key () []byte {
return s .list .arena .getKey (s .n .keyOffset , s .n .keySize )
}
func (s *Iterator ) Value () y .ValueStruct {
valOffset , valSize := s .n .getValueOffset ()
return s .list .arena .getVal (valOffset , valSize )
}
func (s *Iterator ) ValueUint64 () uint64 {
return s .n .value .Load ()
}
func (s *Iterator ) Next () {
y .AssertTrue (s .Valid ())
s .n = s .list .getNext (s .n , 0 )
}
func (s *Iterator ) Prev () {
y .AssertTrue (s .Valid ())
s .n , _ = s .list .findNear (s .Key (), true , false )
}
func (s *Iterator ) Seek (target []byte ) {
s .n , _ = s .list .findNear (target , false , true )
}
func (s *Iterator ) SeekForPrev (target []byte ) {
s .n , _ = s .list .findNear (target , true , true )
}
func (s *Iterator ) SeekToFirst () {
s .n = s .list .getNext (s .list .head , 0 )
}
func (s *Iterator ) SeekToLast () {
s .n = s .list .findLast ()
}
type UniIterator struct {
iter *Iterator
reversed bool
}
func (s *Skiplist ) NewUniIterator (reversed bool ) *UniIterator {
return &UniIterator {
iter : s .NewIterator (),
reversed : reversed ,
}
}
func (s *UniIterator ) Next () {
if !s .reversed {
s .iter .Next ()
} else {
s .iter .Prev ()
}
}
func (s *UniIterator ) Rewind () {
if !s .reversed {
s .iter .SeekToFirst ()
} else {
s .iter .SeekToLast ()
}
}
func (s *UniIterator ) Seek (key []byte ) {
if !s .reversed {
s .iter .Seek (key )
} else {
s .iter .SeekForPrev (key )
}
}
func (s *UniIterator ) Key () []byte { return s .iter .Key () }
func (s *UniIterator ) Value () y .ValueStruct { return s .iter .Value () }
func (s *UniIterator ) Valid () bool { return s .iter .Valid () }
func (s *UniIterator ) Close () error { return s .iter .Close () }
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .