Files
qb/atree/x.go
2026-05-17 12:58:46 +00:00

148 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package atree
import "gordenko.dev/dima/qb/bin"
const (
payloadSize = 24
recordSize = 4 + 4 // timestamp + pageNo
)
type DataPage struct {
PrevPageNo uint32
LowerTimestamp uint32
Timestamps [][]byte // chunks
TimestampsSize int
Values [][]byte // chunks
ValuesSize int
PageNo uint32
IsReused bool
}
type IndexPage struct {
LowerTimestamp uint32
Data []byte
PageNo uint32
IsReused bool
}
type IndexLevel struct {
// вже записані дані (лише для першої в списку індексної сторінки, беремо із _metric)
// дані потрібні, якщо сторінка буде заповнена і піде на запис в .index файл
Offset int // (заповнені одразу)
LowerTimestamp uint32 // (заповнені одразу)
Data []byte // поточні дані index сторінки (заповнені одразу)
Filled []IndexPage // пусто
}
func appendIndexRecord(data []byte, timestamp uint32, pageNo uint32) []byte {
var tmp = []byte{
0, 0, 0, 0, // timestamp
0, 0, 0, 0, // pageNo
}
bin.PutUint32(tmp, timestamp)
bin.PutUint32(tmp[4:], pageNo)
return append(data, tmp...)
}
type ChunksToDataPageReq struct {
PrevPageNo uint32
Timestamps [][]byte // chunks
TimestampsSize uint16
Values [][]byte // chunks
ValuesSize uint16
}
func ChunksToDataPage(buf []byte, req ChunksToDataPageReq) {
var (
remainingSize = int(req.TimestampsSize)
pos = 0
)
for _, chunk := range req.Timestamps {
if remainingSize >= len(chunk) {
copy(buf[pos:], chunk)
remainingSize -= len(chunk)
pos += len(chunk)
} else {
copy(buf[pos:], chunk[:remainingSize])
break
}
}
remainingSize = int(req.ValuesSize)
pos = int(req.TimestampsSize)
for _, chunk := range req.Values {
if remainingSize >= len(chunk) {
copy(buf[pos:], chunk)
remainingSize -= len(chunk)
pos += len(chunk)
} else {
copy(buf[pos:], chunk[:remainingSize])
break
}
}
bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
bin.PutUint32(buf[prevPageIdx:], req.PrevPageNo)
bin.PutUint32(buf[dataCRC32Idx:], calcChecksum(buf[:dataCRC32Idx]))
}
func DataToIndexPage(buf []byte, data []byte, lastLevel bool) {
copy(buf, data)
bin.PutUint16(buf[indexRecordsQtyIdx:], uint16(len(data)/indexRecordSize))
if lastLevel {
buf[isLastLevelIdx] = 1
}
bin.PutUint32(buf[indexCRC32Idx:], calcChecksum(buf[:indexCRC32Idx]))
}
type Pager interface {
GetPageNumber() (uint32, bool)
}
func AppendDataPagesToTree(pager Pager, levels []*IndexLevel, dataPages []*DataPage) {
for i, d := range dataPages[1:] {
d.PageNo, d.IsReused = pager.GetPageNumber()
d.PrevPageNo = dataPages[i-1].PageNo
}
for _, d := range dataPages {
var (
upTimestamp = d.LowerTimestamp
upPageNo = d.PageNo
levelIdx = 0
)
for {
if levelIdx == len(levels) {
// encode prev page record + up*
levels = append(levels, &IndexLevel{
LowerTimestamp: upTimestamp,
Data: appendIndexRecord(nil, upTimestamp, upPageNo),
})
break
}
level := levels[levelIdx]
if len(level.Data)+recordSize <= payloadSize {
level.Data = appendIndexRecord(level.Data, upTimestamp, upPageNo)
break
}
// в буфері немає місця для додавання нової пари, отже це
// заповнена сторінка
filled := IndexPage{
LowerTimestamp: level.LowerTimestamp,
Data: level.Data,
}
filled.PageNo, filled.IsReused = pager.GetPageNumber()
level.Filled = append(level.Filled, filled)
//
level.LowerTimestamp = upTimestamp
level.Data = appendIndexRecord(nil, upTimestamp, upPageNo)
//
upPageNo = filled.PageNo
upTimestamp = filled.LowerTimestamp
levelIdx++
}
}
}