спеціалізована СУБД для зберігання та обробки показань датчиків та лічильників
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
diploma/atree/cursor.go

187 lines
4.7 KiB

package atree
import (
"errors"
"fmt"
octopus "gordenko.dev/dima/diploma"
"gordenko.dev/dima/diploma/bin"
"gordenko.dev/dima/diploma/enc"
)
type BackwardCursor struct {
metricType octopus.MetricType
fracDigits byte
atree *Atree
pageNo uint32
pageData []byte
timestampDecompressor octopus.TimestampDecompressor
valueDecompressor octopus.ValueDecompressor
}
type BackwardCursorOptions struct {
MetricType octopus.MetricType
FracDigits byte
PageNo uint32
PageData []byte
Atree *Atree
}
func NewBackwardCursor(opt BackwardCursorOptions) (*BackwardCursor, error) {
switch opt.MetricType {
case octopus.Instant, octopus.Cumulative:
// ok
default:
return nil, fmt.Errorf("MetricType option has wrong value: %d", opt.MetricType)
}
if opt.FracDigits > octopus.MaxFracDigits {
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
done bool
err error
)
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.releaseDataPage(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)
}
return timestamp, value, false, nil
}
func (s *BackwardCursor) Close() {
s.atree.releaseDataPage(s.pageNo)
}
// HELPER
func (s *BackwardCursor) makeDecompressors() error {
timestampsPayloadSize := bin.GetUint16(s.pageData[timestampsSizeIdx:])
valuesPayloadSize := bin.GetUint16(s.pageData[valuesSizeIdx:])
payloadSize := timestampsPayloadSize + valuesPayloadSize
if payloadSize > dataFooterIdx {
return fmt.Errorf("corrupted data page %d: timestamps + values size %d gt payload size",
s.pageNo, payloadSize)
}
s.timestampDecompressor = enc.NewReverseTimeDeltaOfDeltaDecompressor(
s.pageData[:timestampsPayloadSize],
)
vbuf := s.pageData[timestampsPayloadSize : timestampsPayloadSize+valuesPayloadSize]
switch s.metricType {
case octopus.Instant:
s.valueDecompressor = enc.NewReverseInstantDeltaDecompressor(
vbuf, s.fracDigits)
case octopus.Cumulative:
s.valueDecompressor = enc.NewReverseCumulativeDeltaDecompressor(
vbuf, s.fracDigits)
default:
return fmt.Errorf("bug: wrong metricType %d", s.metricType)
}
return nil
}
func makeDecompressors(pageData []byte, metricType octopus.MetricType, fracDigits byte) (
octopus.TimestampDecompressor, octopus.ValueDecompressor, error,
) {
timestampsPayloadSize := bin.GetUint16(pageData[timestampsSizeIdx:])
valuesPayloadSize := bin.GetUint16(pageData[valuesSizeIdx:])
payloadSize := timestampsPayloadSize + valuesPayloadSize
if payloadSize > dataFooterIdx {
return nil, nil, fmt.Errorf("corrupted: timestamps + values size %d > payload size",
payloadSize)
}
timestampDecompressor := enc.NewReverseTimeDeltaOfDeltaDecompressor(
pageData[:timestampsPayloadSize],
)
vbuf := pageData[timestampsPayloadSize : timestampsPayloadSize+valuesPayloadSize]
var valueDecompressor octopus.ValueDecompressor
switch metricType {
case octopus.Instant:
valueDecompressor = enc.NewReverseInstantDeltaDecompressor(
vbuf, fracDigits)
case octopus.Cumulative:
valueDecompressor = enc.NewReverseCumulativeDeltaDecompressor(
vbuf, fracDigits)
default:
return nil, nil, fmt.Errorf("bug: wrong metricType %d", metricType)
}
return timestampDecompressor, valueDecompressor, nil
}