kevo/pkg/sstable/block/block_reader.go
Jeremy Tregunna 6fc3be617d
Some checks failed
Go Tests / Run Tests (1.24.2) (push) Has been cancelled
feat: Initial release of kevo storage engine.
Adds a complete LSM-based storage engine with these features:
- Single-writer based architecture for the storage engine
- WAL for durability, and hey it's configurable
- MemTable with skip list implementation for fast read/writes
- SSTable with block-based structure for on-disk level-based storage
- Background compaction with tiered strategy
- ACID transactions
- Good documentation (I hope)
2025-04-20 14:06:50 -06:00

73 lines
1.8 KiB
Go

package block
import (
"encoding/binary"
"fmt"
"github.com/cespare/xxhash/v2"
)
// Reader provides methods to read data from a serialized block
type Reader struct {
data []byte
restartPoints []uint32
numRestarts uint32
checksum uint64
}
// NewReader creates a new block reader
func NewReader(data []byte) (*Reader, error) {
if len(data) < BlockFooterSize {
return nil, fmt.Errorf("block data too small: %d bytes", len(data))
}
// Read footer
footerOffset := len(data) - BlockFooterSize
numRestarts := binary.LittleEndian.Uint32(data[footerOffset : footerOffset+4])
checksum := binary.LittleEndian.Uint64(data[footerOffset+4:])
// Verify checksum - the checksum covers everything except the checksum itself
computedChecksum := xxhash.Sum64(data[:len(data)-8])
if computedChecksum != checksum {
return nil, fmt.Errorf("block checksum mismatch: expected %d, got %d",
checksum, computedChecksum)
}
// Read restart points
restartOffset := footerOffset - int(numRestarts)*4
if restartOffset < 0 {
return nil, fmt.Errorf("invalid restart points offset")
}
restartPoints := make([]uint32, numRestarts)
for i := uint32(0); i < numRestarts; i++ {
restartPoints[i] = binary.LittleEndian.Uint32(
data[restartOffset+int(i)*4:])
}
reader := &Reader{
data: data,
restartPoints: restartPoints,
numRestarts: numRestarts,
checksum: checksum,
}
return reader, nil
}
// Iterator returns an iterator for the block
func (r *Reader) Iterator() *Iterator {
// Calculate the data end position (everything before the restart points array)
dataEnd := len(r.data) - BlockFooterSize - 4*len(r.restartPoints)
return &Iterator{
reader: r,
currentPos: 0,
currentKey: nil,
currentVal: nil,
restartIdx: 0,
initialized: false,
dataEnd: uint32(dataEnd),
}
}