Skip to main content

skl.go

skl.go - Overview

This file implements a concurrent skiplist data structure adapted from RocksDB's inline skiplist. It provides efficient insertion, retrieval, and iteration over a sorted set of key-value pairs. It is optimized for concurrent access and supports overwrites.

Detailed Documentation

Constants

maxHeight

const maxHeight = 20

The maximum height of the skiplist.

heightIncrease

const heightIncrease = math.MaxUint32 / 3

Determines the probability of a node having a higher level.

MaxNodeSize

const MaxNodeSize = int(unsafe.Sizeof(node{}))

The memory footprint of a node of maximum height.

Structs

node

type node struct {
value atomic.Uint64
keyOffset uint32
keySize uint16
height uint16
tower [maxHeight]atomic.Uint32
}

Represents a node in the skiplist.

  • value: Stores the value's offset and size in the arena.
  • keyOffset: Stores the key's offset in the arena.
  • keySize: Stores the key's size.
  • height: The height of the node in the skiplist.
  • tower: An array of atomic uint32 values, representing the offsets of the next nodes in each level.

Skiplist

type Skiplist struct {
height atomic.Int32
head *node
ref atomic.Int32
arena *Arena
OnClose func()
}

Represents the skiplist data structure.

  • height: The current height of the skiplist.
  • head: A pointer to the head node of the skiplist.
  • ref: An atomic counter for managing the skiplist's lifecycle.
  • arena: A pointer to the arena where nodes and keys are allocated.
  • OnClose: A function to be called when the skiplist is no longer referenced.

Functions

IncrRef

func (s *Skiplist) IncrRef()

Increases the reference count of the skiplist.

DecrRef

func (s *Skiplist) DecrRef()

Decreases the reference count of the skiplist. Deallocates the Skiplist when the reference count reaches zero.

newNode

func newNode(arena *Arena, key []byte, v y.ValueStruct, height int) *node

Creates a new node in the arena.

  • arena: The arena to allocate the node in.
  • key: The key to store in the node.
  • v: The value to store in the node.
  • height: The height of the new node.

Returns:

  • A pointer to the newly created node.

encodeValue

func encodeValue(valOffset uint32, valSize uint32) uint64

Encodes the value offset and size into a uint64.

  • valOffset: The offset of the value in the arena.
  • valSize: The size of the value.

Returns:

  • A uint64 containing the encoded value offset and size.

decodeValue

func decodeValue(value uint64) (valOffset uint32, valSize uint32)

Decodes the value offset and size from a uint64.

  • value: The encoded uint64 value.

Returns:

  • valOffset: The offset of the value in the arena.
  • valSize: The size of the value.

NewSkiplist

func NewSkiplist(arenaSize int64) *Skiplist

Creates a new empty skiplist.

  • arenaSize: The size of the arena to allocate for the skiplist.

Returns:

  • A pointer to the newly created skiplist.

getValueOffset

func (s *node) getValueOffset() (uint32, uint32)

Gets the value offset and size from the node.

Returns:

  • uint32: The offset of the value in the arena.
  • uint32: The size of the value.

key

func (s *node) key(arena *Arena) []byte

Gets the key from the arena.

  • arena: The arena containing the key.

Returns:

  • The key as a byte slice.

setValue

func (s *node) setValue(arena *Arena, v y.ValueStruct)

Sets the value of a node.

  • arena: The arena to allocate the value in.
  • v: The value to set.

getNextOffset

func (s *node) getNextOffset(h int) uint32

Gets the offset of the next node at a given level.

  • h: The level to get the next node offset from.

Returns:

  • The offset of the next node at the given level.

casNextOffset

func (s *node) casNextOffset(h int, old, val uint32) bool

Atomically compares and swaps the next offset at a given level.

  • h: The level to perform the compare and swap.
  • old: The old offset value.
  • val: The new offset value.

Returns:

  • true if the compare and swap was successful, false otherwise.

randomHeight

func (s *Skiplist) randomHeight() int

Generates a random height for a new node.

Returns:

  • A random height between 1 and maxHeight.

getNext

func (s *Skiplist) getNext(nd *node, height int) *node

Gets the next node at a given level.

  • nd: The current node.
  • height: The level to get the next node from.

Returns:

  • A pointer to the next node at the given level.

findNear

func (s *Skiplist) findNear(key []byte, less bool, allowEqual bool) (*node, bool)

Finds the node near to the key.

  • key: The key to search for.
  • less: If true, finds the rightmost node such that node.key < key (if allowEqual=false) or node.key <= key (if allowEqual=true). If false, finds the leftmost node such that node.key > key (if allowEqual=false) or node.key >= key (if allowEqual=true).
  • allowEqual: If true, allows finding a node with the same key.

Returns:

  • *node: The node found.
  • bool: True if the node has key equal to the given key.

findSpliceForLevel

func (s *Skiplist) findSpliceForLevel(key []byte, before *node, level int) (*node, *node)

Finds the nodes before and after the key at a given level.

  • key: The key to search for.
  • before: The node to start searching from.
  • level: The level to search at.

Returns:

  • *node: The node before the key.
  • *node: The node after the key.

getHeight

func (s *Skiplist) getHeight() int32

Gets the current height of the skiplist.

Returns:

  • The current height of the skiplist.

Put

func (s *Skiplist) Put(key []byte, v y.ValueStruct)

Inserts the key-value pair into the skiplist.

  • key: The key to insert.
  • v: The value to insert.

Empty

func (s *Skiplist) Empty() bool

Checks if the skiplist is empty.

Returns:

  • true if the skiplist is empty, false otherwise.

findLast

func (s *Skiplist) findLast() *node

Finds the last node in the skiplist.

Returns:

  • A pointer to the last node in the skiplist, or nil if the skiplist is empty.

Get

func (s *Skiplist) Get(key []byte) y.ValueStruct

Gets the value associated with the key.

  • key: The key to search for.

Returns:

  • The value associated with the key, or an empty y.ValueStruct if the key is not found.

NewIterator

func (s *Skiplist) NewIterator() *Iterator

Creates a new iterator for the skiplist.

Returns:

  • A pointer to the newly created iterator.

MemSize

func (s *Skiplist) MemSize() int64

Returns the memory usage of the skiplist.

Returns:

  • The memory usage of the skiplist in bytes.

Structs

Iterator

type Iterator struct {
list *Skiplist
n *node
}

Represents an iterator for the skiplist.

  • list: A pointer to the skiplist being iterated over.
  • n: A pointer to the current node in the iteration.

Functions

Close

func (s *Iterator) Close() error

Closes the iterator and releases resources.

Returns:

  • nil

Valid

func (s *Iterator) Valid() bool

Checks if the iterator is currently pointing to a valid node.

Returns:

  • true if the iterator is valid, false otherwise.

Key

func (s *Iterator) Key() []byte

Gets the key of the current node.

Returns:

  • The key of the current node as a byte slice.

Value

func (s *Iterator) Value() y.ValueStruct

Gets the value of the current node.

Returns:

  • The value of the current node.

ValueUint64

func (s *Iterator) ValueUint64() uint64

Gets the uint64 value of the current node.

Returns:

  • The uint64 value of the current node.

Next

func (s *Iterator) Next()

Advances the iterator to the next node.

Prev

func (s *Iterator) Prev()

Moves the iterator to the previous node.

Seek

func (s *Iterator) Seek(target []byte)

Seeks the iterator to the first node with a key greater than or equal to the target.

  • target: The target key.

SeekForPrev

func (s *Iterator) SeekForPrev(target []byte)

Seeks the iterator to the first node with a key less than or equal to the target.

  • target: The target key.

SeekToFirst

func (s *Iterator) SeekToFirst()

Seeks the iterator to the first node in the skiplist.

SeekToLast

func (s *Iterator) SeekToLast()

Seeks the iterator to the last node in the skiplist.

Structs

UniIterator

type UniIterator struct {
iter *Iterator
reversed bool
}

Represents a unidirectional iterator for the skiplist.

  • iter: A pointer to the underlying iterator.
  • reversed: Indicates whether the iterator is reversed.

Functions

NewUniIterator

func (s *Skiplist) NewUniIterator(reversed bool) *UniIterator

Creates a new unidirectional iterator for the skiplist.

  • reversed: Indicates whether the iterator should be reversed.

Returns:

  • A pointer to the newly created unidirectional iterator.

Next

func (s *UniIterator) Next()

Advances the iterator to the next node.

Rewind

func (s *UniIterator) Rewind()

Rewinds the iterator to the beginning or end, depending on the direction.

Seek

func (s *UniIterator) Seek(key []byte)

Seeks the iterator to the first node with a key greater than or equal to the target (or less than or equal to if reversed).

  • key: The target key.

Key

func (s *UniIterator) Key() []byte

Gets the key of the current node.

Returns:

  • The key of the current node as a byte slice.

Value

func (s *UniIterator) Value() y.ValueStruct

Gets the value of the current node.

Returns:

  • The value of the current node.

Valid

func (s *UniIterator) Valid() bool

Checks if the iterator is currently pointing to a valid node.

Returns:

  • true if the iterator is valid, false otherwise.

Close

func (s *UniIterator) Close() error

Closes the iterator and releases resources.

Returns:

  • nil

Code Examples

Creating a new Skiplist

package main

import (
"fmt"

"github.com/dgraph-io/badger/v4/y"
"github.com/hypermode/storage/skl"
)

func main() {
arenaSize := int64(1 << 20) // 1MB arena size
skiplist := skl.NewSkiplist(arenaSize)
defer skiplist.DecrRef()

key := []byte("exampleKey")
value := y.ValueStruct{Value: []byte("exampleValue")}

skiplist.Put(key, value)

retrievedValue := skiplist.Get(key)

fmt.Printf("Retrieved Value: %s\n", retrievedValue.Value)
}