2026-06-10 06:18:45 +03:00
|
|
|
|
package storage
|
2026-05-21 23:38:52 +03:00
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"bytes"
|
|
|
|
|
|
|
|
|
|
|
|
bin "gordenko.dev/dima/bin/little"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const recordSize = 8
|
|
|
|
|
|
const chunkSize = 24
|
|
|
|
|
|
|
|
|
|
|
|
func copyPayloadFromChunks(w *bytes.Buffer, chunks [][]byte, size int, offset int) {
|
|
|
|
|
|
bin.WriteUint16(w, uint16(size))
|
|
|
|
|
|
var (
|
|
|
|
|
|
chunkIdx = offset / chunkSize
|
|
|
|
|
|
byteIdx = offset % chunkSize
|
|
|
|
|
|
)
|
|
|
|
|
|
for _, chunk := range chunks[chunkIdx:] {
|
|
|
|
|
|
available := len(chunk) - byteIdx
|
|
|
|
|
|
if available <= size {
|
|
|
|
|
|
w.Write(chunk[byteIdx:])
|
|
|
|
|
|
size -= available
|
|
|
|
|
|
if size == 0 {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
byteIdx = 0
|
|
|
|
|
|
} else {
|
|
|
|
|
|
w.Write(chunk[byteIdx : byteIdx+size])
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////
|
|
|
|
|
|
|
2026-06-05 19:43:01 +00:00
|
|
|
|
// type DataPage struct {
|
|
|
|
|
|
// PageNo uint32
|
|
|
|
|
|
// Reused bool
|
|
|
|
|
|
// PrevPageNo uint32
|
|
|
|
|
|
// Checksum uint32
|
|
|
|
|
|
// Timestamps []byte
|
|
|
|
|
|
// Values []byte
|
|
|
|
|
|
// }
|
2026-05-21 23:38:52 +03:00
|
|
|
|
|
2026-06-05 19:43:01 +00:00
|
|
|
|
// type IndexPage struct {
|
|
|
|
|
|
// PageNo uint32
|
|
|
|
|
|
// Reused bool
|
|
|
|
|
|
// Checksum uint32
|
|
|
|
|
|
// Records []byte
|
|
|
|
|
|
// }
|
2026-05-21 23:38:52 +03:00
|
|
|
|
|
2026-06-05 19:43:01 +00:00
|
|
|
|
// type IndexLevel struct {
|
|
|
|
|
|
// Pages []IndexPage
|
|
|
|
|
|
// Records []byte // unfilled
|
|
|
|
|
|
// }
|
2026-05-21 23:38:52 +03:00
|
|
|
|
|
|
|
|
|
|
// Для декодінга
|
2026-06-05 19:43:01 +00:00
|
|
|
|
// type TxAppendedMeasures struct {
|
|
|
|
|
|
// MetricID uint32
|
|
|
|
|
|
// DataPages []DataPage
|
|
|
|
|
|
// // offsets потрібні тому що дані не додаються в кінець, а перезаписують кілька
|
|
|
|
|
|
// // останніх байтів unfilled даних. Потрібно для коректного recovery.
|
|
|
|
|
|
// // Якщо є DataPages, то застосовуються для 1-ї Data сторінки, інакше - до timestamps і values (unfilled)
|
|
|
|
|
|
// TimestampsOffset int
|
|
|
|
|
|
// ValuesOffset int
|
|
|
|
|
|
// Timestamps []byte // unfilled
|
|
|
|
|
|
// Values []byte // unfilled
|
|
|
|
|
|
// IndexLevels []IndexLevel
|
|
|
|
|
|
// }
|
2026-05-21 23:38:52 +03:00
|
|
|
|
|
2026-06-05 19:43:01 +00:00
|
|
|
|
// func (s *TxAppendedMeasures) Read(r *bytes.Buffer) (err error) {
|
|
|
|
|
|
// s.MetricID, err = bin.ReadUint32(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// dataPagesQty, err := bin.ReadVarSize(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// for range dataPagesQty {
|
|
|
|
|
|
// var (
|
|
|
|
|
|
// p DataPage
|
|
|
|
|
|
// timestampsSize, valuesSize int
|
|
|
|
|
|
// //b byte
|
|
|
|
|
|
// )
|
|
|
|
|
|
// p.PageNo, err = bin.ReadUint32(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// p.Reused, err = bin.ReadBool(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// p.PrevPageNo, err = bin.ReadUint32(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// p.Checksum, err = bin.ReadUint32(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// timestampsSize, err = bin.ReadUint16AsInt(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// p.Timestamps, err = bin.ReadN(r, timestampsSize)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// valuesSize, err = bin.ReadUint16AsInt(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// p.Values, err = bin.ReadN(r, valuesSize)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// s.DataPages = append(s.DataPages, p)
|
|
|
|
|
|
// }
|
|
|
|
|
|
// s.TimestampsOffset, err = bin.ReadUint16AsInt(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// s.ValuesOffset, err = bin.ReadUint16AsInt(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// var (
|
|
|
|
|
|
// timestampsSize, valuesSize int
|
|
|
|
|
|
// )
|
|
|
|
|
|
// timestampsSize, err = bin.ReadVarSize(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// s.Timestamps, err = bin.ReadN(r, timestampsSize)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// valuesSize, err = bin.ReadVarSize(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// s.Values, err = bin.ReadN(r, valuesSize)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// if len(s.DataPages) == 0 {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// indexLevelsQty, err := bin.ReadVarSize(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// for range indexLevelsQty {
|
|
|
|
|
|
// var (
|
|
|
|
|
|
// indexPagesQty int
|
|
|
|
|
|
// level IndexLevel
|
|
|
|
|
|
// recordsQty int
|
|
|
|
|
|
// )
|
|
|
|
|
|
// indexPagesQty, err = bin.ReadVarSize(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// for range indexPagesQty {
|
|
|
|
|
|
// var (
|
|
|
|
|
|
// p IndexPage
|
|
|
|
|
|
// recordsQty int
|
|
|
|
|
|
// )
|
|
|
|
|
|
// p.PageNo, err = bin.ReadUint32(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// p.Reused, err = bin.ReadBool(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// p.Checksum, err = bin.ReadUint32(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// recordsQty, err = bin.ReadVarSize(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// p.Records, err = bin.ReadN(r, recordsQty*recordSize)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// level.Pages = append(level.Pages, p)
|
|
|
|
|
|
// }
|
|
|
|
|
|
// recordsQty, err = bin.ReadVarSize(r)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// level.Records, err = bin.ReadN(r, recordsQty*recordSize)
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|
|
|
|
|
|
// s.IndexLevels = append(s.IndexLevels, level)
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return
|
|
|
|
|
|
// }
|