127 lines
2.7 KiB
Go
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))
|
|
}
|