2026-05-17 12:58:46 +00:00
|
|
|
|
package atree
|
|
|
|
|
|
|
2026-05-21 23:38:52 +03:00
|
|
|
|
import (
|
|
|
|
|
|
"gordenko.dev/dima/qb/bin"
|
|
|
|
|
|
"gordenko.dev/dima/qb/util"
|
|
|
|
|
|
)
|
2026-05-17 12:58:46 +00:00
|
|
|
|
|
|
|
|
|
|
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
|
2026-05-21 23:38:52 +03:00
|
|
|
|
Checksum uint32
|
2026-05-17 12:58:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type IndexPage struct {
|
|
|
|
|
|
LowerTimestamp uint32
|
|
|
|
|
|
Data []byte
|
|
|
|
|
|
PageNo uint32
|
|
|
|
|
|
IsReused bool
|
2026-05-21 23:38:52 +03:00
|
|
|
|
Checksum uint32
|
2026-05-17 12:58:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
type IndexLevel struct {
|
|
|
|
|
|
// вже записані дані (лише для першої в списку індексної сторінки, беремо із _metric)
|
|
|
|
|
|
// дані потрібні, якщо сторінка буде заповнена і піде на запис в .index файл
|
2026-05-21 23:38:52 +03:00
|
|
|
|
Offset int // (заповнені одразу)
|
|
|
|
|
|
LowerTimestamp uint32 // (заповнені одразу)
|
|
|
|
|
|
Data []byte // поточні дані index сторінки (заповнені одразу)
|
|
|
|
|
|
Filled []*IndexPage // пусто
|
2026-05-17 12:58:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2026-05-21 23:38:52 +03:00
|
|
|
|
TimestampsSize int
|
2026-05-17 12:58:46 +00:00
|
|
|
|
Values [][]byte // chunks
|
2026-05-21 23:38:52 +03:00
|
|
|
|
ValuesSize int
|
2026-05-17 12:58:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-21 23:38:52 +03:00
|
|
|
|
// return checksum
|
|
|
|
|
|
func ChunksToDataPage(buf []byte, req ChunksToDataPageReq) (checksum uint32) {
|
2026-05-17 12:58:46 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-21 23:38:52 +03:00
|
|
|
|
bin.PutUint16(buf[timestampsSizeIdx:], uint16(req.TimestampsSize))
|
|
|
|
|
|
bin.PutUint16(buf[valuesSizeIdx:], uint16(req.ValuesSize))
|
2026-05-17 12:58:46 +00:00
|
|
|
|
bin.PutUint32(buf[prevPageIdx:], req.PrevPageNo)
|
2026-05-21 23:38:52 +03:00
|
|
|
|
|
|
|
|
|
|
checksum = util.CalcChecksum(buf[:dataCRC32Idx])
|
|
|
|
|
|
bin.PutUint32(buf[dataCRC32Idx:], checksum)
|
|
|
|
|
|
return
|
2026-05-17 12:58:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-21 23:38:52 +03:00
|
|
|
|
func DataToIndexPage(buf []byte, data []byte, lastLevel bool) (checksum uint32) {
|
2026-05-17 12:58:46 +00:00
|
|
|
|
copy(buf, data)
|
|
|
|
|
|
bin.PutUint16(buf[indexRecordsQtyIdx:], uint16(len(data)/indexRecordSize))
|
|
|
|
|
|
if lastLevel {
|
|
|
|
|
|
buf[isLastLevelIdx] = 1
|
|
|
|
|
|
}
|
2026-05-21 23:38:52 +03:00
|
|
|
|
checksum = util.CalcChecksum(buf[:indexCRC32Idx])
|
|
|
|
|
|
bin.PutUint32(buf[indexCRC32Idx:], checksum)
|
|
|
|
|
|
return
|
2026-05-17 12:58:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Pager interface {
|
|
|
|
|
|
GetPageNumber() (uint32, bool)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-21 23:38:52 +03:00
|
|
|
|
// fix - reduce leves
|
2026-05-17 12:58:46 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
// в буфері немає місця для додавання нової пари, отже це
|
|
|
|
|
|
// заповнена сторінка
|
2026-05-21 23:38:52 +03:00
|
|
|
|
filled := &IndexPage{
|
2026-05-17 12:58:46 +00:00
|
|
|
|
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++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|