Files
qb/atree/cursor.go

188 lines
4.6 KiB
Go
Raw Normal View History

2026-02-10 14:02:11 +00:00
package atree
import (
"errors"
"fmt"
2026-06-15 05:32:15 +03:00
"gordenko.dev/dima/bin"
2026-05-31 20:01:28 +00:00
"gordenko.dev/dima/qb"
2026-02-10 14:02:11 +00:00
)
type BackwardCursor struct {
2026-05-31 20:01:28 +00:00
metricType qb.MetricType
2026-02-10 14:02:11 +00:00
fracDigits byte
atree *Atree
pageNo uint32
pageData []byte
2026-05-31 20:01:28 +00:00
timestampDecompressor qb.TimestampDecompressor
valueDecompressor qb.ValueDecompressor
2026-02-10 14:02:11 +00:00
}
type BackwardCursorOptions struct {
2026-05-31 20:01:28 +00:00
MetricType qb.MetricType
2026-02-10 14:02:11 +00:00
FracDigits byte
PageNo uint32
PageData []byte
Atree *Atree
}
func NewBackwardCursor(opt BackwardCursorOptions) (*BackwardCursor, error) {
switch opt.MetricType {
2026-05-31 20:01:28 +00:00
case qb.Instant, qb.Cumulative:
2026-02-10 14:02:11 +00:00
// ok
default:
return nil, fmt.Errorf("MetricType option has wrong value: %d", opt.MetricType)
}
2026-05-31 20:01:28 +00:00
if opt.FracDigits > qb.MaxFracDigits {
2026-02-10 14:02:11 +00:00
return nil, errors.New("FracDigits option is required")
}
if opt.Atree == nil {
return nil, errors.New("Atree option is required")
}
if opt.PageNo == 0 {
return nil, errors.New("PageNo option is required")
}
if len(opt.PageData) == 0 {
return nil, errors.New("PageData option is required")
}
s := &BackwardCursor{
metricType: opt.MetricType,
fracDigits: opt.FracDigits,
atree: opt.Atree,
pageNo: opt.PageNo,
pageData: opt.PageData,
}
err := s.makeDecompressors()
if err != nil {
return nil, err
}
return s, nil
}
// timestamp, value, done, error
func (s *BackwardCursor) Prev() (uint32, float64, bool, error) {
var (
timestamp uint32
value float64
2026-06-10 06:18:45 +03:00
//done bool
//err error
2026-02-10 14:02:11 +00:00
)
2026-06-10 06:18:45 +03:00
// timestamp, done = s.timestampDecompressor.NextValue()
// if !done {
// value, done = s.valueDecompressor.NextValue()
// if done {
// return 0, 0, false,
// fmt.Errorf("corrupted data page %d: has timestamp, no value",
// s.pageNo)
// }
// return timestamp, value, false, nil
// }
// prevPageNo, _ := bin.GetUint32(s.pageData[prevPageIdx:])
// if prevPageNo == 0 {
// return 0, 0, true, nil
// }
// s.atree.releasePage(s.pageNo)
// s.pageNo = prevPageNo
// s.pageData, err = s.atree.fetchDataPage(s.pageNo)
// if err != nil {
// return 0, 0, false, fmt.Errorf("atree.fetchDataPage(%d): %s", s.pageNo, err)
// }
// err = s.makeDecompressors()
// if err != nil {
// return 0, 0, false, err
// }
// timestamp, done = s.timestampDecompressor.NextValue()
// if done {
// return 0, 0, false,
// fmt.Errorf("corrupted data page %d: no timestamps",
// s.pageNo)
// }
// value, done = s.valueDecompressor.NextValue()
// if done {
// return 0, 0, false,
// fmt.Errorf("corrupted data page %d: no values",
// s.pageNo)
// }
2026-02-10 14:02:11 +00:00
return timestamp, value, false, nil
}
func (s *BackwardCursor) Close() {
2026-02-23 19:37:05 +00:00
s.atree.releasePage(s.pageNo)
2026-02-10 14:02:11 +00:00
}
// HELPER
func (s *BackwardCursor) makeDecompressors() error {
2026-06-15 05:32:15 +03:00
timestampsSize, _ := bin.GetUint16(s.pageData[timestampsSizeIdx:])
valuesSize, _ := bin.GetUint16(s.pageData[valuesSizeIdx:])
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
payloadSize := timestampsSize + valuesSize
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
if payloadSize > dataFooterIdx {
return fmt.Errorf("corrupted data page %d: timestamps + values size %d gt payload size",
s.pageNo, payloadSize)
}
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
s.timestampDecompressor = enc.NewTimeDeltaDecompressor(
s.pageData[:timestampsSize],
)
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
vbuf := s.pageData[timestampsSize : timestampsSize+valuesSize]
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
switch s.metricType {
case qb.Instant:
s.valueDecompressor = enc.NewInstantDeltaDecompressor(
vbuf, s.fracDigits)
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
case qb.Cumulative:
s.valueDecompressor = enc.NewCumulativeDeltaDecompressor(
vbuf, s.fracDigits)
2026-02-10 14:02:11 +00:00
2026-06-15 05:32:15 +03:00
default:
return fmt.Errorf("bug: wrong metricType %d", s.metricType)
}
2026-02-10 14:02:11 +00:00
return nil
}
2026-06-15 05:32:15 +03:00
// func makeDecompressors(pageData []byte, metricType qb.MetricType, fracDigits byte) (
// qb.TimestampDecompressor, qb.ValueDecompressor, error,
// ) {
// timestampsSize, _ := bin.GetUint16(pageData[timestampsSizeIdx:])
// valuesSize, _ := bin.GetUint16(pageData[valuesSizeIdx:])
// payloadSize := timestampsSize + valuesSize
// if payloadSize > dataFooterIdx {
// return nil, nil, fmt.Errorf("corrupted: timestamps + values size %d > payload size",
// payloadSize)
// }
// timestampDecompressor := enc.NewTimeDeltaDecompressor(
// pageData[:timestampsSize],
// )
// vbuf := pageData[timestampsSize : timestampsSize+valuesSize]
// var valueDecompressor qb.ValueDecompressor
// switch metricType {
// case qb.Instant:
// valueDecompressor = enc.NewInstantDeltaDecompressor(
// vbuf, fracDigits)
// case qb.Cumulative:
// valueDecompressor = enc.NewCumulativeDeltaDecompressor(
// vbuf, fracDigits)
// default:
// return nil, nil, fmt.Errorf("bug: wrong metricType %d", metricType)
// }
//return timestampDecompressor, valueDecompressor, nil
// return nil, nil, nil
// }