package storage import ( "errors" "fmt" bin "gordenko.dev/dima/bin/little" "gordenko.dev/dima/qb" ) type BackwardCursor struct { metricType qb.MetricType fracDigits byte fetchDataPage func(uint32) ([]byte, error) releasePage func(uint32) pageNo uint32 pageData []byte timestampDecompressor qb.TimestampDecompressor valueDecompressor qb.ValueDecompressor } type BackwardCursorOptions struct { MetricType qb.MetricType FracDigits byte PageNo uint32 PageData []byte FetchDataPage func(uint32) ([]byte, error) ReleasePage func(uint32) } func NewBackwardCursor(opt BackwardCursorOptions) (*BackwardCursor, error) { switch opt.MetricType { case qb.Instant, qb.Cumulative: // ok default: return nil, fmt.Errorf("MetricType option has wrong value: %d", opt.MetricType) } if opt.FracDigits > qb.MaxFracDigits { return nil, errors.New("FracDigits option is required") } if opt.FetchDataPage == nil { return nil, errors.New("FetchDataPage option is required") } if opt.ReleasePage == nil { return nil, errors.New("ReleasePage 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, fetchDataPage: opt.FetchDataPage, releasePage: opt.ReleasePage, pageNo: opt.PageNo, pageData: opt.PageData, } var err error s.timestampDecompressor, err = CreateTimestampDecompressor(s.pageData) if err != nil { return nil, fmt.Errorf("CreateTimestampDecompressor: %s", err) } s.valueDecompressor, err = CreateValueDecompressor(s.pageData, s.metricType, s.fracDigits) if err != nil { return nil, fmt.Errorf("CreateValueDecompressor: %s", 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.releasePage(s.pageNo) s.pageNo = prevPageNo s.pageData, err = s.fetchDataPage(s.pageNo) if err != nil { return 0, 0, false, fmt.Errorf("fetchDataPage(%d): %s", s.pageNo, err) } s.timestampDecompressor, err = CreateTimestampDecompressor(s.pageData) if err != nil { return 0, 0, false, fmt.Errorf("CreateTimestampDecompressor: %s", err) } s.valueDecompressor, err = CreateValueDecompressor(s.pageData, s.metricType, s.fracDigits) if err != nil { return 0, 0, false, fmt.Errorf("CreateValueDecompressor: %s", 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.releasePage(s.pageNo) } // HELPER //func (s *BackwardCursor) makeDecompressors() error { //}