qcc/internal/store/index.go
2026-06-30 12:52:09 +03:00

127 lines
2.7 KiB
Go

package store
import (
"encoding/binary"
"fmt"
"os"
"path/filepath"
"github.com/niko/qcc/pkg/types"
)
type IndexFrame struct {
Entries map[uint64]IndexEntry
FrameID uint64
}
func BuildIndex(table *Table) (*IndexFrame, error) {
table.mu.RLock()
defer table.mu.RUnlock()
idx := &IndexFrame{
Entries: make(map[uint64]IndexEntry),
FrameID: table.nextID - 1,
}
for keyHash, entry := range table.index {
idx.Entries[keyHash] = entry
}
return idx, nil
}
func (idx *IndexFrame) MarshalBinary() ([]byte, error) {
size := 8 + 8 + len(idx.Entries)*24
buf := make([]byte, size)
off := 0
binary.BigEndian.PutUint64(buf[off:], idx.FrameID)
off += 8
binary.BigEndian.PutUint64(buf[off:], uint64(len(idx.Entries)))
off += 8
for keyHash, entry := range idx.Entries {
binary.BigEndian.PutUint64(buf[off:], keyHash)
off += 8
binary.BigEndian.PutUint64(buf[off:], entry.FrameID)
off += 8
binary.BigEndian.PutUint64(buf[off:], uint64(entry.Offset))
off += 8
}
return buf, nil
}
func UnmarshalIndexFrame(data []byte) (*IndexFrame, error) {
if len(data) < 16 {
return nil, nil
}
idx := &IndexFrame{
Entries: make(map[uint64]IndexEntry),
}
off := 0
idx.FrameID = binary.BigEndian.Uint64(data[off:])
off += 8
count := binary.BigEndian.Uint64(data[off:])
off += 8
for i := uint64(0); i < count; i++ {
if off+24 > len(data) {
break
}
keyHash := binary.BigEndian.Uint64(data[off:])
off += 8
frameID := binary.BigEndian.Uint64(data[off:])
off += 8
offset := binary.BigEndian.Uint64(data[off:])
off += 8
idx.Entries[keyHash] = IndexEntry{FrameID: frameID, Offset: uint32(offset)}
}
return idx, nil
}
func AppendIndexToTable(table *Table, idx *IndexFrame) error {
idxData, err := idx.MarshalBinary()
if err != nil {
return err
}
keyHash := uint64(0xFFFFFFFFFFFFFFFF)
header := RecordHeader{
RecType: types.RecIndex,
Flags: 0,
TableID: 0,
KeyHash: keyHash,
PayloadSize: uint32(len(idxData)),
}
frame := &Frame{
Header: NewFrameHeader(table.nextID-1, table.lastHash),
Records: []Record{
{Header: header, Payload: idxData},
},
}
data, err := MarshalFrame(frame)
if err != nil {
return err
}
frameName := pathForIndexFrame(table.name, table.nextID)
framePath := filepath.Join(table.path, frameName)
if err := os.WriteFile(framePath, data, 0600); err != nil {
return err
}
checksum := ComputeFrameChecksum(data[:len(data)-32])
table.engine.UpdateFrameState(table.name, table.nextID, checksum)
table.lastHash = checksum
table.nextID++
table.frameCount++
return nil
}
func pathForIndexFrame(tableName string, frameID uint64) string {
return filepath.Join(tableName, "indexes", fmt.Sprintf("%016x.idx", frameID))
}