mirror of
https://github.com/luxfi/zapdb.git
synced 2026-07-26 22:46:36 +00:00
Removed /v4 suffix from module path and all internal imports. Go module is now github.com/luxfi/zapdb (no major version suffix). Import as: import "github.com/luxfi/zapdb" No code changes — only module path and import rewrite.
123 lines
3.4 KiB
Go
123 lines
3.4 KiB
Go
/*
|
|
* SPDX-FileCopyrightText: © 2017-2025 Istari Digital, Inc.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package skl
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"unsafe"
|
|
|
|
"github.com/luxfi/zapdb/y"
|
|
)
|
|
|
|
const (
|
|
offsetSize = int(unsafe.Sizeof(uint32(0)))
|
|
|
|
// Always align nodes on 64-bit boundaries, even on 32-bit architectures,
|
|
// so that the node.value field is 64-bit aligned. This is necessary because
|
|
// node.getValueOffset uses atomic.LoadUint64, which expects its input
|
|
// pointer to be 64-bit aligned.
|
|
nodeAlign = int(unsafe.Sizeof(uint64(0))) - 1
|
|
)
|
|
|
|
// Arena should be lock-free.
|
|
type Arena struct {
|
|
n atomic.Uint32
|
|
buf []byte
|
|
}
|
|
|
|
// newArena returns a new arena.
|
|
func newArena(n int64) *Arena {
|
|
// Don't store data at position 0 in order to reserve offset=0 as a kind
|
|
// of nil pointer.
|
|
out := &Arena{buf: make([]byte, n)}
|
|
out.n.Store(1)
|
|
return out
|
|
}
|
|
|
|
func (s *Arena) size() int64 {
|
|
return int64(s.n.Load())
|
|
}
|
|
|
|
// putNode allocates a node in the arena. The node is aligned on a pointer-sized
|
|
// boundary. The arena offset of the node is returned.
|
|
func (s *Arena) putNode(height int) uint32 {
|
|
// Compute the amount of the tower that will never be used, since the height
|
|
// is less than maxHeight.
|
|
unusedSize := (maxHeight - height) * offsetSize
|
|
|
|
// Pad the allocation with enough bytes to ensure pointer alignment.
|
|
l := uint32(MaxNodeSize - unusedSize + nodeAlign)
|
|
n := s.n.Add(l)
|
|
y.AssertTruef(int(n) <= len(s.buf),
|
|
"Arena too small, toWrite:%d newTotal:%d limit:%d",
|
|
l, n, len(s.buf))
|
|
|
|
// Return the aligned offset.
|
|
m := (n - l + uint32(nodeAlign)) & ^uint32(nodeAlign)
|
|
return m
|
|
}
|
|
|
|
// Put will *copy* val into arena. To make better use of this, reuse your input
|
|
// val buffer. Returns an offset into buf. User is responsible for remembering
|
|
// size of val. We could also store this size inside arena but the encoding and
|
|
// decoding will incur some overhead.
|
|
func (s *Arena) putVal(v y.ValueStruct) uint32 {
|
|
l := v.EncodedSize()
|
|
n := s.n.Add(l)
|
|
y.AssertTruef(int(n) <= len(s.buf),
|
|
"Arena too small, toWrite:%d newTotal:%d limit:%d",
|
|
l, n, len(s.buf))
|
|
m := n - l
|
|
v.Encode(s.buf[m:])
|
|
return m
|
|
}
|
|
|
|
func (s *Arena) putKey(key []byte) uint32 {
|
|
l := uint32(len(key))
|
|
n := s.n.Add(l)
|
|
y.AssertTruef(int(n) <= len(s.buf),
|
|
"Arena too small, toWrite:%d newTotal:%d limit:%d",
|
|
l, n, len(s.buf))
|
|
// m is the offset where you should write.
|
|
// n = new len - key len give you the offset at which you should write.
|
|
m := n - l
|
|
// Copy to buffer from m:n
|
|
y.AssertTrue(len(key) == copy(s.buf[m:n], key))
|
|
return m
|
|
}
|
|
|
|
// getNode returns a pointer to the node located at offset. If the offset is
|
|
// zero, then the nil node pointer is returned.
|
|
func (s *Arena) getNode(offset uint32) *node {
|
|
if offset == 0 {
|
|
return nil
|
|
}
|
|
|
|
return (*node)(unsafe.Pointer(&s.buf[offset]))
|
|
}
|
|
|
|
// getKey returns byte slice at offset.
|
|
func (s *Arena) getKey(offset uint32, size uint16) []byte {
|
|
return s.buf[offset : offset+uint32(size)]
|
|
}
|
|
|
|
// getVal returns byte slice at offset. The given size should be just the value
|
|
// size and should NOT include the meta bytes.
|
|
func (s *Arena) getVal(offset uint32, size uint32) (ret y.ValueStruct) {
|
|
ret.Decode(s.buf[offset : offset+size])
|
|
return
|
|
}
|
|
|
|
// getNodeOffset returns the offset of node in the arena. If the node pointer is
|
|
// nil, then the zero offset is returned.
|
|
func (s *Arena) getNodeOffset(nd *node) uint32 {
|
|
if nd == nil {
|
|
return 0
|
|
}
|
|
|
|
return uint32(uintptr(unsafe.Pointer(nd)) - uintptr(unsafe.Pointer(&s.buf[0])))
|
|
}
|