mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
647 lines
18 KiB
Go
647 lines
18 KiB
Go
package bufferpool
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
const PAGE_SIZE int64 = 8192
|
|
|
|
const INVALID_PAGE int64 = -1
|
|
|
|
const PAGE_TYPE_BTREE_OVERFLOW = 8
|
|
const PAGE_TYPE_BTREE_HEADER = 9
|
|
const PAGE_TYPE_BTREE_INTERNAL = 10
|
|
const PAGE_TYPE_BTREE_LEAF = 11
|
|
const PAGE_TYPE_HASH_TABLE = 12
|
|
|
|
// SLOTTED PAGE
|
|
// page size 8192 bytes
|
|
// byte aligned, big endian
|
|
|
|
// |====================================================|
|
|
// | offset | length | |
|
|
// |----------------------------------------------------|
|
|
// | header |
|
|
// |====================================================|
|
|
// | 0 | 8 | pageNumber (int64) |
|
|
// | 8 | 2 | pageType (int16) |
|
|
// | 10 | 2 | slotCount (int16) |
|
|
// | 12 | 2 | localDepth (int16) | // used only for hash tables
|
|
// | 14 | 2 | freeSpaceOffset (int16) |
|
|
// | 16 | 8 | prevPointer (int64) |
|
|
// | 24 | 8 | nextPointer (int64) |
|
|
// | 32 | 4 | checksum (int32) |
|
|
// | 36 | 16 | pageLSN (int64) |
|
|
// |====================================================|
|
|
// | <start of slot array 1..slotCount> |
|
|
// |----------------------------------------------------|
|
|
// | 52 | slotcount | slot entry is int16 |
|
|
// | | * slotwidth | values (payloadOffset) |
|
|
// | | * #slots | |
|
|
// |----------------------------------------------------|
|
|
// | <free space> |
|
|
// |----------------------------------------------------|
|
|
// | <payload starting at freeSpaceOffset> |
|
|
// | see below... |
|
|
// |====================================================|
|
|
|
|
// == internal payload chunk ==
|
|
// keyLength (int16)
|
|
// keyBytes
|
|
// ptrValue (int64) (page number)
|
|
|
|
// == hash table payload chunk ==
|
|
// keyLength (int16)
|
|
// keyBytes
|
|
// rowPayloadChunkLen (int16)
|
|
// rowPayloadChunkBytes
|
|
|
|
// == leaf payload chunk ==
|
|
// keyLength (int16)
|
|
// keyBytes
|
|
// flags int8
|
|
// overflowPtr (int64) [optional]
|
|
// rowPayloadTotalLen (int32) [optional]
|
|
// rowPayloadChunkLen (int16)
|
|
// rowPayloadChunkBytes
|
|
|
|
// == row payload bytes ==
|
|
// writeTID (int64)
|
|
// schemaVersion (int16)
|
|
// versionsPtr (int64)
|
|
// flags (int8) flags that include a deletion marker
|
|
// fieldOffsets (one for each field, int32, FF is null)
|
|
// offsets point to:
|
|
// fieldData (one for each field)
|
|
// valueLen (int32) only used for variable length types
|
|
// valueBytes
|
|
|
|
const PAGE_NUMBER_OFFSET = 0 // offset 0, length 8, end 8
|
|
const PAGE_TYPE_OFFSET = 8 // offset 8, length 2, end 10
|
|
const PAGE_SLOT_COUNT_OFFSET = 10 // offset 10, length 2, end 12
|
|
const PAGE_LOCAL_DEPTH_OFFSET = 12 // offset 12, length 2, end 14
|
|
const PAGE_FREE_SPACE_OFFSET = 14 // offset 14, length 2, end 16
|
|
const PAGE_PREV_POINTER_OFFSET = 16 // offset 16, length 8, end 24
|
|
const PAGE_NEXT_POINTER_OFFSET = 24 // offset 24, length 8, end 32
|
|
const PAGE_CHECKSUM = 32 // offset 32, length 4, end 36
|
|
const PAGE_LSN = 36 // offset 36, length 16, end 52
|
|
const PAGE_SLOTS_START_OFFSET = 52 // offset 52
|
|
|
|
// PAGE_SLOT_LENGTH is the size of the page slot offset value.
|
|
const PAGE_SLOT_LENGTH = 2
|
|
|
|
// MAX_KEY_ON_PAGE_SIZE is 1k; the max key size for now
|
|
// we can make this bigger later with overflow
|
|
const MAX_KEY_ON_PAGE_SIZE = 1024
|
|
|
|
// MAX_PAYLOAD_CHUNK_SIZE_ON_PAGE bytes is the max payload on a
|
|
// page before we overflow. The default of 768 (assumming max key size)
|
|
// gives us 1792 bytes for key and payload.
|
|
// Thus available space on a page after header is 8144 (ish)
|
|
// This gives us room for 4 key/value @ 2036 bytes per page, plus
|
|
// a little bit of wiggle room.
|
|
const MAX_PAYLOAD_CHUNK_SIZE_ON_PAGE = 768
|
|
|
|
type PageLatchState int
|
|
|
|
const (
|
|
None PageLatchState = iota
|
|
Read
|
|
Write
|
|
)
|
|
|
|
// Page represents various types of data page on disk and in memory
|
|
type Page struct {
|
|
mu sync.RWMutex
|
|
latchState PageLatchState
|
|
id PageID
|
|
pinCount int
|
|
isDirty bool
|
|
data [PAGE_SIZE]byte
|
|
}
|
|
|
|
func NewPage(pageID PageID, pinCount int) *Page {
|
|
return &Page{
|
|
id: pageID,
|
|
latchState: None,
|
|
pinCount: pinCount,
|
|
isDirty: false,
|
|
data: [PAGE_SIZE]byte{},
|
|
}
|
|
}
|
|
|
|
func (p *Page) TakeReadLatch() {
|
|
p.mu.RLock()
|
|
p.latchState = Read
|
|
}
|
|
|
|
func (p *Page) ReleaseReadLatch() {
|
|
p.mu.RUnlock()
|
|
p.latchState = None
|
|
}
|
|
|
|
func (p *Page) TakeWriteLatch() {
|
|
p.mu.Lock()
|
|
p.latchState = Write
|
|
}
|
|
|
|
func (p *Page) ReleaseWriteLatch() {
|
|
p.mu.Unlock()
|
|
p.latchState = None
|
|
}
|
|
|
|
func (p *Page) ReleaseAnyLatch() {
|
|
if p.latchState == Read {
|
|
p.ReleaseReadLatch()
|
|
} else if p.latchState == Write {
|
|
p.ReleaseWriteLatch()
|
|
}
|
|
}
|
|
|
|
func (p *Page) LatchState() PageLatchState {
|
|
return p.latchState
|
|
}
|
|
|
|
// routines to read and write from page header information
|
|
func (p *Page) WritePageNumber(pageNumber int64) {
|
|
p.id.Page = pageNumber
|
|
binary.BigEndian.PutUint64(p.data[PAGE_NUMBER_OFFSET:], uint64(pageNumber))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ReadPageNumber() int64 {
|
|
return int64(binary.BigEndian.Uint32(p.data[PAGE_NUMBER_OFFSET:]))
|
|
}
|
|
|
|
func (p *Page) WritePageType(pageType int16) {
|
|
binary.BigEndian.PutUint16(p.data[PAGE_TYPE_OFFSET:], uint16(pageType))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ReadPageType() int16 {
|
|
return int16(binary.BigEndian.Uint16(p.data[PAGE_TYPE_OFFSET:]))
|
|
}
|
|
|
|
func (p *Page) WriteSlotCount(slotCount int16) {
|
|
binary.BigEndian.PutUint16(p.data[PAGE_SLOT_COUNT_OFFSET:], uint16(slotCount))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ReadSlotCount() int16 {
|
|
return int16(binary.BigEndian.Uint16(p.data[PAGE_SLOT_COUNT_OFFSET:]))
|
|
}
|
|
|
|
func (p *Page) WriteLocalDepth(localDepth int16) {
|
|
binary.BigEndian.PutUint16(p.data[PAGE_LOCAL_DEPTH_OFFSET:], uint16(localDepth))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ReadLocalDepth() int16 {
|
|
return int16(binary.BigEndian.Uint16(p.data[PAGE_LOCAL_DEPTH_OFFSET:]))
|
|
}
|
|
|
|
func (p *Page) WriteFreeSpaceOffset(offset int16) {
|
|
binary.BigEndian.PutUint16(p.data[PAGE_FREE_SPACE_OFFSET:], uint16(offset))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ReadFreeSpaceOffset() int16 {
|
|
return int16(binary.BigEndian.Uint16(p.data[PAGE_FREE_SPACE_OFFSET:]))
|
|
}
|
|
|
|
func (p *Page) WritePrevPointer(prevPointer PageID) {
|
|
binary.BigEndian.PutUint64(p.data[PAGE_PREV_POINTER_OFFSET:], uint64(prevPointer.Page))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ReadPrevPointer() PageID {
|
|
page := int64(binary.BigEndian.Uint64(p.data[PAGE_PREV_POINTER_OFFSET:]))
|
|
return PageID{ObjectID: p.id.ObjectID, Shard: p.id.Shard, Page: page}
|
|
}
|
|
|
|
func (p *Page) WriteNextPointer(nextPointer PageID) {
|
|
binary.BigEndian.PutUint64(p.data[PAGE_NEXT_POINTER_OFFSET:], uint64(nextPointer.Page))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ReadNextPointer() PageID {
|
|
page := int64(binary.BigEndian.Uint64(p.data[PAGE_NEXT_POINTER_OFFSET:]))
|
|
return PageID{ObjectID: p.id.ObjectID, Shard: p.id.Shard, Page: page}
|
|
}
|
|
|
|
// read slots from pages
|
|
func (p *Page) ReadPageSlot(slot int16) PageSlot {
|
|
offset := PAGE_SLOTS_START_OFFSET + PAGE_SLOT_LENGTH*slot
|
|
keyOffset := int16(binary.BigEndian.Uint16(p.data[offset:]))
|
|
return PageSlot{
|
|
PayloadOffset: keyOffset,
|
|
}
|
|
}
|
|
|
|
func (p *Page) WritePageSlot(slot int16, value PageSlot) {
|
|
offset := PAGE_SLOTS_START_OFFSET + PAGE_SLOT_LENGTH*slot
|
|
binary.BigEndian.PutUint16(p.data[offset:], uint16(value.PayloadOffset))
|
|
}
|
|
|
|
func (p *Page) ComputeInternalPayloadTotalLength(keyLength int) int32 {
|
|
l := /*keyLength*/ 2 + keyLength + /*ptrValue*/ 8
|
|
return int32(l)
|
|
}
|
|
|
|
func (p *Page) WriteInternalPagePayload(offset int16, keyLen int16, keyBytes []byte, ptrValue int64) {
|
|
binary.BigEndian.PutUint16(p.data[offset:], uint16(keyLen))
|
|
offset += 2
|
|
copy(p.data[offset:], keyBytes)
|
|
offset += keyLen
|
|
binary.BigEndian.PutUint64(p.data[offset:], uint64(ptrValue))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ComputeLeafPayloadTotalLength(keyLength int, payloadLength int) int32 {
|
|
l := /*keyLength*/ 2 + keyLength + /*flags*/ 1 + /*payLoadLength*/ 2 + payloadLength
|
|
if l > MAX_PAYLOAD_CHUNK_SIZE_ON_PAGE {
|
|
l += 8 + 4 // add overflow ptr and total payload size
|
|
}
|
|
return int32(l)
|
|
}
|
|
|
|
func (p *Page) ComputeHashPayloadTotalLength(keyLength int, payloadLength int) int32 {
|
|
l := /*keyLength*/ 2 + keyLength + /*payLoadLength*/ 2 + payloadLength
|
|
return int32(l)
|
|
}
|
|
|
|
func (p *Page) WriteLeafPagePayloadHeader(offset int16, keyLen int16, keyBytes []byte, flags int8, overflowPtr int64, payloadTotalLen int32) int16 {
|
|
binary.BigEndian.PutUint16(p.data[offset:], uint16(keyLen))
|
|
offset += 2
|
|
copy(p.data[offset:], keyBytes)
|
|
offset += keyLen
|
|
p.data[offset] = byte(flags)
|
|
offset += 1
|
|
// if we are overflowing write the overflow ptr
|
|
if flags == 1 {
|
|
binary.BigEndian.PutUint64(p.data[offset:], uint64(overflowPtr))
|
|
offset += 8
|
|
// total length
|
|
binary.BigEndian.PutUint32(p.data[offset:], uint32(payloadTotalLen))
|
|
offset += 4
|
|
}
|
|
p.isDirty = true
|
|
return offset
|
|
}
|
|
|
|
func (p *Page) WriteLeafPagePayloadBytes(offset int16, payloadChunkLength int16, payloadChunkBytes []byte) {
|
|
// chunk length
|
|
binary.BigEndian.PutUint16(p.data[offset:], uint16(payloadChunkLength))
|
|
offset += 2
|
|
// now copy the payload bytes
|
|
copy(p.data[offset:], payloadChunkBytes)
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) ReadLeafPagePayloadBytes(offset int16) (int16, []byte) {
|
|
chunkLen := int16(binary.BigEndian.Uint16(p.data[offset:]))
|
|
offset += 2
|
|
chunkBytes := make([]byte, chunkLen)
|
|
copy(chunkBytes, p.data[offset:offset+chunkLen])
|
|
return chunkLen, chunkBytes
|
|
}
|
|
|
|
func (p *Page) WriteHashPagePayloadBytes(offset int16, keyLen int16, keyBytes []byte, payloadChunkLength int16, payloadChunkBytes []byte) {
|
|
// write key len
|
|
binary.BigEndian.PutUint16(p.data[offset:], uint16(keyLen))
|
|
offset += 2
|
|
// write key
|
|
copy(p.data[offset:], keyBytes)
|
|
offset += keyLen
|
|
// chunk length
|
|
binary.BigEndian.PutUint16(p.data[offset:], uint16(payloadChunkLength))
|
|
offset += 2
|
|
// now copy the payload bytes
|
|
copy(p.data[offset:], payloadChunkBytes)
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) WriteInternalPageChunk(offset int16, chunk InternalPageChunk) {
|
|
binary.BigEndian.PutUint16(p.data[offset:], uint16(chunk.KeyLength))
|
|
offset += 2
|
|
copy(p.data[offset:], chunk.KeyBytes)
|
|
offset += int16(len(chunk.KeyBytes))
|
|
binary.BigEndian.PutUint64(p.data[offset:], uint64(chunk.PtrValue))
|
|
p.isDirty = true
|
|
}
|
|
|
|
func (p *Page) FreeSpaceOnPage() int16 {
|
|
freeSpaceOffset := p.ReadFreeSpaceOffset()
|
|
freespace := freeSpaceOffset - (p.ReadSlotCount()*PAGE_SLOT_LENGTH + PAGE_SLOT_LENGTH + PAGE_SLOTS_START_OFFSET)
|
|
return freespace
|
|
}
|
|
|
|
func (p *Page) CopyPageTo(page *Page) {
|
|
// copy everything but pageNumber & pageType
|
|
offset := int64(PAGE_SLOT_COUNT_OFFSET)
|
|
copy(page.data[offset:], p.data[offset:offset+PAGE_SIZE-offset])
|
|
}
|
|
|
|
func (p *Page) PinCount() int {
|
|
return p.pinCount
|
|
}
|
|
|
|
func (p *Page) ID() PageID {
|
|
return p.id
|
|
}
|
|
|
|
func (p *Page) DecPinCount() {
|
|
if p.pinCount > 0 {
|
|
p.pinCount--
|
|
}
|
|
}
|
|
|
|
func (pg *Page) Dump(label string) {
|
|
indent := 0
|
|
if len(label) > 0 {
|
|
fmt.Printf("%s%s:\n", fmt.Sprintf("%*s", indent, ""), label)
|
|
indent += 4
|
|
}
|
|
pageType := pg.ReadPageType()
|
|
fmt.Printf("%sPAGE(%d) pageType: %d slotCount: %d, freeSpaceOffset: %d, freeSpaceOnPage: %d, prevPtr: %d, nextPtr: %d\n", fmt.Sprintf("%*s", indent, ""), pg.ID(), pageType, pg.ReadSlotCount(), pg.ReadFreeSpaceOffset(), pg.FreeSpaceOnPage(), pg.ReadPrevPointer(), pg.ReadNextPointer())
|
|
fmt.Printf("%sKEYS: -->\n", fmt.Sprintf("%*s", indent, ""))
|
|
indent += 4
|
|
|
|
switch pageType {
|
|
case PAGE_TYPE_BTREE_LEAF:
|
|
// get the keys off the page
|
|
keys := make([]int, 0)
|
|
iter := NewPageSlotIterator(pg, 0)
|
|
for {
|
|
ps := iter.Next()
|
|
if ps == nil {
|
|
break
|
|
}
|
|
pl := ps.KeyPayload(pg)
|
|
keys = append(keys, int(pl.KeyAsInt(pg)))
|
|
}
|
|
|
|
for _, key := range keys {
|
|
fmt.Printf("%s(%d)\n", fmt.Sprintf("%*s", indent, ""), key)
|
|
}
|
|
case PAGE_TYPE_BTREE_INTERNAL, PAGE_TYPE_BTREE_HEADER:
|
|
// get the keys off the page
|
|
keys := make([]int, 0)
|
|
pointers := make([]PageID, 0)
|
|
iter := NewPageSlotIterator(pg, 0)
|
|
for {
|
|
ps := iter.Next()
|
|
if ps == nil {
|
|
break
|
|
}
|
|
pl := ps.KeyPayload(pg)
|
|
keys = append(keys, int(pl.KeyAsInt(pg)))
|
|
ipl := ps.InternalPayload(pg)
|
|
pointers = append(pointers, ipl.ValueAsPagePointer(pg))
|
|
}
|
|
for idx, key := range keys {
|
|
ptr := pointers[idx]
|
|
fmt.Printf("%s(%d, %d)\n", fmt.Sprintf("%*s", indent, ""), key, ptr)
|
|
}
|
|
ptr := pg.ReadNextPointer()
|
|
fmt.Printf("%s(-->, %d)\n", fmt.Sprintf("%*s", indent, ""), ptr)
|
|
}
|
|
}
|
|
|
|
type KeyPayload struct {
|
|
BaseOffset int16
|
|
}
|
|
|
|
func (p *KeyPayload) KeyAsInt(page *Page) int32 {
|
|
return int32(binary.BigEndian.Uint32(page.data[p.BaseOffset+2:]))
|
|
}
|
|
|
|
func (p *KeyPayload) KeyBytes(page *Page) []byte {
|
|
offset := p.BaseOffset
|
|
keyLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2
|
|
result := make([]byte, keyLen)
|
|
copy(result, page.data[offset:offset+keyLen])
|
|
return result
|
|
}
|
|
|
|
type InternalPayload struct {
|
|
BaseOffset int16
|
|
}
|
|
|
|
func (p *InternalPayload) ptrOffset(page *Page) int16 {
|
|
offset := p.BaseOffset
|
|
keyLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2 + keyLen
|
|
return offset
|
|
}
|
|
|
|
func (p *InternalPayload) ValueAsPagePointer(page *Page) PageID {
|
|
offset := p.ptrOffset(page)
|
|
pageid := int64(binary.BigEndian.Uint64(page.data[offset:]))
|
|
return PageID{ObjectID: page.id.ObjectID, Shard: page.id.Shard, Page: pageid}
|
|
}
|
|
|
|
func (p *InternalPayload) PutPagePointer(page *Page, pagePtr PageID) error {
|
|
offset := p.ptrOffset(page)
|
|
binary.BigEndian.PutUint64(page.data[offset:], uint64(pagePtr.Page))
|
|
page.isDirty = true
|
|
return nil
|
|
}
|
|
|
|
func (l *InternalPayload) InternalPageChunk(page *Page) InternalPageChunk {
|
|
offset := l.BaseOffset
|
|
keyLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2
|
|
keyBytes := make([]byte, keyLen)
|
|
copy(keyBytes, page.data[offset:offset+keyLen])
|
|
offset += keyLen
|
|
ptrValue := int64(binary.BigEndian.Uint64(page.data[offset:]))
|
|
return InternalPageChunk{
|
|
KeyLength: keyLen,
|
|
KeyBytes: keyBytes,
|
|
PtrValue: ptrValue,
|
|
}
|
|
}
|
|
|
|
type LeafPayload struct {
|
|
BaseOffset int16
|
|
}
|
|
|
|
func (l *LeafPayload) valueOffset(page *Page) int16 {
|
|
offset := l.BaseOffset
|
|
keyLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2 + keyLen
|
|
return offset
|
|
}
|
|
|
|
func (l *LeafPayload) ValueLength(page *Page) int32 {
|
|
offset := l.valueOffset(page)
|
|
valueLen := int32(binary.BigEndian.Uint32(page.data[offset:]))
|
|
return valueLen
|
|
}
|
|
|
|
func (l *LeafPayload) GetPayloadReader(page *Page) LeafPagePayLoadReader {
|
|
offset := l.BaseOffset
|
|
keyLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2
|
|
keyBytes := make([]byte, keyLen)
|
|
copy(keyBytes, page.data[offset:offset+keyLen])
|
|
offset += keyLen
|
|
flags := int8(page.data[offset])
|
|
offset += 1
|
|
overflowPtr := int64(0)
|
|
payloadTotalLength := int32(0)
|
|
if flags == 1 {
|
|
overflowPtr = int64(binary.BigEndian.Uint64(page.data[offset:]))
|
|
offset += 8
|
|
payloadTotalLength = int32(binary.BigEndian.Uint32(page.data[offset:]))
|
|
offset += 4
|
|
}
|
|
valueLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2
|
|
if payloadTotalLength == 0 {
|
|
payloadTotalLength = int32(valueLen)
|
|
}
|
|
valueBytes := make([]byte, valueLen)
|
|
copy(valueBytes, page.data[offset:int16(offset)+valueLen])
|
|
return LeafPagePayLoadReader{
|
|
KeyLength: keyLen,
|
|
KeyBytes: keyBytes,
|
|
Flags: flags,
|
|
OverflowPtr: overflowPtr,
|
|
PayloadTotalLength: payloadTotalLength,
|
|
PayloadChunkLength: valueLen,
|
|
PayloadChunkBytes: valueBytes,
|
|
}
|
|
}
|
|
|
|
func (l *LeafPayload) IsVisibleToTID(page *Page, tid int64) bool {
|
|
return true
|
|
}
|
|
|
|
type HashPayload struct {
|
|
BaseOffset int16
|
|
}
|
|
|
|
func (l *HashPayload) valueOffset(page *Page) int16 {
|
|
offset := l.BaseOffset
|
|
keyLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2 + keyLen
|
|
return offset
|
|
}
|
|
|
|
func (l *HashPayload) ValueLength(page *Page) int16 {
|
|
offset := l.valueOffset(page)
|
|
valueLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
return valueLen
|
|
}
|
|
|
|
func (l *HashPayload) GetPayloadReader(page *Page) HashPagePayLoadReader {
|
|
offset := l.BaseOffset
|
|
keyLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2
|
|
keyBytes := make([]byte, keyLen)
|
|
copy(keyBytes, page.data[offset:offset+keyLen])
|
|
offset += keyLen
|
|
valueLen := int16(binary.BigEndian.Uint16(page.data[offset:]))
|
|
offset += 2
|
|
valueBytes := make([]byte, valueLen)
|
|
copy(valueBytes, page.data[offset:int16(offset)+valueLen])
|
|
return HashPagePayLoadReader{
|
|
KeyLength: keyLen,
|
|
KeyBytes: keyBytes,
|
|
PayloadChunkLength: valueLen,
|
|
PayloadChunkBytes: valueBytes,
|
|
}
|
|
}
|
|
|
|
type PageSlot struct {
|
|
PayloadOffset int16
|
|
}
|
|
|
|
func (s *PageSlot) KeyPayload(page *Page) KeyPayload {
|
|
return KeyPayload{BaseOffset: s.PayloadOffset}
|
|
}
|
|
|
|
func (s *PageSlot) InternalPayload(page *Page) InternalPayload {
|
|
return InternalPayload{BaseOffset: s.PayloadOffset}
|
|
}
|
|
|
|
func (s *PageSlot) LeafPayload(page *Page) LeafPayload {
|
|
return LeafPayload{BaseOffset: s.PayloadOffset}
|
|
}
|
|
|
|
func (s *PageSlot) HashPayload(page *Page) HashPayload {
|
|
return HashPayload{BaseOffset: s.PayloadOffset}
|
|
}
|
|
|
|
type HashPagePayLoadReader struct {
|
|
KeyLength int16
|
|
KeyBytes []byte
|
|
PayloadChunkLength int16
|
|
PayloadChunkBytes []byte
|
|
}
|
|
|
|
func (pc *HashPagePayLoadReader) Length() int32 {
|
|
l := /*KeyLength*/ 2 + pc.KeyLength + /*PayLoadChunkLength*/ 2 + pc.PayloadChunkLength
|
|
return int32(l)
|
|
}
|
|
|
|
type LeafPagePayLoadReader struct {
|
|
KeyLength int16
|
|
KeyBytes []byte
|
|
Flags int8
|
|
OverflowPtr int64
|
|
PayloadTotalLength int32
|
|
PayloadChunkLength int16
|
|
PayloadChunkBytes []byte
|
|
}
|
|
|
|
func (pc *LeafPagePayLoadReader) Length() int32 {
|
|
l := /*keyLength*/ 2 + pc.KeyLength + /*flags*/ 1 + /*payLoadLength*/ 2 + pc.PayloadChunkLength
|
|
if pc.Flags == 1 {
|
|
l += 8 + 4 // add overflow ptr and total payload size
|
|
}
|
|
return int32(l)
|
|
}
|
|
|
|
type InternalPageChunk struct {
|
|
KeyLength int16
|
|
KeyBytes []byte
|
|
PtrValue int64
|
|
}
|
|
|
|
func (pc *InternalPageChunk) Length() int {
|
|
return 2 + len(pc.KeyBytes) + 8
|
|
}
|
|
|
|
type PageSlotIterator struct {
|
|
page *Page
|
|
slotCount int16
|
|
cursor int16
|
|
}
|
|
|
|
func NewPageSlotIterator(page *Page, fromSlot int16) *PageSlotIterator {
|
|
i := &PageSlotIterator{
|
|
page: page,
|
|
slotCount: page.ReadSlotCount(),
|
|
cursor: fromSlot,
|
|
}
|
|
return i
|
|
}
|
|
|
|
func (i *PageSlotIterator) Next() *PageSlot {
|
|
if i.cursor < i.slotCount {
|
|
s := i.page.ReadPageSlot(i.cursor)
|
|
i.cursor++
|
|
return &s
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *PageSlotIterator) Cursor() int16 {
|
|
return i.cursor
|
|
}
|