wp
This commit is contained in:
412
atree/atree.go
412
atree/atree.go
@@ -8,7 +8,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
diploma "gordenko.dev/dima/qb"
|
|
||||||
"gordenko.dev/dima/qb/bin"
|
"gordenko.dev/dima/qb/bin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,8 +18,8 @@ const (
|
|||||||
filePerm = 0770
|
filePerm = 0770
|
||||||
|
|
||||||
// common
|
// common
|
||||||
crc32Idx = PageSize - 4
|
crc32Idx = PageSize - 4
|
||||||
pageType = PageSize - 5
|
pageTypeIdx = PageSize - 5
|
||||||
|
|
||||||
// index page
|
// index page
|
||||||
indexRecordsQtyIdx = PageSize - 8
|
indexRecordsQtyIdx = PageSize - 8
|
||||||
@@ -73,16 +72,16 @@ type _page struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Atree struct {
|
type Atree struct {
|
||||||
freelist FreeList
|
|
||||||
file *os.File
|
file *os.File
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
freelist FreeList
|
||||||
allocatedPagesQty uint32
|
allocatedPagesQty uint32
|
||||||
pages map[uint32]*_page
|
pages map[uint32]*_page
|
||||||
pageWaits map[uint32][]chan readResult
|
pageWaits map[uint32][]chan readResult
|
||||||
pagesToRead []uint32
|
pagesToRead []uint32
|
||||||
readSignalCh chan struct{}
|
readSignalCh chan struct{}
|
||||||
writeSignalCh chan struct{}
|
writeSignalCh chan struct{}
|
||||||
writeTasksQueue []WriteTask
|
//writeTasksQueue []WriteTask
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
@@ -131,7 +130,7 @@ func New(opt Options) (*Atree, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tree := &Atree{
|
tree := &Atree{
|
||||||
//freelist: opt.FreeList,
|
freelist: opt.FreeList,
|
||||||
file: file,
|
file: file,
|
||||||
allocatedPagesQty: allocatedPagesQty,
|
allocatedPagesQty: allocatedPagesQty,
|
||||||
pages: make(map[uint32]*_page),
|
pages: make(map[uint32]*_page),
|
||||||
@@ -144,7 +143,7 @@ func New(opt Options) (*Atree, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Atree) Run() {
|
func (s *Atree) Run() {
|
||||||
go s.pageWriter()
|
//go s.pageWriter()
|
||||||
go s.pageReader()
|
go s.pageReader()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +160,7 @@ func (s *Atree) findDataPage(rootPageNo uint32, timestamp uint32) (uint32, []byt
|
|||||||
foundPageNo := findPageNo(buf, timestamp)
|
foundPageNo := findPageNo(buf, timestamp)
|
||||||
s.releasePage(indexPageNo)
|
s.releasePage(indexPageNo)
|
||||||
|
|
||||||
|
// fix
|
||||||
if buf[isDataPageNumbersIdx] == 1 {
|
if buf[isDataPageNumbersIdx] == 1 {
|
||||||
buf, err := s.fetchDataPage(foundPageNo)
|
buf, err := s.fetchDataPage(foundPageNo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -173,20 +173,20 @@ func (s *Atree) findDataPage(rootPageNo uint32, timestamp uint32) (uint32, []byt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type pathLeg struct {
|
type PathLeg struct {
|
||||||
PageNo uint32
|
PageNo uint32
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type pathToDataPage struct {
|
type PathToDataPage struct {
|
||||||
Legs []pathLeg
|
Legs []PathLeg
|
||||||
LastPageNo uint32
|
LastPageNo uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err error) {
|
func (s *Atree) FindPathToLastPage(rootPageNo uint32) (_ PathToDataPage, err error) {
|
||||||
var (
|
var (
|
||||||
pageNo = rootPageNo
|
pageNo = rootPageNo
|
||||||
legs []pathLeg
|
legs []PathLeg
|
||||||
)
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -197,7 +197,7 @@ func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
legs = append(legs, pathLeg{
|
legs = append(legs, PathLeg{
|
||||||
PageNo: pageNo,
|
PageNo: pageNo,
|
||||||
Data: buf,
|
Data: buf,
|
||||||
// childIdx не нужен
|
// childIdx не нужен
|
||||||
@@ -205,8 +205,9 @@ func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err err
|
|||||||
|
|
||||||
foundPageNo := getLastPageNo(buf)
|
foundPageNo := getLastPageNo(buf)
|
||||||
|
|
||||||
|
// fix
|
||||||
if buf[isDataPageNumbersIdx] == 1 {
|
if buf[isDataPageNumbersIdx] == 1 {
|
||||||
return pathToDataPage{
|
return PathToDataPage{
|
||||||
Legs: legs,
|
Legs: legs,
|
||||||
LastPageNo: foundPageNo,
|
LastPageNo: foundPageNo,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -218,26 +219,19 @@ func (s *Atree) findPathToLastPage(rootPageNo uint32) (_ pathToDataPage, err err
|
|||||||
|
|
||||||
// APPEND DATA PAGE
|
// APPEND DATA PAGE
|
||||||
|
|
||||||
|
//MetricID uint32
|
||||||
|
//Timestamp uint32
|
||||||
|
//Value float64
|
||||||
|
//RootPageNo uint32
|
||||||
|
//PrevPageNo uint32
|
||||||
|
|
||||||
type AppendDataPageReq struct {
|
type AppendDataPageReq struct {
|
||||||
MetricID uint32
|
|
||||||
Timestamp uint32
|
|
||||||
Value float64
|
|
||||||
Since uint32
|
Since uint32
|
||||||
RootPageNo uint32
|
|
||||||
PrevPageNo uint32
|
|
||||||
TimestampsChunks [][]byte
|
TimestampsChunks [][]byte
|
||||||
TimestampsSize uint16
|
TimestampsSize uint16
|
||||||
ValuesChunks [][]byte
|
ValuesChunks [][]byte
|
||||||
ValuesSize uint16
|
ValuesSize uint16
|
||||||
}
|
}
|
||||||
type Report struct {
|
|
||||||
//IsDataPageReused bool
|
|
||||||
//DataPageNo uint32
|
|
||||||
IsRootChanged bool
|
|
||||||
NewRootPageNo uint32
|
|
||||||
//ReusedIndexPages []uint32
|
|
||||||
Pages []PageToWrite
|
|
||||||
}
|
|
||||||
|
|
||||||
// type ChangedPage struct {
|
// type ChangedPage struct {
|
||||||
// PageNo uint32
|
// PageNo uint32
|
||||||
@@ -253,186 +247,165 @@ type Report struct {
|
|||||||
// Тому що є проблема із відновленням FreeList після збою, якщо з нього будуть паралельно
|
// Тому що є проблема із відновленням FreeList після збою, якщо з нього будуть паралельно
|
||||||
// забирати та добавляти номери сторінок інші потоки.
|
// забирати та добавляти номери сторінок інші потоки.
|
||||||
// Це буде працювати, якщо додавання в txlog і маніпуляції із freeList будуть відбуватись в одному потоці
|
// Це буде працювати, якщо додавання в txlog і маніпуляції із freeList будуть відбуватись в одному потоці
|
||||||
func (s *Atree) AppendDataPage(req AppendDataPageReq) (_ Report, err error) {
|
// NotLinkedDataPage - це data сторінка із payload, але без встановленого prevPageNo та без розрахованого CRC32
|
||||||
|
type NotLinkedDataPage struct {
|
||||||
|
Since uint32
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s NotLinkedDataPage) SetPrevPageNo(prevPageNo uint32) {
|
||||||
|
bin.PutUint32(s.Data[prevPageIdx:], prevPageNo)
|
||||||
|
bin.PutUint32(s.Data[crc32Idx:], calcChecksum(s.Data[:crc32Idx]))
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppendDataPagesReq struct {
|
||||||
|
LastPageNo uint32
|
||||||
|
Legs []PathLeg
|
||||||
|
DataPages []NotLinkedDataPage
|
||||||
|
}
|
||||||
|
|
||||||
|
type Report struct {
|
||||||
|
NewRootPageNo uint32
|
||||||
|
LastPageNo uint32
|
||||||
|
Pages []PageToWrite
|
||||||
|
}
|
||||||
|
|
||||||
|
// Можливо atree не треба блокувати при читанні. Я можу створити копії index сторінок,
|
||||||
|
// а потім під 1 мутексом замінити в pages.
|
||||||
|
func (s *Atree) AppendDataPages(req AppendDataPagesReq) Report {
|
||||||
var (
|
var (
|
||||||
pagesToRelease []uint32
|
//pagesToRelease []uint32
|
||||||
report Report
|
newRootPageNo uint32
|
||||||
|
lastPageNo = req.LastPageNo
|
||||||
|
pages []PageToWrite
|
||||||
|
legs = req.Legs
|
||||||
|
changed = make(map[uint32]PathLeg)
|
||||||
)
|
)
|
||||||
|
|
||||||
newDataPage := s.allocPage()
|
for _, p := range req.DataPages {
|
||||||
pagesToRelease = append(pagesToRelease, newDataPage.PageNo)
|
newDataPageNo, isReused := s.allocPageNumber() // alloc only number
|
||||||
|
// pagesToRelease = append(pagesToRelease, newDataPage.PageNo)
|
||||||
|
|
||||||
chunksToDataPage(newDataPage.Data, chunksToDataPageReq{
|
// set prevPageNo
|
||||||
PrevPageNo: req.PrevPageNo,
|
p.SetPrevPageNo(lastPageNo)
|
||||||
TimestampsChunks: req.TimestampsChunks,
|
|
||||||
TimestampsSize: req.TimestampsSize,
|
|
||||||
ValuesChunks: req.ValuesChunks,
|
|
||||||
ValuesSize: req.ValuesSize,
|
|
||||||
})
|
|
||||||
|
|
||||||
report.Pages = append(report.Pages, PageToWrite{
|
pages = append(pages, PageToWrite{
|
||||||
PageNo: newDataPage.PageNo,
|
PageNo: newDataPageNo,
|
||||||
Data: newDataPage.Data,
|
Data: p.Data,
|
||||||
IsReused: newDataPage.IsReused,
|
IsReused: isReused,
|
||||||
})
|
|
||||||
|
|
||||||
// redoWriter, err := NewWriter(WriterOptions{
|
|
||||||
// MetricID: req.MetricID,
|
|
||||||
// Timestamp: req.Timestamp,
|
|
||||||
// Value: req.Value,
|
|
||||||
// IsDataPageReused: newDataPage.IsReused,
|
|
||||||
// DataPageNo: newDataPage.PageNo,
|
|
||||||
// Page: newDataPage.Data,
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
if req.RootPageNo > 0 {
|
|
||||||
var path pathToDataPage
|
|
||||||
path, err = s.findPathToLastPage(req.RootPageNo)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, leg := range path.Legs {
|
|
||||||
pagesToRelease = append(pagesToRelease, leg.PageNo)
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.LastPageNo != req.PrevPageNo {
|
|
||||||
diploma.Abort(
|
|
||||||
diploma.WrongPrevPageNo,
|
|
||||||
fmt.Errorf("bug: last pageNo %d in tree != prev pageNo %d in _metric",
|
|
||||||
path.LastPageNo, req.PrevPageNo),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
newPageNo := newDataPage.PageNo
|
|
||||||
lastIdx := len(path.Legs) - 1
|
|
||||||
|
|
||||||
for legIdx := lastIdx; legIdx >= 0; legIdx-- {
|
|
||||||
leg := path.Legs[legIdx]
|
|
||||||
|
|
||||||
ok := appendPair(leg.Data, req.Since, newPageNo)
|
|
||||||
if ok {
|
|
||||||
// index
|
|
||||||
report.Pages = append(report.Pages, PageToWrite{
|
|
||||||
PageNo: leg.PageNo,
|
|
||||||
Data: leg.Data,
|
|
||||||
})
|
|
||||||
// err = redoWriter.AppendIndexPage(leg.PageNo, leg.Data, 0)
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
newIndexPage := s.allocPage()
|
|
||||||
pagesToRelease = append(pagesToRelease, newIndexPage.PageNo)
|
|
||||||
appendPair(newIndexPage.Data, req.Since, newPageNo)
|
|
||||||
// ставлю мітку що всі pageNo на сторінці - це data pageNo
|
|
||||||
if legIdx == lastIdx {
|
|
||||||
newIndexPage.Data[isDataPageNumbersIdx] = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// flags = 0
|
|
||||||
// if newIndexPage.IsReused {
|
|
||||||
// flags |= FlagReused
|
|
||||||
// }
|
|
||||||
report.Pages = append(report.Pages, PageToWrite{
|
|
||||||
PageNo: newIndexPage.PageNo,
|
|
||||||
Data: newIndexPage.Data,
|
|
||||||
IsReused: newIndexPage.IsReused,
|
|
||||||
})
|
|
||||||
// err = redoWriter.AppendIndexPage(newIndexPage.PageNo, newIndexPage.Data, flags)
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
newPageNo = newIndexPage.PageNo
|
|
||||||
|
|
||||||
if legIdx == 0 {
|
|
||||||
newRoot := s.allocPage()
|
|
||||||
pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
|
||||||
appendPair(newRoot.Data, getSince(leg.Data), leg.PageNo) // old rootPageNo
|
|
||||||
appendPair(newRoot.Data, req.Since, newIndexPage.PageNo)
|
|
||||||
|
|
||||||
// Фиксирую новый root в REDO логе
|
|
||||||
|
|
||||||
report.Pages = append(report.Pages, PageToWrite{
|
|
||||||
PageNo: newRoot.PageNo,
|
|
||||||
Data: newRoot.Data,
|
|
||||||
IsReused: newRoot.IsReused,
|
|
||||||
})
|
|
||||||
report.NewRootPageNo = newRoot.PageNo
|
|
||||||
// flags = FlagNewRoot
|
|
||||||
// if newRoot.IsReused {
|
|
||||||
// flags |= FlagReused
|
|
||||||
// }
|
|
||||||
// err = redoWriter.AppendIndexPage(newRoot.PageNo, newRoot.Data, flags)
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newRoot := s.allocPage()
|
|
||||||
pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
|
||||||
newRoot.Data[isDataPageNumbersIdx] = 1
|
|
||||||
appendPair(newRoot.Data, req.Since, newDataPage.PageNo)
|
|
||||||
|
|
||||||
report.Pages = append(report.Pages, PageToWrite{
|
|
||||||
PageNo: newRoot.PageNo,
|
|
||||||
Data: newRoot.Data,
|
|
||||||
IsReused: newRoot.IsReused,
|
|
||||||
})
|
})
|
||||||
report.NewRootPageNo = newRoot.PageNo
|
|
||||||
// flags = FlagNewRoot
|
// FIX - після додавання index page потрібно модифікувати path,
|
||||||
// if newRoot.IsReused {
|
// або будувати щось типу дерева знизу вверх
|
||||||
// flags |= FlagReused
|
|
||||||
// }
|
if len(legs) > 0 {
|
||||||
// err = redoWriter.AppendIndexPage(newRoot.PageNo, newRoot.Data, flags)
|
newPageNo := newDataPageNo
|
||||||
// if err != nil {
|
lastIdx := len(legs) - 1
|
||||||
// return
|
|
||||||
|
for legIdx := lastIdx; legIdx >= 0; legIdx-- {
|
||||||
|
leg := req.Legs[legIdx]
|
||||||
|
ok := appendPair(leg.Data, p.Since, newPageNo)
|
||||||
|
if ok {
|
||||||
|
// index FIX
|
||||||
|
// потрібно запам'ятати змінені сторінки, але їх можуть змінювати
|
||||||
|
// кілька ітерацій, тому додавати в Pages не можна.
|
||||||
|
// на індексній сторінці достатньо місця. Запис вставлено.
|
||||||
|
changed[leg.PageNo] = leg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// на індексній сторінці НЕ достатньо місця. Створюю нову.
|
||||||
|
newIndexPage := s.allocPage()
|
||||||
|
//pagesToRelease = append(pagesToRelease, newIndexPage.PageNo)
|
||||||
|
appendPair(newIndexPage.Data, p.Since, newPageNo)
|
||||||
|
// ставлю мітку що всі pageNo на сторінці - це data pageNo
|
||||||
|
// fix - єдина оптимізація від існування isDataPageNumbersIdx - getAllPages не завантажує data pages
|
||||||
|
// if legIdx == lastIdx {
|
||||||
|
// newIndexPage.Data[isDataPageNumbersIdx] = 1
|
||||||
|
// }
|
||||||
|
pages = append(pages, PageToWrite{
|
||||||
|
PageNo: newIndexPage.PageNo,
|
||||||
|
Data: newIndexPage.Data,
|
||||||
|
IsReused: newIndexPage.IsReused,
|
||||||
|
})
|
||||||
|
// замінюю крок в path на новий
|
||||||
|
legs[legIdx] = PathLeg{
|
||||||
|
PageNo: newIndexPage.PageNo,
|
||||||
|
Data: newIndexPage.Data,
|
||||||
|
}
|
||||||
|
//
|
||||||
|
newPageNo = newIndexPage.PageNo
|
||||||
|
|
||||||
|
if legIdx == 0 {
|
||||||
|
newRoot := s.allocPage()
|
||||||
|
//pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
||||||
|
appendPair(newRoot.Data, getSince(leg.Data), leg.PageNo) // old rootPageNo
|
||||||
|
appendPair(newRoot.Data, p.Since, newIndexPage.PageNo)
|
||||||
|
|
||||||
|
// Фиксирую новый root в REDO логе
|
||||||
|
pages = append(pages, PageToWrite{
|
||||||
|
PageNo: newRoot.PageNo,
|
||||||
|
Data: newRoot.Data,
|
||||||
|
IsReused: newRoot.IsReused,
|
||||||
|
})
|
||||||
|
newRootPageNo = newRoot.PageNo
|
||||||
|
// додаю в початок списку кроків нову root сторінку
|
||||||
|
legs = append([]PathLeg{
|
||||||
|
{
|
||||||
|
PageNo: newRoot.PageNo,
|
||||||
|
Data: newRoot.Data,
|
||||||
|
},
|
||||||
|
}, legs...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// індексну root сторінку створюю одразу для першої data сторінки,
|
||||||
|
// root data сторінки, як в B+Tree не буває.
|
||||||
|
newRoot := s.allocPage()
|
||||||
|
//pagesToRelease = append(pagesToRelease, newRoot.PageNo)
|
||||||
|
//newRoot.Data[isDataPageNumbersIdx] = 1
|
||||||
|
appendPair(newRoot.Data, p.Since, newDataPageNo)
|
||||||
|
|
||||||
|
pages = append(pages, PageToWrite{
|
||||||
|
PageNo: newRoot.PageNo,
|
||||||
|
Data: newRoot.Data,
|
||||||
|
IsReused: newRoot.IsReused,
|
||||||
|
})
|
||||||
|
newRootPageNo = newRoot.PageNo
|
||||||
|
// додаю в початок списку кроків нову root сторінку
|
||||||
|
legs = append([]PathLeg{
|
||||||
|
{
|
||||||
|
PageNo: newRoot.PageNo,
|
||||||
|
Data: newRoot.Data,
|
||||||
|
},
|
||||||
|
}, legs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// На данний момен схема - наступна. Всі сторінки - data та index - зафіксовані в кеші.
|
||||||
|
// Отже запис на диск пройде максимально швидко. Після цього ReferenceCount кожної
|
||||||
|
// сторінки зменшиться на 1. Оскільки на метрику утримується XLock, сторінки мають
|
||||||
|
// ReferenceCount = 1 (немає інших читачів).
|
||||||
|
// for _, pageNo := range indexPagesToRelease {
|
||||||
|
// s.releasePage(pageNo)
|
||||||
// }
|
// }
|
||||||
|
lastPageNo = newDataPageNo
|
||||||
|
}
|
||||||
|
for _, leg := range changed {
|
||||||
|
// fix recalc checksum
|
||||||
|
pages = append(pages, PageToWrite{
|
||||||
|
PageNo: leg.PageNo,
|
||||||
|
Data: leg.Data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Report{
|
||||||
|
NewRootPageNo: newRootPageNo,
|
||||||
|
LastPageNo: lastPageNo,
|
||||||
|
Pages: pages,
|
||||||
}
|
}
|
||||||
|
|
||||||
// err = redoWriter.Close()
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// На данний момен схема - наступна. Всі сторінки - data та index - зафіксовані в кеші.
|
|
||||||
// Отже запис на диск пройде максимально швидко. Після цього ReferenceCount кожної
|
|
||||||
// сторінки зменшиться на 1. Оскільки на метрику утримується XLock, сторінки мають
|
|
||||||
// ReferenceCount = 1 (немає інших читачів).
|
|
||||||
// waitCh := make(chan struct{})
|
|
||||||
|
|
||||||
// task := WriteTask{
|
|
||||||
// WaitCh: waitCh,
|
|
||||||
// Pages: report.Pages,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// s.appendWriteTaskToQueue(task)
|
|
||||||
|
|
||||||
// <-waitCh
|
|
||||||
|
|
||||||
// for _, pageNo := range dataPagesToRelease {
|
|
||||||
// s.releasePage(pageNo)
|
|
||||||
// }
|
|
||||||
// for _, pageNo := range indexPagesToRelease {
|
|
||||||
// s.releasePage(pageNo)
|
|
||||||
// }
|
|
||||||
return report, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DELETE
|
// DELETE
|
||||||
|
|
||||||
type PageLists struct {
|
|
||||||
DataPages []uint32
|
|
||||||
IndexPages []uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type Level struct {
|
type Level struct {
|
||||||
PageNo uint32
|
PageNo uint32
|
||||||
PageData []byte
|
PageData []byte
|
||||||
@@ -440,31 +413,29 @@ type Level struct {
|
|||||||
ChildQty int
|
ChildQty int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) {
|
func (s *Atree) GetAllPages(rootPageNo uint32) (_ []uint32, err error) {
|
||||||
var (
|
var (
|
||||||
dataPages []uint32
|
pageNumbers []uint32
|
||||||
indexPages []uint32
|
levels []*Level
|
||||||
levels []*Level
|
|
||||||
)
|
)
|
||||||
|
|
||||||
buf, err := s.fetchIndexPage(rootPageNo)
|
buf, err := s.fetchIndexPage(rootPageNo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("fetchIndexPage(%d): %s", rootPageNo, err)
|
return nil, fmt.Errorf("fetchIndexPage(%d): %s", rootPageNo, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
indexPages = append(indexPages, rootPageNo)
|
pageNumbers = append(pageNumbers, rootPageNo)
|
||||||
|
|
||||||
if buf[isDataPageNumbersIdx] == 1 {
|
// if buf[isDataPageNumbersIdx] == 1 {
|
||||||
pageNumbers := listPageNumbers(buf)
|
// pageNumbers := listPageNumbers(buf)
|
||||||
dataPages = append(dataPages, pageNumbers...)
|
// dataPages = append(dataPages, pageNumbers...)
|
||||||
|
|
||||||
s.releasePage(rootPageNo)
|
// s.releasePage(rootPageNo)
|
||||||
|
|
||||||
return PageLists{
|
// return PageLists{
|
||||||
DataPages: dataPages,
|
// DataPages: dataPages,
|
||||||
IndexPages: indexPages,
|
// IndexPages: indexPages,
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
levels = append(levels, &Level{
|
levels = append(levels, &Level{
|
||||||
PageNo: rootPageNo,
|
PageNo: rootPageNo,
|
||||||
@@ -475,10 +446,7 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
if len(levels) == 0 {
|
if len(levels) == 0 {
|
||||||
return PageLists{
|
return pageNumbers, nil
|
||||||
DataPages: dataPages,
|
|
||||||
IndexPages: indexPages,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastIdx := len(levels) - 1
|
lastIdx := len(levels) - 1
|
||||||
@@ -489,17 +457,15 @@ func (s *Atree) GetAllPages(rootPageNo uint32) (_ PageLists, err error) {
|
|||||||
level.Idx++
|
level.Idx++
|
||||||
|
|
||||||
var buf []byte
|
var buf []byte
|
||||||
buf, err = s.fetchIndexPage(pageNo)
|
buf, err = s.fetchPage(pageNo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("fetchIndexPage(%d): %s", pageNo, err)
|
return nil, fmt.Errorf("fetchPage(%d): %s", pageNo, err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
indexPages = append(indexPages, pageNo)
|
pageNumbers = append(pageNumbers, pageNo)
|
||||||
|
|
||||||
if buf[isDataPageNumbersIdx] == 1 {
|
|
||||||
pageNumbers := listPageNumbers(buf)
|
|
||||||
dataPages = append(dataPages, pageNumbers...)
|
|
||||||
|
|
||||||
|
if buf[pageTypeIdx] == PageTypeData {
|
||||||
|
//pageNumbers := listPageNumbers(buf)
|
||||||
|
//dataPages = append(dataPages, pageNumbers...)
|
||||||
s.releasePage(pageNo)
|
s.releasePage(pageNo)
|
||||||
} else {
|
} else {
|
||||||
levels = append(levels, &Level{
|
levels = append(levels, &Level{
|
||||||
|
|||||||
152
atree/io.go
152
atree/io.go
@@ -34,20 +34,31 @@ func (s *Atree) DeletePages(pageNumbers []uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
|
func (s *Atree) fetchIndexPage(pageNo uint32) ([]byte, error) {
|
||||||
return s.fetchPage(pageNo, PageTypeIndex)
|
buf, err := s.fetchPage(pageNo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf[pageTypeIdx] != PageTypeIndex {
|
||||||
|
return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
|
func (s *Atree) fetchDataPage(pageNo uint32) ([]byte, error) {
|
||||||
return s.fetchPage(pageNo, PageTypeData)
|
buf, err := s.fetchPage(pageNo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if buf[pageTypeIdx] != PageTypeIndex {
|
||||||
|
return nil, fmt.Errorf("wrong pageType %d instead of %d", buf[pageTypeIdx], PageTypeIndex)
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Atree) fetchPage(pageNo uint32, pageTypeIdx byte) ([]byte, error) {
|
func (s *Atree) fetchPage(pageNo uint32) ([]byte, error) {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
p, ok := s.pages[pageNo]
|
p, ok := s.pages[pageNo]
|
||||||
if ok {
|
if ok {
|
||||||
if p.Buf[pageTypeIdx] != pageTypeIdx {
|
|
||||||
return nil, fmt.Errorf("wrong pageType %d instead of %d", p.Buf[pageTypeIdx], pageTypeIdx)
|
|
||||||
}
|
|
||||||
p.ReferenceCount++
|
p.ReferenceCount++
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
return p.Buf, nil
|
return p.Buf, nil
|
||||||
@@ -93,23 +104,28 @@ func (s *Atree) releasePage(pageNo uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Atree) allocPageNumber() (uint32, bool) {
|
||||||
|
// fix
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Atree) allocPage() AllocatedPage {
|
func (s *Atree) allocPage() AllocatedPage {
|
||||||
var (
|
var (
|
||||||
allocated = AllocatedPage{
|
allocated = AllocatedPage{
|
||||||
Data: make([]byte, PageSize),
|
Data: make([]byte, PageSize),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
allocated.PageNo = s.freelist.ReservePage()
|
//allocated.PageNo = s.freelist.ReservePage()
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
if allocated.PageNo > 0 {
|
// if allocated.PageNo > 0 {
|
||||||
allocated.IsReused = true
|
// allocated.IsReused = true
|
||||||
} else {
|
// } else {
|
||||||
if s.allocatedPagesQty == math.MaxUint32 {
|
// if s.allocatedPagesQty == math.MaxUint32 {
|
||||||
qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
|
// qb.Abort(qb.MaxAtreeSizeExceeded, errors.New("no space in Atree index"))
|
||||||
}
|
// }
|
||||||
s.allocatedPagesQty++
|
// s.allocatedPagesQty++
|
||||||
allocated.PageNo = s.allocatedPagesQty
|
// allocated.PageNo = s.allocatedPagesQty
|
||||||
}
|
// }
|
||||||
|
|
||||||
// s.pages[allocated.PageNo] = &_page{
|
// s.pages[allocated.PageNo] = &_page{
|
||||||
// // fix pageType
|
// // fix pageType
|
||||||
@@ -241,61 +257,61 @@ func (s *Atree) readPages() {
|
|||||||
|
|
||||||
// WRITE
|
// WRITE
|
||||||
|
|
||||||
func (s *Atree) pageWriter() {
|
// func (s *Atree) pageWriter() {
|
||||||
for {
|
// for {
|
||||||
select {
|
// select {
|
||||||
case <-s.writeSignalCh:
|
// case <-s.writeSignalCh:
|
||||||
err := s.writeTasks()
|
// err := s.writeTasks()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
qb.Abort(qb.WriteToAtreeFailed, err)
|
// qb.Abort(qb.WriteToAtreeFailed, err)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
type WriteTask struct {
|
// type WriteTask struct {
|
||||||
WaitCh chan struct{}
|
// WaitCh chan struct{}
|
||||||
Pages []PageToWrite
|
// Pages []PageToWrite
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
|
// func (s *Atree) appendWriteTaskToQueue(task WriteTask) {
|
||||||
s.mutex.Lock()
|
// s.mutex.Lock()
|
||||||
s.writeTasksQueue = append(s.writeTasksQueue, task)
|
// s.writeTasksQueue = append(s.writeTasksQueue, task)
|
||||||
s.mutex.Unlock()
|
// s.mutex.Unlock()
|
||||||
|
|
||||||
select {
|
// select {
|
||||||
case s.writeSignalCh <- struct{}{}:
|
// case s.writeSignalCh <- struct{}{}:
|
||||||
default:
|
// default:
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Atree) writeTasks() error {
|
// func (s *Atree) writeTasks() error {
|
||||||
s.mutex.Lock()
|
// s.mutex.Lock()
|
||||||
tasks := s.writeTasksQueue
|
// tasks := s.writeTasksQueue
|
||||||
s.writeTasksQueue = nil
|
// s.writeTasksQueue = nil
|
||||||
s.mutex.Unlock()
|
// s.mutex.Unlock()
|
||||||
|
|
||||||
for _, task := range tasks {
|
// for _, task := range tasks {
|
||||||
for _, p := range task.Pages {
|
// for _, p := range task.Pages {
|
||||||
if len(p.Data) != PageSize {
|
// if len(p.Data) != PageSize {
|
||||||
return fmt.Errorf("wrong page %d size: %d",
|
// return fmt.Errorf("wrong page %d size: %d",
|
||||||
p.PageNo, len(p.Data))
|
// p.PageNo, len(p.Data))
|
||||||
}
|
// }
|
||||||
bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx]))
|
// bin.PutUint32(p.Data[crc32Idx:], calcChecksum(p.Data[:crc32Idx]))
|
||||||
|
|
||||||
off := (p.PageNo - 1) * PageSize
|
// off := (p.PageNo - 1) * PageSize
|
||||||
n, err := s.file.WriteAt(p.Data, int64(off))
|
// n, err := s.file.WriteAt(p.Data, int64(off))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if n != len(p.Data) {
|
// if n != len(p.Data) {
|
||||||
return fmt.Errorf("write %d instead of %d", n, len(p.Data))
|
// return fmt.Errorf("write %d instead of %d", n, len(p.Data))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
close(task.WaitCh)
|
// close(task.WaitCh)
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// IO
|
// IO
|
||||||
|
|
||||||
@@ -339,9 +355,9 @@ func openFile(fileName string, pageSize int) (_ *os.File, _ uint32, err error) {
|
|||||||
return file, uint32(allocatedPagesQty), nil
|
return file, uint32(allocatedPagesQty), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Atree) ApplyREDO(task WriteTask) {
|
// func (s *Atree) ApplyREDO(task WriteTask) {
|
||||||
s.appendWriteTaskToQueue(task)
|
// s.appendWriteTaskToQueue(task)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Atree) verifyCRC(data []byte, pageSize int) error {
|
func (s *Atree) verifyCRC(data []byte, pageSize int) error {
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -55,16 +55,15 @@ func BinarySearch(qty int, keyComparator bin.KeyComparator) (elemIdx int, isFoun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type chunksToDataPageReq struct {
|
type ChunksToNotLinkedDataPageReq struct {
|
||||||
PrevPageNo uint32
|
// PrevPageNo uint32
|
||||||
TimestampsChunks [][]byte
|
TimestampsChunks [][]byte
|
||||||
TimestampsSize uint16
|
TimestampsSize uint16
|
||||||
ValuesChunks [][]byte
|
ValuesChunks [][]byte
|
||||||
ValuesSize uint16
|
ValuesSize uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func chunksToDataPage(buf []byte, req chunksToDataPageReq) {
|
func ChunksToNotLinkedDataPage(buf []byte, req ChunksToNotLinkedDataPageReq) {
|
||||||
bin.PutUint32(buf[prevPageIdx:], req.PrevPageNo)
|
|
||||||
bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
|
bin.PutUint16(buf[timestampsSizeIdx:], req.TimestampsSize)
|
||||||
bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
|
bin.PutUint16(buf[valuesSizeIdx:], req.ValuesSize)
|
||||||
|
|
||||||
@@ -96,7 +95,6 @@ func chunksToDataPage(buf []byte, req chunksToDataPageReq) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bin.PutUint32(buf[crc32Idx:], calcChecksum(buf[:crc32Idx]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPrevPageNo(buf []byte, pageNo uint32) {
|
func setPrevPageNo(buf []byte, pageNo uint32) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
package chunkenc
|
package chunkenc
|
||||||
|
|
||||||
const eps = 0.000001
|
const eps = 0.000001
|
||||||
|
|
||||||
|
const flagLiteral = 128
|
||||||
298
chunkenc/chunkenc_test.go
Normal file
298
chunkenc/chunkenc_test.go
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
package chunkenc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCumdelta(t *testing.T) {
|
||||||
|
var (
|
||||||
|
fracDigits byte = 0
|
||||||
|
buf = conbuf.New(nil)
|
||||||
|
value float64
|
||||||
|
done bool
|
||||||
|
)
|
||||||
|
c := NewReverseCumulativeDeltaCompressor(buf, 0, fracDigits)
|
||||||
|
// c.Append(1.55)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23.5)
|
||||||
|
|
||||||
|
c.Append(130)
|
||||||
|
c.Append(191)
|
||||||
|
|
||||||
|
c.Append(248)
|
||||||
|
c.Append(305)
|
||||||
|
|
||||||
|
//fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
//fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
d := NewReverseCumulativeDeltaDecompressor(buf, c.Size(), fracDigits)
|
||||||
|
|
||||||
|
for range 8 {
|
||||||
|
value, done = d.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsdelta(t *testing.T) {
|
||||||
|
var (
|
||||||
|
fracDigits byte = 2
|
||||||
|
buf = conbuf.New(nil)
|
||||||
|
value float64
|
||||||
|
done bool
|
||||||
|
)
|
||||||
|
c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits)
|
||||||
|
c.Append(-1.55)
|
||||||
|
c.Append(23)
|
||||||
|
|
||||||
|
//fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
//fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
c.Append(23)
|
||||||
|
c.Append(23)
|
||||||
|
|
||||||
|
c.Append(-23.5)
|
||||||
|
|
||||||
|
//fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
//fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
|
||||||
|
|
||||||
|
for range 8 {
|
||||||
|
value, done = d.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeDelta(t *testing.T) {
|
||||||
|
var (
|
||||||
|
buf = conbuf.New(nil)
|
||||||
|
value uint32
|
||||||
|
done bool
|
||||||
|
)
|
||||||
|
c := NewReverseTimeDeltaCompressor(buf, 0)
|
||||||
|
c.Append(10)
|
||||||
|
|
||||||
|
//c.Append(131)
|
||||||
|
|
||||||
|
//fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
//fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
c.Append(70)
|
||||||
|
|
||||||
|
c.Append(130)
|
||||||
|
c.Append(191)
|
||||||
|
|
||||||
|
c.Append(248)
|
||||||
|
c.Append(305)
|
||||||
|
c.Append(330)
|
||||||
|
c.Append(390)
|
||||||
|
c.Append(450)
|
||||||
|
|
||||||
|
// fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
// fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
//c.Sync()
|
||||||
|
|
||||||
|
bound := c.GetState()
|
||||||
|
|
||||||
|
fmt.Println("AFTER SYNC")
|
||||||
|
fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
chunks := buf.Chunks()
|
||||||
|
if len(chunks) > 0 {
|
||||||
|
fmt.Printf("%d\n", chunks[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
d := NewReverseTimeDeltaDecompressor(buf, c.Size())
|
||||||
|
d.RestoreFromBound(bound)
|
||||||
|
//d.RestoreFromEnd()
|
||||||
|
|
||||||
|
for range 12 {
|
||||||
|
value, done = d.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCumdeltaBound(t *testing.T) {
|
||||||
|
var (
|
||||||
|
fracDigits byte = 0
|
||||||
|
buf = conbuf.New(nil)
|
||||||
|
value float64
|
||||||
|
done bool
|
||||||
|
)
|
||||||
|
c := NewReverseCumulativeDeltaCompressor(buf, 0, fracDigits)
|
||||||
|
// c.Append(1.55)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23.5)
|
||||||
|
c.Append(10)
|
||||||
|
c.Append(70)
|
||||||
|
|
||||||
|
c.Append(130)
|
||||||
|
|
||||||
|
// fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
// fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
c.Append(191)
|
||||||
|
|
||||||
|
// fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
// fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
c.Append(191)
|
||||||
|
|
||||||
|
fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
bound := c.GetState()
|
||||||
|
|
||||||
|
fmt.Printf("bound: pos=%d, h=%d\n", bound.Pos, bound.H)
|
||||||
|
|
||||||
|
c.Append(248)
|
||||||
|
c.Append(305)
|
||||||
|
|
||||||
|
fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
d := NewReverseCumulativeDeltaDecompressor(buf, c.Size(), fracDigits)
|
||||||
|
d.RestoreFromEnd()
|
||||||
|
|
||||||
|
for range 8 {
|
||||||
|
value, done = d.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
|
||||||
|
boundDecompressor := NewReverseCumulativeDeltaDecompressor(buf, c.Size(), fracDigits)
|
||||||
|
boundDecompressor.RestoreFromBound(bound)
|
||||||
|
|
||||||
|
fmt.Println("from bound:")
|
||||||
|
for range 8 {
|
||||||
|
value, done = boundDecompressor.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsdeltaBound(t *testing.T) {
|
||||||
|
var (
|
||||||
|
fracDigits byte = 0
|
||||||
|
buf = conbuf.New(nil)
|
||||||
|
value float64
|
||||||
|
done bool
|
||||||
|
)
|
||||||
|
c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits)
|
||||||
|
// c.Append(1.55)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23.5)
|
||||||
|
c.Append(10)
|
||||||
|
c.Append(70)
|
||||||
|
|
||||||
|
c.Append(130)
|
||||||
|
|
||||||
|
// fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
// fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
c.Append(191)
|
||||||
|
|
||||||
|
// fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
// fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
c.Append(191)
|
||||||
|
|
||||||
|
fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
bound := c.GetState()
|
||||||
|
|
||||||
|
fmt.Printf("bound: pos=%d, h=%d\n", bound.Pos, bound.H)
|
||||||
|
|
||||||
|
c.Append(248)
|
||||||
|
c.Append(305)
|
||||||
|
|
||||||
|
fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
|
||||||
|
d.RestoreFromEnd()
|
||||||
|
|
||||||
|
for range 8 {
|
||||||
|
value, done = d.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
|
||||||
|
boundDecompressor := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
|
||||||
|
boundDecompressor.RestoreFromBound(bound)
|
||||||
|
|
||||||
|
fmt.Println("from bound:")
|
||||||
|
for range 8 {
|
||||||
|
value, done = boundDecompressor.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsdeltaBound2(t *testing.T) {
|
||||||
|
var (
|
||||||
|
fracDigits byte = 0
|
||||||
|
buf = conbuf.New(nil)
|
||||||
|
value float64
|
||||||
|
done bool
|
||||||
|
)
|
||||||
|
c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits)
|
||||||
|
// c.Append(1.55)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23)
|
||||||
|
// c.Append(23.5)
|
||||||
|
c.Append(305)
|
||||||
|
c.Append(248)
|
||||||
|
|
||||||
|
c.Append(191)
|
||||||
|
|
||||||
|
// fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
// fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
c.Append(191)
|
||||||
|
|
||||||
|
// fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
// fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
c.Append(130)
|
||||||
|
|
||||||
|
fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
//////////////////////////
|
||||||
|
|
||||||
|
bound := c.GetState()
|
||||||
|
|
||||||
|
fmt.Printf("bound: pos=%d, h=%d\n", bound.Pos, bound.H)
|
||||||
|
|
||||||
|
c.Append(70)
|
||||||
|
c.Append(10)
|
||||||
|
|
||||||
|
fmt.Printf("pos: %d\n", c.pos)
|
||||||
|
fmt.Printf("%d\n", buf.Chunks()[0])
|
||||||
|
|
||||||
|
d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
|
||||||
|
d.RestoreFromEnd()
|
||||||
|
|
||||||
|
for range 8 {
|
||||||
|
value, done = d.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
|
||||||
|
boundDecompressor := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits)
|
||||||
|
boundDecompressor.RestoreFromBound(bound)
|
||||||
|
|
||||||
|
fmt.Println("from bound:")
|
||||||
|
for range 8 {
|
||||||
|
value, done = boundDecompressor.NextValue()
|
||||||
|
fmt.Println(value, done)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,22 +2,49 @@ package chunkenc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"gordenko.dev/dima/qb/bin"
|
|
||||||
"gordenko.dev/dima/qb/conbuf"
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// REVERSE
|
// COMPRESSOR REVERSE
|
||||||
|
|
||||||
|
/*
|
||||||
|
Формат:
|
||||||
|
base value (var u64)
|
||||||
|
delta (var u64)
|
||||||
|
q (qty of previous deltas; msb=0 - series, msb=1 - non series, low 7 bits - qty 1..128)
|
||||||
|
delta
|
||||||
|
delta
|
||||||
|
delta
|
||||||
|
q
|
||||||
|
|
||||||
|
Дельта рахується від base value.
|
||||||
|
Декодування у зворотньому порядку.
|
||||||
|
|
||||||
|
Після base value слідують run або literal блоки.
|
||||||
|
Run блок - це delta + header byte в кінці.
|
||||||
|
Literal блок - це від одної до N дельт + header byte в кінці.
|
||||||
|
Спочатку створюється literal блок.
|
||||||
|
Якщо для останної дельти додається дублікат, literal блок модифікується -
|
||||||
|
лічильник зменшується до 1. А остання дельта переміщюється в новий run блок.
|
||||||
|
Причому лічильник 0 - означає 2 елементи. Приклад:
|
||||||
|
До:
|
||||||
|
v1 v2 v3 h-byte(literal, 3) <- v3
|
||||||
|
Після:
|
||||||
|
v1 v2 h-byte(literal, 2) v3 h-byte(run, 2)
|
||||||
|
*/
|
||||||
|
|
||||||
type ReverseCumulativeDeltaCompressor struct {
|
type ReverseCumulativeDeltaCompressor struct {
|
||||||
buf *conbuf.ContinuousBuffer
|
buf *conbuf.ContinuousBuffer
|
||||||
coef float64
|
coef float64
|
||||||
pos int
|
pos int
|
||||||
firstValue float64
|
baseValue float64
|
||||||
lastDelta uint64
|
lastDelta uint64
|
||||||
length uint16
|
lastDeltaSize int
|
||||||
numIdx int
|
h byte
|
||||||
|
state *CumulativeDeltaBound
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReverseCumulativeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaCompressor {
|
func NewReverseCumulativeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaCompressor {
|
||||||
@@ -27,236 +54,166 @@ func NewReverseCumulativeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int,
|
|||||||
}
|
}
|
||||||
s := &ReverseCumulativeDeltaCompressor{
|
s := &ReverseCumulativeDeltaCompressor{
|
||||||
buf: buf,
|
buf: buf,
|
||||||
pos: size,
|
pos: size, // перший вільний байт
|
||||||
coef: coef,
|
coef: coef,
|
||||||
}
|
}
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
s.restoreState()
|
u64, _, err := s.buf.GetVarUint64(0)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("bug: get base value: %s", err)
|
||||||
|
}
|
||||||
|
s.baseValue = float64(u64) / s.coef
|
||||||
|
s.h = s.buf.GetByte(s.pos - 1)
|
||||||
|
s.lastDelta, s.lastDeltaSize, err = s.buf.ReverseGetVarUint64(s.pos - 2)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("bug: get last delta: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) restoreState() {
|
|
||||||
u64, n, err := s.buf.GetVarUint64(0)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("bug: get first value: %s", err))
|
|
||||||
}
|
|
||||||
s.firstValue = float64(u64) / s.coef
|
|
||||||
|
|
||||||
if s.pos > n {
|
|
||||||
pos := s.pos - 1
|
|
||||||
idxOf8 := uint(8 - s.buf.GetByte(pos))
|
|
||||||
pos--
|
|
||||||
s8 := s.buf.GetByte(pos)
|
|
||||||
pos--
|
|
||||||
|
|
||||||
var n int
|
|
||||||
s.lastDelta, n, err = s.buf.ReverseGetVarUint64(pos)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("bug: get last delta: %s", err))
|
|
||||||
}
|
|
||||||
pos -= n
|
|
||||||
s.numIdx = pos + 1
|
|
||||||
|
|
||||||
var flag byte = 1 << idxOf8
|
|
||||||
if (s8 & flag) == flag {
|
|
||||||
s.length, _ = s.buf.DecodeRunLength(pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) Size() int {
|
func (s *ReverseCumulativeDeltaCompressor) Size() int {
|
||||||
return s.pos
|
return s.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) CalcRequiredSpace(value float64) int {
|
//func (s *ReverseCumulativeDeltaCompressor) CalcRequiredSpace(value float64) int {
|
||||||
if s.pos == 0 {
|
//}
|
||||||
n := bin.CalcVarUint64Length(uint64(value * s.coef))
|
|
||||||
return n + 3
|
|
||||||
}
|
|
||||||
delta := uint64((value-s.firstValue)*s.coef + eps)
|
|
||||||
if delta == s.lastDelta {
|
|
||||||
if s.length == 0 {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
newLength := s.length + 1
|
|
||||||
if newLength < 130 {
|
|
||||||
return 0
|
|
||||||
} else if newLength == 130 {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
if newLength < 32769 {
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
n := bin.CalcVarUint64Length(delta)
|
|
||||||
n += 2
|
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
|
||||||
if s8q == 8 {
|
|
||||||
n -= 1
|
|
||||||
} else {
|
|
||||||
n -= 2
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n := bin.CalcVarUint64Length(delta)
|
|
||||||
n += 2
|
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
|
||||||
if s8q == 8 {
|
|
||||||
n -= 1
|
|
||||||
} else {
|
|
||||||
n -= 2
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) Append(value float64) {
|
func (s *ReverseCumulativeDeltaCompressor) Append(value float64) {
|
||||||
if s.pos == 0 {
|
if s.pos == 0 {
|
||||||
n := s.buf.PutVarUint64(s.pos, uint64(value*s.coef))
|
// base value
|
||||||
s.pos += n
|
s.pos += s.buf.PutVarUint64(s.pos, uint64(value*s.coef))
|
||||||
s.firstValue = value
|
s.baseValue = value
|
||||||
s.encodeNewDelta(0, 0, 1)
|
s.appendNewLiteral(0)
|
||||||
} else {
|
} else {
|
||||||
delta := uint64((value-s.firstValue)*s.coef + eps)
|
delta := uint64((value-s.baseValue)*s.coef + eps)
|
||||||
if delta == s.lastDelta {
|
if delta == s.lastDelta {
|
||||||
if s.length == 0 {
|
if s.h < 128 {
|
||||||
s.length = 2
|
// run блок - отже треба збільшити лічильник
|
||||||
s.shiftOnePosToRight()
|
if s.h < 127 {
|
||||||
s.buf.SetByte(s.numIdx-1, 0)
|
// increase counter
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
s.h++
|
||||||
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
|
s.buf.SetByte(s.pos-1, s.h)
|
||||||
} else {
|
|
||||||
s.length++
|
|
||||||
if s.length < 130 {
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
|
|
||||||
} else if s.length == 130 {
|
|
||||||
s.shiftOnePosToRight()
|
|
||||||
s.encode2bLength()
|
|
||||||
} else {
|
} else {
|
||||||
if s.length < 32769 {
|
// не можу збільшити - буде переповнення. Додаю новий literal блок
|
||||||
s.encode2bLength()
|
// counter overflow
|
||||||
} else {
|
s.appendNewLiteral(delta)
|
||||||
s.appendNewDelta(delta)
|
}
|
||||||
}
|
} else {
|
||||||
|
// literal блок.
|
||||||
|
// Якщо в ньому лише одне значення - перетворюю його на run блок.
|
||||||
|
// Інакше забираю останнє значення із literal блока і додаю новий run блок.
|
||||||
|
q := s.h & 127
|
||||||
|
if q == 0 { // 1 кодується як 0
|
||||||
|
s.convertLiteralToRun()
|
||||||
|
} else {
|
||||||
|
// забираю останнє значення із literal блоку щоб зробити run блок
|
||||||
|
s.convertLastFromLiteralToRun()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s.appendNewDelta(delta)
|
if s.h < 127 { // fix 128?
|
||||||
|
// end of run
|
||||||
|
s.appendNewLiteral(delta)
|
||||||
|
} else {
|
||||||
|
if s.h < 255 {
|
||||||
|
// encode value from pos - 1, then append h byte
|
||||||
|
s.appendDeltaToLiteral(delta)
|
||||||
|
} else {
|
||||||
|
// overflowed - encode new
|
||||||
|
s.appendNewLiteral(delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) appendNewDelta(delta uint64) {
|
func (s *ReverseCumulativeDeltaCompressor) convertLastFromLiteralToRun() {
|
||||||
s.length = 0
|
// Зменшую кількість елементів в literal блоці
|
||||||
|
s.h--
|
||||||
s8 := s.buf.GetByte(s.pos - 2)
|
s.pos -= 1 + s.lastDeltaSize
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
s.buf.SetByte(s.pos, s.h) // закриваю literal блок
|
||||||
|
s.pos++
|
||||||
if s8q == 8 {
|
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, s.lastDelta)
|
||||||
s.pos -= 1
|
s.pos += s.lastDeltaSize
|
||||||
s8 = 0
|
s.h = 0 // run блок, довжини 2
|
||||||
s8q = 1
|
s.buf.SetByte(s.pos, s.h)
|
||||||
} else {
|
s.pos++
|
||||||
s.pos -= 2
|
|
||||||
s8q++
|
|
||||||
}
|
|
||||||
|
|
||||||
s.encodeNewDelta(delta, s8, s8q)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) encodeNewDelta(delta uint64, s8 byte, s8q byte) {
|
func (s *ReverseCumulativeDeltaCompressor) convertLiteralToRun() {
|
||||||
|
// Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
|
||||||
|
s.h = 0
|
||||||
|
s.buf.SetByte(s.pos-1, s.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) appendDeltaToLiteral(delta uint64) {
|
||||||
|
s.h++ // збільшую к-сть дельт
|
||||||
s.lastDelta = delta
|
s.lastDelta = delta
|
||||||
s.numIdx = s.pos
|
s.pos--
|
||||||
n := s.buf.ReversePutVarUint64(s.pos, s.lastDelta)
|
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, delta)
|
||||||
s.pos += n
|
s.pos += s.lastDeltaSize
|
||||||
s.buf.SetByte(s.pos, s8)
|
s.buf.SetByte(s.pos, s.h)
|
||||||
s.pos++
|
|
||||||
s.buf.SetByte(s.pos, s8q)
|
|
||||||
s.pos++
|
s.pos++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) shiftOnePosToRight() {
|
func (s *ReverseCumulativeDeltaCompressor) appendNewLiteral(delta uint64) {
|
||||||
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
|
s.h = flagLiteral
|
||||||
|
s.lastDelta = delta
|
||||||
|
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, delta)
|
||||||
|
s.pos += s.lastDeltaSize
|
||||||
|
s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1
|
||||||
s.pos++
|
s.pos++
|
||||||
s.numIdx++
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) encode2bLength() {
|
|
||||||
num := s.length - 2
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
|
|
||||||
s.buf.SetByte(s.numIdx-2, byte(num>>7))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaCompressor) DeleteLast() {
|
func (s *ReverseCumulativeDeltaCompressor) DeleteLast() {
|
||||||
var (
|
|
||||||
s8q = s.buf.GetByte(s.pos - 1)
|
|
||||||
s8 = s.buf.GetByte(s.pos - 2)
|
|
||||||
flag byte = 1 << uint(8-s8q)
|
|
||||||
)
|
|
||||||
|
|
||||||
if s.length > 0 {
|
}
|
||||||
if s.length == 2 {
|
|
||||||
s.length = 0
|
type CumulativeDeltaBound struct {
|
||||||
s.buf.UnsetFlag(s.pos-2, flag)
|
Pos int
|
||||||
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
H byte
|
||||||
s.numIdx--
|
LastDelta uint64
|
||||||
s.pos--
|
Chunks [][]byte
|
||||||
} else if s.length < 130 {
|
}
|
||||||
s.length--
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
// delta h
|
||||||
} else if s.length == 130 {
|
func (s *ReverseCumulativeDeltaCompressor) Lock() {
|
||||||
s.length--
|
s.state = &CumulativeDeltaBound{
|
||||||
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
Pos: s.pos - 1 - s.lastDeltaSize,
|
||||||
s.numIdx--
|
H: s.h,
|
||||||
s.pos--
|
LastDelta: s.lastDelta,
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
Chunks: s.buf.Chunks(),
|
||||||
} else {
|
|
||||||
s.length--
|
|
||||||
s.encode2bLength()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if s8q > 1 {
|
|
||||||
s8q--
|
|
||||||
flag = 1 << uint(8-s8q)
|
|
||||||
s.pos = s.numIdx + 2
|
|
||||||
s.buf.SetByte(s.pos-2, s8)
|
|
||||||
s.buf.SetByte(s.pos-1, s8q)
|
|
||||||
} else {
|
|
||||||
s.pos = s.numIdx + 1
|
|
||||||
s.buf.SetByte(s.pos-1, 8)
|
|
||||||
s8 = s.buf.GetByte(s.pos - 2)
|
|
||||||
flag = 1
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
pos = s.pos - 3
|
|
||||||
n int
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
s.lastDelta, n, err = s.buf.ReverseGetVarUint64(pos)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
s.numIdx = pos - n
|
|
||||||
if (s8 & flag) == flag {
|
|
||||||
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) CreateDecompressor(fracDigits byte) *ReverseCumulativeDeltaDecompressor {
|
||||||
|
if s.state == nil {
|
||||||
|
d := NewReverseCumulativeDeltaDecompressor(s.buf, s.Size(), fracDigits)
|
||||||
|
d.RestoreFromEnd()
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
d := NewReverseCumulativeDeltaDecompressor(s.buf, s.Size(), fracDigits)
|
||||||
|
d.RestoreFromBound(*s.state)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) Unlock() {
|
||||||
|
s.state = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DECOMPRESSOR
|
||||||
|
|
||||||
type ReverseCumulativeDeltaDecompressor struct {
|
type ReverseCumulativeDeltaDecompressor struct {
|
||||||
buf *conbuf.ContinuousBuffer
|
buf *conbuf.ContinuousBuffer
|
||||||
pos int
|
coef float64
|
||||||
bound int
|
pos int
|
||||||
firstValue float64
|
bound int
|
||||||
lastValue float64
|
baseValue float64
|
||||||
length uint16
|
lastValue float64
|
||||||
coef float64
|
isRun bool
|
||||||
idxOf8 uint
|
pending int
|
||||||
s8 byte
|
done bool
|
||||||
step byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReverseCumulativeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaDecompressor {
|
func NewReverseCumulativeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaDecompressor {
|
||||||
@@ -264,67 +221,91 @@ func NewReverseCumulativeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size in
|
|||||||
if fracDigits > 0 {
|
if fracDigits > 0 {
|
||||||
coef = math.Pow(10, float64(fracDigits))
|
coef = math.Pow(10, float64(fracDigits))
|
||||||
}
|
}
|
||||||
return &ReverseCumulativeDeltaDecompressor{
|
u64, n, err := buf.GetVarUint64(0)
|
||||||
buf: buf,
|
if err != nil {
|
||||||
coef: coef,
|
log.Fatalf("bug: get base value: %s", err)
|
||||||
pos: size,
|
|
||||||
}
|
}
|
||||||
|
//fmt.Printf("baseValue: %.2f, bound: %d, pos: %d\n", float64(u64)/coef, n, size)
|
||||||
|
return &ReverseCumulativeDeltaDecompressor{
|
||||||
|
buf: buf,
|
||||||
|
coef: coef,
|
||||||
|
pos: size - 1, // last elem
|
||||||
|
baseValue: float64(u64) / coef,
|
||||||
|
bound: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaDecompressor) RestoreFromEnd() {
|
||||||
|
if s.pos > s.bound {
|
||||||
|
// читаю заголовок наступної серії
|
||||||
|
s.readHeader()
|
||||||
|
s.readValue()
|
||||||
|
} else {
|
||||||
|
s.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaDecompressor) RestoreFromBound(bound CumulativeDeltaBound) {
|
||||||
|
s.pos = bound.Pos - 1
|
||||||
|
s.lastValue = s.baseValue + float64(bound.LastDelta)
|
||||||
|
s.decodeHeaderByte(bound.H)
|
||||||
|
fmt.Printf("restore from bound: isRun=%t, pending=%d\n", s.isRun, s.pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaDecompressor) NextValue() (value float64, done bool) {
|
func (s *ReverseCumulativeDeltaDecompressor) NextValue() (value float64, done bool) {
|
||||||
if s.step > 0 {
|
//fmt.Printf("NextValue(): bound: %d, pos: %d, pending: %d\n", s.bound, s.pos, s.pending)
|
||||||
if s.length > 0 {
|
if s.done {
|
||||||
s.length--
|
return 0, true
|
||||||
return s.lastValue, false
|
|
||||||
}
|
|
||||||
if s.pos < s.bound {
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
if s.idxOf8 == 0 {
|
|
||||||
s.s8 = s.buf.GetByte(s.pos)
|
|
||||||
s.pos--
|
|
||||||
}
|
|
||||||
s.readVar()
|
|
||||||
if s.length > 0 {
|
|
||||||
s.length--
|
|
||||||
}
|
|
||||||
return s.lastValue, false
|
|
||||||
}
|
}
|
||||||
u64, n, err := s.buf.GetVarUint64(0)
|
// повертаю значення, що було прочитано в методі RestoreFromBound/RestoreFromEnd
|
||||||
if err != nil {
|
value = s.lastValue
|
||||||
panic(err)
|
s.pending--
|
||||||
|
if s.pending > 0 {
|
||||||
|
// якщо в серії залишаються елементи
|
||||||
|
if !s.isRun {
|
||||||
|
s.readValue()
|
||||||
|
}
|
||||||
|
} else if s.pos > s.bound {
|
||||||
|
// в серії більше немає елементів, отже перевіряє чи є ще дані в буфері.
|
||||||
|
// дані є - читаю заголовок наступної серії
|
||||||
|
s.readHeader()
|
||||||
|
s.readValue()
|
||||||
|
} else {
|
||||||
|
s.done = true
|
||||||
}
|
}
|
||||||
s.firstValue = float64(u64) / s.coef
|
// серія завершена - перевіряю чи є ще серії
|
||||||
s.bound = n
|
return value, false
|
||||||
s.pos--
|
|
||||||
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
|
|
||||||
s.pos--
|
|
||||||
s.s8 = s.buf.GetByte(s.pos)
|
|
||||||
s.pos--
|
|
||||||
s.readVar()
|
|
||||||
if s.length > 0 {
|
|
||||||
s.length--
|
|
||||||
}
|
|
||||||
s.step = 1
|
|
||||||
return s.lastValue, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseCumulativeDeltaDecompressor) readVar() {
|
func (s *ReverseCumulativeDeltaDecompressor) readHeader() {
|
||||||
|
h := s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
s.decodeHeaderByte(h)
|
||||||
|
// fmt.Println("h:", h)
|
||||||
|
// fmt.Println("isRun:", s.isRun)
|
||||||
|
// fmt.Println("pending:", s.pending)
|
||||||
|
}
|
||||||
|
func (s *ReverseCumulativeDeltaDecompressor) readValue() {
|
||||||
u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
|
u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
// fmt.Println()
|
||||||
|
// fmt.Println("read from pos:", s.pos)
|
||||||
|
// fmt.Println("read delta:", u64)
|
||||||
|
// fmt.Println("read delta n:", n)
|
||||||
|
// fmt.Println()
|
||||||
s.pos -= n
|
s.pos -= n
|
||||||
s.lastValue = s.firstValue + float64(u64)/s.coef
|
s.lastValue = s.baseValue + float64(u64)/s.coef
|
||||||
|
}
|
||||||
|
|
||||||
var flag byte = 1 << s.idxOf8
|
func (s *ReverseCumulativeDeltaDecompressor) decodeHeaderByte(h byte) {
|
||||||
if (s.s8 & flag) == flag {
|
s.isRun = h < 128
|
||||||
s.length, n = s.buf.DecodeRunLength(s.pos)
|
if s.isRun {
|
||||||
s.pos -= n
|
s.pending = int(h&127) + 2
|
||||||
}
|
|
||||||
if s.idxOf8 == 7 {
|
|
||||||
s.idxOf8 = 0
|
|
||||||
} else {
|
} else {
|
||||||
s.idxOf8++
|
s.pending = int(h&127) + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|||||||
@@ -2,22 +2,20 @@ package chunkenc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"gordenko.dev/dima/qb/bin"
|
|
||||||
"gordenko.dev/dima/qb/conbuf"
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// REVERSE
|
|
||||||
|
|
||||||
type ReverseInstantDeltaCompressor struct {
|
type ReverseInstantDeltaCompressor struct {
|
||||||
buf *conbuf.ContinuousBuffer
|
buf *conbuf.ContinuousBuffer
|
||||||
coef float64
|
coef float64
|
||||||
pos int
|
pos int
|
||||||
firstValue float64
|
baseValue float64
|
||||||
lastDelta int64
|
lastDelta int64
|
||||||
length uint16
|
lastDeltaSize int
|
||||||
numIdx int
|
h byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReverseInstantDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaCompressor {
|
func NewReverseInstantDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaCompressor {
|
||||||
@@ -31,246 +29,146 @@ func NewReverseInstantDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fr
|
|||||||
coef: coef,
|
coef: coef,
|
||||||
}
|
}
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
s.restoreState()
|
i64, _, err := s.buf.GetVarInt64(0)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("bug: get base value: %s", err)
|
||||||
|
}
|
||||||
|
s.baseValue = float64(i64) / s.coef
|
||||||
|
s.h = s.buf.GetByte(s.pos - 1)
|
||||||
|
s.lastDelta, s.lastDeltaSize, err = s.buf.ReverseGetVarInt64(s.pos - 2)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("bug: get last delta: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaCompressor) restoreState() {
|
|
||||||
i64, n, err := s.buf.GetVarInt64(0)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("bug: get first value: %s", err))
|
|
||||||
}
|
|
||||||
s.firstValue = float64(i64) / s.coef
|
|
||||||
|
|
||||||
if s.pos > n {
|
|
||||||
pos := s.pos - 1
|
|
||||||
idxOf8 := uint(8 - s.buf.GetByte(pos))
|
|
||||||
pos--
|
|
||||||
s8 := s.buf.GetByte(pos)
|
|
||||||
pos--
|
|
||||||
|
|
||||||
var n int
|
|
||||||
s.lastDelta, n, err = s.buf.ReverseGetVarInt64(pos)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("bug: get last delta: %s", err))
|
|
||||||
}
|
|
||||||
pos -= n
|
|
||||||
s.numIdx = pos + 1
|
|
||||||
var flag byte = 1 << idxOf8
|
|
||||||
if (s8 & flag) == flag {
|
|
||||||
s.length, _ = s.buf.DecodeRunLength(pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaCompressor) Size() int {
|
func (s *ReverseInstantDeltaCompressor) Size() int {
|
||||||
return s.pos
|
return s.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaCompressor) CalcRequiredSpace(value float64) int {
|
|
||||||
if s.pos == 0 {
|
|
||||||
n := bin.CalcVarInt64Length(int64(value * s.coef))
|
|
||||||
return n + 3
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp := (value - s.firstValue) * s.coef
|
|
||||||
if tmp > 0 {
|
|
||||||
tmp += eps
|
|
||||||
} else {
|
|
||||||
tmp -= eps
|
|
||||||
}
|
|
||||||
delta := int64(tmp)
|
|
||||||
|
|
||||||
if delta == s.lastDelta {
|
|
||||||
if s.length == 0 {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
newLength := s.length + 1
|
|
||||||
if newLength < 130 {
|
|
||||||
return 0
|
|
||||||
} else if newLength == 130 {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
if newLength < 32769 {
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
n := bin.CalcVarInt64Length(delta)
|
|
||||||
n += 2
|
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
|
||||||
if s8q == 8 {
|
|
||||||
n -= 1
|
|
||||||
} else {
|
|
||||||
n -= 2
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n := bin.CalcVarInt64Length(delta)
|
|
||||||
n += 2
|
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
|
||||||
if s8q == 8 {
|
|
||||||
n -= 1
|
|
||||||
} else {
|
|
||||||
n -= 2
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// В начале буфера кодирую базовое значение.
|
|
||||||
func (s *ReverseInstantDeltaCompressor) Append(value float64) {
|
func (s *ReverseInstantDeltaCompressor) Append(value float64) {
|
||||||
if s.pos == 0 {
|
if s.pos == 0 {
|
||||||
|
// base value
|
||||||
n := s.buf.PutVarInt64(s.pos, int64(value*s.coef))
|
n := s.buf.PutVarInt64(s.pos, int64(value*s.coef))
|
||||||
s.pos += n
|
s.pos += n
|
||||||
s.firstValue = value
|
s.baseValue = value
|
||||||
s.encodeNewDelta(0, 0, 1)
|
s.appendNewLiteral(0)
|
||||||
} else {
|
} else {
|
||||||
tmp := (value - s.firstValue) * s.coef
|
tmp := (value - s.baseValue) * s.coef
|
||||||
if tmp > 0 {
|
if tmp > 0 {
|
||||||
tmp += eps
|
tmp += eps
|
||||||
} else {
|
} else {
|
||||||
tmp -= eps
|
tmp -= eps
|
||||||
}
|
}
|
||||||
delta := int64(tmp)
|
delta := int64(tmp)
|
||||||
|
|
||||||
if delta == s.lastDelta {
|
if delta == s.lastDelta {
|
||||||
if s.length == 0 {
|
if s.h < 128 {
|
||||||
s.length = 2
|
// run блок - отже треба збільшити лічильник
|
||||||
s.shiftOnePosToRight()
|
if s.h < 127 {
|
||||||
s.buf.SetByte(s.numIdx-1, 0)
|
// increase counter
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
s.h++
|
||||||
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
|
s.buf.SetByte(s.pos-1, s.h)
|
||||||
} else {
|
|
||||||
s.length++
|
|
||||||
if s.length < 130 {
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
|
|
||||||
} else if s.length == 130 {
|
|
||||||
s.shiftOnePosToRight()
|
|
||||||
s.encode2bLength()
|
|
||||||
} else {
|
} else {
|
||||||
if s.length < 32769 {
|
// не можу збільшити - буде переповнення. Додаю новий literal блок
|
||||||
s.encode2bLength()
|
// counter overflow
|
||||||
} else {
|
// fix encode delta
|
||||||
s.appendNewDelta(delta)
|
s.appendNewLiteral(delta)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// literal блок.
|
||||||
|
// Якщо в ньому лише одне значення - перетворюю його на run блок.
|
||||||
|
// Інакше забираю останнє значення із literal блока і додаю новий run блок.
|
||||||
|
q := s.h & 127
|
||||||
|
if q == 0 { // 1 кодується як 0
|
||||||
|
s.convertLiteralToRun()
|
||||||
|
} else {
|
||||||
|
s.convertLastFromLiteralToRun()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s.appendNewDelta(delta)
|
if s.h < 127 {
|
||||||
|
// end of run
|
||||||
|
s.appendNewLiteral(delta)
|
||||||
|
} else {
|
||||||
|
if s.h < 255 {
|
||||||
|
// encode value from pos - 1, then append h byte
|
||||||
|
s.appendDeltaToLiteral(delta)
|
||||||
|
} else {
|
||||||
|
// overflowed - encode new
|
||||||
|
s.appendNewLiteral(delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaCompressor) appendNewDelta(delta int64) {
|
func (s *ReverseInstantDeltaCompressor) convertLastFromLiteralToRun() {
|
||||||
s.length = 0
|
// Зменшую кількість елементів в literal блоці
|
||||||
|
s.h--
|
||||||
s8 := s.buf.GetByte(s.pos - 2)
|
s.pos -= 1 + s.lastDeltaSize
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
s.buf.SetByte(s.pos, s.h) // закриваю literal блок
|
||||||
|
s.pos++
|
||||||
if s8q == 8 {
|
s.lastDeltaSize = s.buf.ReversePutVarInt64(s.pos, s.lastDelta)
|
||||||
s.pos -= 1
|
s.pos += s.lastDeltaSize
|
||||||
s8 = 0
|
s.h = 0 // run блок, довжини 2
|
||||||
s8q = 1
|
s.buf.SetByte(s.pos, s.h)
|
||||||
} else {
|
s.pos++
|
||||||
s.pos -= 2
|
|
||||||
s8q++
|
|
||||||
}
|
|
||||||
s.encodeNewDelta(delta, s8, s8q)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaCompressor) encodeNewDelta(delta int64, s8 byte, s8q byte) {
|
func (s *ReverseInstantDeltaCompressor) convertLiteralToRun() {
|
||||||
|
// Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
|
||||||
|
s.h = 0
|
||||||
|
s.buf.SetByte(s.pos-1, s.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) appendDeltaToLiteral(delta int64) {
|
||||||
|
s.h++ // збільшую к-сть дельт
|
||||||
s.lastDelta = delta
|
s.lastDelta = delta
|
||||||
s.numIdx = s.pos
|
s.pos--
|
||||||
n := s.buf.ReversePutVarInt64(s.pos, s.lastDelta)
|
s.lastDeltaSize = s.buf.ReversePutVarInt64(s.pos, delta)
|
||||||
s.pos += n
|
s.pos += s.lastDeltaSize
|
||||||
s.buf.SetByte(s.pos, s8)
|
s.buf.SetByte(s.pos, s.h)
|
||||||
s.pos++
|
|
||||||
s.buf.SetByte(s.pos, s8q)
|
|
||||||
s.pos++
|
s.pos++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaCompressor) shiftOnePosToRight() {
|
func (s *ReverseInstantDeltaCompressor) appendNewLiteral(delta int64) {
|
||||||
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
|
s.h = flagLiteral
|
||||||
|
s.lastDelta = delta
|
||||||
|
s.lastDeltaSize = s.buf.ReversePutVarInt64(s.pos, delta)
|
||||||
|
s.pos += s.lastDeltaSize
|
||||||
|
s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1
|
||||||
s.pos++
|
s.pos++
|
||||||
s.numIdx++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaCompressor) encode2bLength() {
|
func (s *ReverseInstantDeltaCompressor) DeleteLast() {}
|
||||||
num := s.length - 2
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
|
type InstantDeltaBound struct {
|
||||||
s.buf.SetByte(s.numIdx-2, byte(num>>7))
|
Pos int
|
||||||
|
H byte
|
||||||
|
LastDelta int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaCompressor) DeleteLast() {
|
func (s *ReverseInstantDeltaCompressor) GetState() InstantDeltaBound {
|
||||||
var (
|
return InstantDeltaBound{
|
||||||
s8q = s.buf.GetByte(s.pos - 1)
|
Pos: s.pos - 1 - s.lastDeltaSize,
|
||||||
s8 = s.buf.GetByte(s.pos - 2)
|
H: s.h,
|
||||||
flag byte = 1 << uint(8-s8q)
|
LastDelta: s.lastDelta,
|
||||||
)
|
|
||||||
|
|
||||||
if s.length > 0 {
|
|
||||||
if s.length == 2 {
|
|
||||||
s.length = 0
|
|
||||||
s.buf.UnsetFlag(s.pos-2, flag)
|
|
||||||
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
|
||||||
s.numIdx--
|
|
||||||
s.pos--
|
|
||||||
} else if s.length < 130 {
|
|
||||||
s.length--
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
|
||||||
} else if s.length == 130 {
|
|
||||||
s.length--
|
|
||||||
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
|
||||||
s.numIdx--
|
|
||||||
s.pos--
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
|
||||||
} else {
|
|
||||||
s.length--
|
|
||||||
s.encode2bLength()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if s8q > 1 {
|
|
||||||
s8q--
|
|
||||||
flag = 1 << uint(8-s8q)
|
|
||||||
s.pos = s.numIdx + 2
|
|
||||||
s.buf.SetByte(s.pos-2, s8)
|
|
||||||
s.buf.SetByte(s.pos-1, s8q)
|
|
||||||
} else {
|
|
||||||
s.pos = s.numIdx + 1
|
|
||||||
s.buf.SetByte(s.pos-1, 8)
|
|
||||||
s8 = s.buf.GetByte(s.pos - 2)
|
|
||||||
flag = 1
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
pos = s.pos - 3
|
|
||||||
n int
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
s.lastDelta, n, err = s.buf.ReverseGetVarInt64(pos)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
s.numIdx = pos - n
|
|
||||||
if (s8 & flag) == flag {
|
|
||||||
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DECOMPRESSOR
|
||||||
|
|
||||||
type ReverseInstantDeltaDecompressor struct {
|
type ReverseInstantDeltaDecompressor struct {
|
||||||
step byte
|
buf *conbuf.ContinuousBuffer
|
||||||
buf *conbuf.ContinuousBuffer
|
coef float64
|
||||||
pos int
|
pos int
|
||||||
bound int
|
bound int
|
||||||
firstValue float64
|
baseValue float64
|
||||||
lastValue float64
|
lastValue float64
|
||||||
length uint16
|
isRun bool
|
||||||
coef float64
|
pending int
|
||||||
idxOf8 uint
|
done bool
|
||||||
s8 byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor {
|
func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor {
|
||||||
@@ -278,68 +176,169 @@ func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int,
|
|||||||
if fracDigits > 0 {
|
if fracDigits > 0 {
|
||||||
coef = math.Pow(10, float64(fracDigits))
|
coef = math.Pow(10, float64(fracDigits))
|
||||||
}
|
}
|
||||||
return &ReverseInstantDeltaDecompressor{
|
i64, n, err := buf.GetVarInt64(0)
|
||||||
buf: buf,
|
if err != nil {
|
||||||
coef: coef,
|
log.Fatalf("bug: get base value: %s", err)
|
||||||
pos: size,
|
|
||||||
}
|
}
|
||||||
|
//fmt.Printf("baseValue: %.2f, bound: %d, pos: %d\n", float64(u64)/coef, n, size)
|
||||||
|
return &ReverseInstantDeltaDecompressor{
|
||||||
|
buf: buf,
|
||||||
|
coef: coef,
|
||||||
|
pos: size - 1, // last elem
|
||||||
|
baseValue: float64(i64) / coef,
|
||||||
|
bound: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaDecompressor) RestoreFromEnd() {
|
||||||
|
if s.pos > s.bound {
|
||||||
|
// читаю заголовок наступної серії
|
||||||
|
s.readHeader()
|
||||||
|
s.readValue()
|
||||||
|
} else {
|
||||||
|
s.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaDecompressor) RestoreFromBound(bound InstantDeltaBound) {
|
||||||
|
s.pos = bound.Pos - 1
|
||||||
|
s.lastValue = s.baseValue + float64(bound.LastDelta)
|
||||||
|
s.decodeHeaderByte(bound.H)
|
||||||
|
fmt.Printf("restore from bound: isRun=%t, pending=%d\n", s.isRun, s.pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) {
|
func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) {
|
||||||
if s.step > 0 {
|
//fmt.Printf("NextValue(): bound: %d, pos: %d, pending: %d\n", s.bound, s.pos, s.pending)
|
||||||
if s.length > 0 {
|
if s.done {
|
||||||
s.length--
|
return 0, true
|
||||||
return s.lastValue, false
|
|
||||||
}
|
|
||||||
if s.pos < s.bound {
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.idxOf8 == 0 {
|
|
||||||
s.s8 = s.buf.GetByte(s.pos)
|
|
||||||
s.pos--
|
|
||||||
}
|
|
||||||
s.readVar()
|
|
||||||
if s.length > 0 {
|
|
||||||
s.length--
|
|
||||||
}
|
|
||||||
return s.lastValue, false
|
|
||||||
}
|
}
|
||||||
i64, n, err := s.buf.GetVarInt64(0)
|
// повертаю значення, що було прочитано в методі RestoreFromBound/RestoreFromEnd
|
||||||
if err != nil {
|
value = s.lastValue
|
||||||
panic(err)
|
s.pending--
|
||||||
|
if s.pending > 0 {
|
||||||
|
// якщо в серії залишаються елементи
|
||||||
|
if !s.isRun {
|
||||||
|
s.readValue()
|
||||||
|
}
|
||||||
|
} else if s.pos > s.bound {
|
||||||
|
// в серії більше немає елементів, отже перевіряє чи є ще дані в буфері.
|
||||||
|
// дані є - читаю заголовок наступної серії
|
||||||
|
s.readHeader()
|
||||||
|
s.readValue()
|
||||||
|
} else {
|
||||||
|
s.done = true
|
||||||
}
|
}
|
||||||
s.firstValue = float64(i64) / s.coef
|
// серія завершена - перевіряю чи є ще серії
|
||||||
s.bound = n
|
return value, false
|
||||||
s.pos--
|
|
||||||
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
|
|
||||||
s.pos--
|
|
||||||
s.s8 = s.buf.GetByte(s.pos)
|
|
||||||
s.pos--
|
|
||||||
s.readVar()
|
|
||||||
if s.length > 0 {
|
|
||||||
s.length--
|
|
||||||
}
|
|
||||||
s.step = 1
|
|
||||||
return s.lastValue, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ReverseInstantDeltaDecompressor) readVar() {
|
func (s *ReverseInstantDeltaDecompressor) readHeader() {
|
||||||
|
h := s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
s.decodeHeaderByte(h)
|
||||||
|
// fmt.Println("h:", h)
|
||||||
|
// fmt.Println("isRun:", s.isRun)
|
||||||
|
// fmt.Println("pending:", s.pending)
|
||||||
|
}
|
||||||
|
func (s *ReverseInstantDeltaDecompressor) readValue() {
|
||||||
i64, n, err := s.buf.ReverseGetVarInt64(s.pos)
|
i64, n, err := s.buf.ReverseGetVarInt64(s.pos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
// fmt.Println()
|
||||||
|
// fmt.Println("read from pos:", s.pos)
|
||||||
|
// fmt.Println("read delta:", u64)
|
||||||
|
// fmt.Println("read delta n:", n)
|
||||||
|
// fmt.Println()
|
||||||
s.pos -= n
|
s.pos -= n
|
||||||
s.lastValue = s.firstValue + float64(i64)/s.coef
|
s.lastValue = s.baseValue + float64(i64)/s.coef
|
||||||
|
}
|
||||||
|
|
||||||
var flag byte = 1 << s.idxOf8
|
func (s *ReverseInstantDeltaDecompressor) decodeHeaderByte(h byte) {
|
||||||
if (s.s8 & flag) == flag {
|
s.isRun = h < 128
|
||||||
s.length, n = s.buf.DecodeRunLength(s.pos)
|
if s.isRun {
|
||||||
s.pos -= n
|
s.pending = int(h&127) + 2
|
||||||
}
|
|
||||||
if s.idxOf8 == 7 {
|
|
||||||
s.idxOf8 = 0
|
|
||||||
} else {
|
} else {
|
||||||
s.idxOf8++
|
s.pending = int(h&127) + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DECOMPRESSOR
|
||||||
|
|
||||||
|
// type ReverseInstantDeltaDecompressor struct {
|
||||||
|
// buf *conbuf.ContinuousBuffer
|
||||||
|
// pos int
|
||||||
|
// bound int
|
||||||
|
// baseValue float64
|
||||||
|
// lastValue float64
|
||||||
|
// coef float64
|
||||||
|
// isRun bool
|
||||||
|
// pending int
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor {
|
||||||
|
// var coef float64 = 1
|
||||||
|
// if fracDigits > 0 {
|
||||||
|
// coef = math.Pow(10, float64(fracDigits))
|
||||||
|
// }
|
||||||
|
// u64, n, err := buf.GetVarInt64(0)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalf("bug: get base value: %s", err)
|
||||||
|
// }
|
||||||
|
// //fmt.Printf("baseValue: %.2f, bound: %d, pos: %d\n", float64(u64)/coef, n, size)
|
||||||
|
// return &ReverseInstantDeltaDecompressor{
|
||||||
|
// buf: buf,
|
||||||
|
// coef: coef,
|
||||||
|
// pos: size - 1, // last elem
|
||||||
|
// baseValue: float64(u64) / coef,
|
||||||
|
// bound: n,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) {
|
||||||
|
// //fmt.Printf("NextValue(): bound: %d, pos: %d, pending: %d\n", s.bound, s.pos, s.pending)
|
||||||
|
// if s.pending > 0 {
|
||||||
|
// // якщо в серії залишаються елементи
|
||||||
|
// if !s.isRun {
|
||||||
|
// s.readValue()
|
||||||
|
// }
|
||||||
|
// s.pending--
|
||||||
|
// return s.lastValue, false
|
||||||
|
// }
|
||||||
|
// if s.pos > s.bound {
|
||||||
|
// // читаю заголовок наступної серії
|
||||||
|
// s.readHeader()
|
||||||
|
// s.readValue()
|
||||||
|
// s.pending--
|
||||||
|
// return s.lastValue, false
|
||||||
|
// }
|
||||||
|
// // серія завершена - перевіряю чи є ще серії
|
||||||
|
// return 0, true
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (s *ReverseInstantDeltaDecompressor) readHeader() {
|
||||||
|
// h := s.buf.GetByte(s.pos)
|
||||||
|
// s.pos--
|
||||||
|
// s.isRun = h < 128
|
||||||
|
// if s.isRun {
|
||||||
|
// s.pending = int(h&127) + 2
|
||||||
|
// } else {
|
||||||
|
// s.pending = int(h&127) + 1
|
||||||
|
// }
|
||||||
|
// // fmt.Println("h:", h)
|
||||||
|
// // fmt.Println("isRun:", s.isRun)
|
||||||
|
// // fmt.Println("pending:", s.pending)
|
||||||
|
// }
|
||||||
|
// func (s *ReverseInstantDeltaDecompressor) readValue() {
|
||||||
|
// i64, n, err := s.buf.ReverseGetVarInt64(s.pos)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalln(err)
|
||||||
|
// }
|
||||||
|
// // fmt.Println()
|
||||||
|
// // fmt.Println("read from pos:", s.pos)
|
||||||
|
// // fmt.Println("read delta:", u64)
|
||||||
|
// // fmt.Println("read delta n:", n)
|
||||||
|
// // fmt.Println()
|
||||||
|
// s.pos -= n
|
||||||
|
// s.lastValue = s.baseValue + float64(i64)/s.coef
|
||||||
|
// }
|
||||||
|
|||||||
288
chunkenc/time_delta.go
Normal file
288
chunkenc/time_delta.go
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
package chunkenc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/pretty"
|
||||||
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReverseTimeDeltaCompressor struct {
|
||||||
|
buf *conbuf.ContinuousBuffer
|
||||||
|
pos int
|
||||||
|
lastUnixtime uint32
|
||||||
|
lastDelta uint32
|
||||||
|
lastDeltaSize int
|
||||||
|
h byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReverseTimeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaCompressor {
|
||||||
|
s := &ReverseTimeDeltaCompressor{
|
||||||
|
buf: buf,
|
||||||
|
pos: size, // перший вільний байт
|
||||||
|
}
|
||||||
|
if size > 0 {
|
||||||
|
u64, n, err := s.buf.ReverseGetVarUint64(s.pos - 1)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("bug: get last unixtime: %s", err)
|
||||||
|
}
|
||||||
|
s.lastUnixtime = uint32(u64)
|
||||||
|
s.pos -= n
|
||||||
|
if s.pos > 0 {
|
||||||
|
s.h = s.buf.GetByte(s.pos - 1)
|
||||||
|
u64, s.lastDeltaSize, err = s.buf.ReverseGetVarUint64(s.pos - 2)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("bug: get last delta: %s", err)
|
||||||
|
}
|
||||||
|
s.lastDelta = uint32(u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaCompressor) Size() int {
|
||||||
|
return s.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (s *ReverseTimeDeltaCompressor) CalcRequiredSpace(value float64) int {
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaCompressor) Append(unixtime uint32) {
|
||||||
|
if s.lastUnixtime > 0 {
|
||||||
|
// base value
|
||||||
|
//s.pos += s.buf.PutVarUint64(s.pos, uint64(value*s.coef))
|
||||||
|
//s.baseValue = value
|
||||||
|
//s.appendNewLiteral(0)
|
||||||
|
|
||||||
|
//} else {
|
||||||
|
delta := unixtime - s.lastUnixtime
|
||||||
|
if delta == s.lastDelta {
|
||||||
|
if s.h < 128 {
|
||||||
|
// run блок - отже треба збільшити лічильник
|
||||||
|
if s.h < 127 {
|
||||||
|
// increase counter
|
||||||
|
s.h++
|
||||||
|
s.buf.SetByte(s.pos-1, s.h)
|
||||||
|
} else {
|
||||||
|
// не можу збільшити - буде переповнення. Додаю новий literal блок
|
||||||
|
// counter overflow
|
||||||
|
s.appendNewLiteral(delta)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// literal блок.
|
||||||
|
// Якщо в ньому лише одне значення - перетворюю його на run блок.
|
||||||
|
// Інакше забираю останнє значення із literal блока і додаю новий run блок.
|
||||||
|
q := s.h & 127
|
||||||
|
if q == 0 { // 1 кодується як 0
|
||||||
|
s.convertLiteralToRun()
|
||||||
|
} else {
|
||||||
|
// забираю останнє значення із literal блоку щоб зробити run блок
|
||||||
|
s.convertLastFromLiteralToRun()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s.h < 127 { // fix 128?
|
||||||
|
// end of run
|
||||||
|
s.appendNewLiteral(delta)
|
||||||
|
} else {
|
||||||
|
if s.h < 255 {
|
||||||
|
// encode value from pos - 1, then append h byte
|
||||||
|
s.appendDeltaToLiteral(delta)
|
||||||
|
} else {
|
||||||
|
// overflowed - encode new
|
||||||
|
s.appendNewLiteral(delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.lastUnixtime = unixtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaCompressor) convertLastFromLiteralToRun() {
|
||||||
|
// Зменшую кількість елементів в literal блоці
|
||||||
|
s.h--
|
||||||
|
s.pos -= 1 + s.lastDeltaSize
|
||||||
|
s.buf.SetByte(s.pos, s.h) // закриваю literal блок
|
||||||
|
s.pos++
|
||||||
|
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, uint64(s.lastDelta))
|
||||||
|
s.pos += s.lastDeltaSize
|
||||||
|
s.h = 0 // run блок, довжини 2
|
||||||
|
s.buf.SetByte(s.pos, s.h)
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaCompressor) convertLiteralToRun() {
|
||||||
|
// Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
|
||||||
|
s.h = 0
|
||||||
|
s.buf.SetByte(s.pos-1, s.h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaCompressor) appendDeltaToLiteral(delta uint32) {
|
||||||
|
s.h++ // збільшую к-сть дельт
|
||||||
|
s.lastDelta = delta
|
||||||
|
s.pos--
|
||||||
|
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, uint64(delta))
|
||||||
|
s.pos += s.lastDeltaSize
|
||||||
|
s.buf.SetByte(s.pos, s.h)
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaCompressor) appendNewLiteral(delta uint32) {
|
||||||
|
//fmt.Println("appendNewLiteral", delta)
|
||||||
|
s.h = flagLiteral
|
||||||
|
s.lastDelta = delta
|
||||||
|
s.lastDeltaSize = s.buf.ReversePutVarUint64(s.pos, uint64(delta))
|
||||||
|
s.pos += s.lastDeltaSize
|
||||||
|
s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaCompressor) DeleteLast() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaCompressor) Sync() {
|
||||||
|
s.pos += s.buf.ReversePutVarUint64(s.pos, uint64(s.lastUnixtime))
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimeDeltaBound struct {
|
||||||
|
Pos int
|
||||||
|
H byte
|
||||||
|
LastUnixtime uint32
|
||||||
|
LastDelta uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// delta h
|
||||||
|
func (s *ReverseTimeDeltaCompressor) GetState() TimeDeltaBound {
|
||||||
|
bound := TimeDeltaBound{
|
||||||
|
LastUnixtime: s.lastUnixtime,
|
||||||
|
}
|
||||||
|
if s.pos > 0 {
|
||||||
|
bound.Pos = s.pos - 1 - s.lastDeltaSize
|
||||||
|
bound.H = s.h
|
||||||
|
bound.LastDelta = s.lastDelta
|
||||||
|
}
|
||||||
|
return bound
|
||||||
|
}
|
||||||
|
|
||||||
|
// DECOMPRESSOR
|
||||||
|
|
||||||
|
type ReverseTimeDeltaDecompressor struct {
|
||||||
|
buf *conbuf.ContinuousBuffer
|
||||||
|
pos int
|
||||||
|
lastDelta uint32
|
||||||
|
lastUnixtime uint32
|
||||||
|
isRegular bool
|
||||||
|
isRun bool
|
||||||
|
pending int
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReverseTimeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaDecompressor {
|
||||||
|
return &ReverseTimeDeltaDecompressor{
|
||||||
|
buf: buf,
|
||||||
|
pos: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaDecompressor) RestoreFromEnd() {
|
||||||
|
fmt.Println("RestoreFromEnd", s.pos)
|
||||||
|
if s.pos > 0 {
|
||||||
|
s.pos-- // перший байт даних
|
||||||
|
u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("bug: get last unixtime: %s", err)
|
||||||
|
}
|
||||||
|
s.lastUnixtime = uint32(u64)
|
||||||
|
s.pos -= n
|
||||||
|
fmt.Println("restored", s.lastUnixtime, s.pos)
|
||||||
|
if s.pos > 0 {
|
||||||
|
s.readHeader()
|
||||||
|
s.readDelta()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaDecompressor) RestoreFromBound(bound TimeDeltaBound) {
|
||||||
|
pretty.Println(bound)
|
||||||
|
s.lastUnixtime = bound.LastUnixtime
|
||||||
|
if bound.LastDelta > 0 {
|
||||||
|
s.pos = bound.Pos - 1
|
||||||
|
s.lastDelta = bound.LastDelta
|
||||||
|
s.decodeHeaderByte(bound.H)
|
||||||
|
} else {
|
||||||
|
s.pos = -1
|
||||||
|
}
|
||||||
|
fmt.Printf("restore from bound: pos=%d, isRun=%t, pending=%d\n", s.pos, s.isRun, s.pending)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaDecompressor) NextValue() (value uint32, done bool) {
|
||||||
|
//fmt.Printf("NextValue(): pos: %d, isRegular: %t, pending: %d\n", s.pos, s.isRegular, s.pending)
|
||||||
|
if s.done {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
// повертаю значення, що було прочитано в методі RestoreFromBound/RestoreFromEnd
|
||||||
|
value = s.lastUnixtime
|
||||||
|
if s.isRegular {
|
||||||
|
s.pending--
|
||||||
|
if s.pending > 0 {
|
||||||
|
// якщо в серії залишаються елементи
|
||||||
|
if !s.isRun {
|
||||||
|
s.readDelta()
|
||||||
|
}
|
||||||
|
s.lastUnixtime -= s.lastDelta
|
||||||
|
} else if s.pos > 0 {
|
||||||
|
// в серії більше немає елементів, отже перевіряє чи є ще дані в буфері.
|
||||||
|
// дані є - читаю заголовок наступної серії
|
||||||
|
s.readHeader()
|
||||||
|
s.readDelta()
|
||||||
|
s.lastUnixtime -= s.lastDelta
|
||||||
|
} else {
|
||||||
|
s.done = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s.lastDelta > 0 {
|
||||||
|
s.isRegular = true
|
||||||
|
s.lastUnixtime -= s.lastDelta
|
||||||
|
} else {
|
||||||
|
// був закодований лише last unixtime
|
||||||
|
s.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaDecompressor) readHeader() {
|
||||||
|
h := s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
s.decodeHeaderByte(h)
|
||||||
|
// fmt.Println("h:", h)
|
||||||
|
// fmt.Println("isRun:", s.isRun)
|
||||||
|
// fmt.Println("pending:", s.pending)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaDecompressor) decodeHeaderByte(h byte) {
|
||||||
|
s.isRun = h < 128
|
||||||
|
if s.isRun {
|
||||||
|
s.pending = int(h&127) + 2
|
||||||
|
} else {
|
||||||
|
s.pending = int(h&127) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaDecompressor) readDelta() {
|
||||||
|
u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
// fmt.Println()
|
||||||
|
// fmt.Println("read from pos:", s.pos)
|
||||||
|
// fmt.Println("read delta:", u64)
|
||||||
|
// fmt.Println("read delta n:", n)
|
||||||
|
// fmt.Println()
|
||||||
|
s.pos -= n
|
||||||
|
s.lastDelta = uint32(u64)
|
||||||
|
}
|
||||||
@@ -1,374 +1,270 @@
|
|||||||
package chunkenc
|
package chunkenc
|
||||||
|
|
||||||
import (
|
// // REVERSE
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"gordenko.dev/dima/qb/bin"
|
// // const (
|
||||||
"gordenko.dev/dima/qb/conbuf"
|
// // lastUnixtimeIdx = 0
|
||||||
)
|
// // baseDeltaIdx = 4
|
||||||
|
// // )
|
||||||
|
|
||||||
// REVERSE
|
// type ReverseTimeDeltaOfDeltaCompressor struct {
|
||||||
|
// buf *conbuf.ContinuousBuffer
|
||||||
|
// pos int
|
||||||
|
// lastDelta uint32
|
||||||
|
// lastUnixtime uint32
|
||||||
|
// lastDeltaOfDelta int64
|
||||||
|
// lastDeltaOfDeltaSize int
|
||||||
|
// h byte
|
||||||
|
// }
|
||||||
|
|
||||||
const (
|
// func NewReverseTimeDeltaOfDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaCompressor {
|
||||||
lastUnixtimeIdx = 0
|
// s := &ReverseTimeDeltaOfDeltaCompressor{
|
||||||
baseDeltaIdx = 4
|
// buf: buf,
|
||||||
)
|
// pos: size,
|
||||||
|
// }
|
||||||
|
// if size > 0 {
|
||||||
|
// // s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
|
||||||
|
// // u64, _, err := s.buf.GetVarUint64(baseDeltaIdx)
|
||||||
|
// // if err != nil {
|
||||||
|
// // log.Fatalf("bug: get base delta: %s", err)
|
||||||
|
// // }
|
||||||
|
// // s.baseDelta = uint32(u64)
|
||||||
|
// // s.h = s.buf.GetByte(s.pos - 1)
|
||||||
|
// // s.lastDeltaOfDelta, s.lastDeltaOfDeltaSize, err = s.buf.ReverseGetVarInt64(s.pos - 2)
|
||||||
|
// // if err != nil {
|
||||||
|
// // log.Fatalf("bug: get last delta of delta: %s", err)
|
||||||
|
// // }
|
||||||
|
// } else {
|
||||||
|
// //s.baseDelta = baseDelta
|
||||||
|
// }
|
||||||
|
// return s
|
||||||
|
// }
|
||||||
|
|
||||||
type ReverseTimeDeltaOfDeltaCompressor struct {
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) Size() int {
|
||||||
buf *conbuf.ContinuousBuffer
|
// return s.pos
|
||||||
pos int
|
// }
|
||||||
baseDelta uint32
|
|
||||||
lastUnixtime uint32
|
|
||||||
lastDeltaOfDelta int64
|
|
||||||
length uint16
|
|
||||||
numIdx int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReverseTimeDeltaOfDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaCompressor {
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) Append(unixtime uint32) {
|
||||||
s := &ReverseTimeDeltaOfDeltaCompressor{
|
// if s.lastUnixtime > 0 {
|
||||||
buf: buf,
|
// if s.pos == 0 {
|
||||||
pos: size,
|
// // 2-й unixtime
|
||||||
}
|
// s.lastDelta = unixtime - s.lastUnixtime
|
||||||
if size > 0 {
|
// s.appendNewLiteral(0)
|
||||||
s.restoreState()
|
// } else {
|
||||||
}
|
// // 3-й unixtime і наступні
|
||||||
return s
|
// lastDelta := unixtime - s.lastUnixtime
|
||||||
}
|
// deltaOfDelta := int64(lastDelta) - int64(s.lastDelta)
|
||||||
|
// if deltaOfDelta == s.lastDeltaOfDelta {
|
||||||
|
// if s.h < 128 {
|
||||||
|
// // run блок - отже треба збільшити лічильник
|
||||||
|
// if s.h < 127 {
|
||||||
|
// // increase counter
|
||||||
|
// s.h++
|
||||||
|
// s.buf.SetByte(s.pos-1, s.h)
|
||||||
|
// } else {
|
||||||
|
// // не можу збільшити - буде переповнення. Додаю новий literal блок
|
||||||
|
// // counter overflow
|
||||||
|
// // fix encode delta
|
||||||
|
// s.appendNewLiteral(deltaOfDelta)
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // literal блок.
|
||||||
|
// // Якщо в ньому лише одне значення - перетворюю його на run блок.
|
||||||
|
// // Інакше забираю останнє значення із literal блока і додаю новий run блок.
|
||||||
|
// q := s.h & 127
|
||||||
|
// if q == 0 { // 1 кодується як 0
|
||||||
|
// s.convertLiteralToRun()
|
||||||
|
// } else {
|
||||||
|
// s.convertLastFromLiteralToRun(deltaOfDelta)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// s.lastDelta = lastDelta
|
||||||
|
// if s.h < 127 {
|
||||||
|
// // end of run
|
||||||
|
// s.appendNewLiteral(deltaOfDelta)
|
||||||
|
// } else {
|
||||||
|
// if s.h < 255 {
|
||||||
|
// // encode value from pos - 1, then append h byte
|
||||||
|
// s.appendDeltaToLiteral(deltaOfDelta)
|
||||||
|
// } else {
|
||||||
|
// // overflowed - encode new
|
||||||
|
// s.appendNewLiteral(deltaOfDelta)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // для першого unixtime - лише оновлюємо lastUnixtime, а для наступних -
|
||||||
|
// // теж оновлюємо, але після запису серій в buf.
|
||||||
|
// s.lastUnixtime = unixtime
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) restoreState() {
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) convertLastFromLiteralToRun(deltaOfDelta int64) {
|
||||||
s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
|
// // Зменшую кількість елементів в literal блоці
|
||||||
if s.pos > 4 {
|
// s.h--
|
||||||
u64, _, err := s.buf.GetVarUint64(baseDeltaIdx)
|
// s.pos -= 1 + s.lastDeltaOfDeltaSize
|
||||||
if err != nil {
|
// s.buf.SetByte(s.pos, s.h) // закриваю literal блок
|
||||||
panic(fmt.Sprintf("bug: get base delta: %s", err))
|
// s.pos++
|
||||||
}
|
// s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
|
||||||
s.baseDelta = uint32(u64)
|
// s.pos += s.lastDeltaOfDeltaSize
|
||||||
|
// s.h = 0 // run блок, довжини 2
|
||||||
|
// s.buf.SetByte(s.pos, s.h)
|
||||||
|
// s.pos++
|
||||||
|
// }
|
||||||
|
|
||||||
pos := s.pos - 1
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) convertLiteralToRun() {
|
||||||
idxOf8 := uint(8 - s.buf.GetByte(pos))
|
// // Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
|
||||||
pos--
|
// s.h = 0
|
||||||
s8 := s.buf.GetByte(pos)
|
// s.buf.SetByte(s.pos-1, s.h)
|
||||||
pos--
|
// }
|
||||||
|
|
||||||
var n int
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) appendDeltaToLiteral(deltaOfDelta int64) {
|
||||||
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(pos)
|
// s.h++ // збільшую к-сть дельт
|
||||||
if err != nil {
|
// s.lastDeltaOfDelta = deltaOfDelta
|
||||||
panic(err)
|
// s.pos--
|
||||||
}
|
// s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
|
||||||
pos -= n
|
// s.pos += s.lastDeltaOfDeltaSize
|
||||||
s.numIdx = pos + 1
|
// s.buf.SetByte(s.pos, s.h)
|
||||||
var flag byte = 1 << idxOf8
|
// s.pos++
|
||||||
if (s8 & flag) == flag {
|
// }
|
||||||
s.length, _ = s.buf.DecodeRunLength(pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) Size() int {
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) appendNewLiteral(deltaOfDelta int64) {
|
||||||
return s.pos
|
// s.h = flagLiteral
|
||||||
}
|
// s.lastDeltaOfDelta = deltaOfDelta
|
||||||
|
// s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
|
||||||
|
// s.pos += s.lastDeltaOfDeltaSize
|
||||||
|
// s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1
|
||||||
|
// s.pos++
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) CalcRequiredSpace(unixtime uint32) int {
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) Sync() {
|
||||||
if s.pos == 0 {
|
// // дописую в кінець lastDelta та lastUnixtime
|
||||||
return 4
|
// if s.pos > 0 {
|
||||||
}
|
// n := s.buf.ReversePutVarUint64(s.pos, uint64(s.lastDelta))
|
||||||
|
// s.pos += n
|
||||||
|
// }
|
||||||
|
// if s.lastUnixtime > 0 {
|
||||||
|
// s.buf.PutUint32(s.pos, s.lastUnixtime)
|
||||||
|
// s.pos += 4
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if s.baseDelta == 0 {
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) DeleteLast() {
|
||||||
baseDelta := unixtime - s.lastUnixtime
|
|
||||||
n := bin.CalcVarUint64Length(uint64(baseDelta))
|
|
||||||
return n + 3
|
|
||||||
}
|
|
||||||
|
|
||||||
deltaOfDelta := int64(unixtime-s.lastUnixtime) - int64(s.baseDelta)
|
// }
|
||||||
if deltaOfDelta == s.lastDeltaOfDelta {
|
|
||||||
if s.length == 0 {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
newLength := s.length + 1
|
|
||||||
if newLength < 130 {
|
|
||||||
return 0
|
|
||||||
} else if newLength == 130 {
|
|
||||||
return 1
|
|
||||||
} else {
|
|
||||||
if newLength < 32769 {
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
n := bin.CalcVarInt64Length(deltaOfDelta)
|
|
||||||
n += 2
|
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
|
||||||
if s8q == 8 {
|
|
||||||
n -= 1
|
|
||||||
} else {
|
|
||||||
n -= 2
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n := bin.CalcVarInt64Length(deltaOfDelta)
|
|
||||||
n += 2
|
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
|
||||||
if s8q == 8 {
|
|
||||||
n -= 1
|
|
||||||
} else {
|
|
||||||
n -= 2
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) Append(unixtime uint32) {
|
// type TimeDeltaOfDeltaBound struct {
|
||||||
if s.pos == 0 {
|
// LastUnixtime uint32
|
||||||
s.lastUnixtime = unixtime
|
// LastDelta uint32
|
||||||
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
|
// Pos int
|
||||||
s.pos += 4
|
// H byte
|
||||||
return
|
// LastDeltaOfDelta int64
|
||||||
}
|
// }
|
||||||
|
|
||||||
if s.baseDelta == 0 {
|
// func (s *ReverseTimeDeltaOfDeltaCompressor) GetState() TimeDeltaOfDeltaBound {
|
||||||
s.baseDelta = unixtime - s.lastUnixtime
|
// return TimeDeltaOfDeltaBound{
|
||||||
s.lastDeltaOfDelta = 0
|
// LastUnixtime: s.lastUnixtime,
|
||||||
s.lastUnixtime = unixtime
|
// LastDelta: s.lastDelta,
|
||||||
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
|
// Pos: s.pos,
|
||||||
|
// H: s.h,
|
||||||
|
// LastDeltaOfDelta: s.lastDeltaOfDelta,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
n := s.buf.PutVarUint64(s.pos, uint64(s.baseDelta))
|
// // DECOMPRESSOR
|
||||||
s.pos += n
|
|
||||||
s.encodeNewDeltaOfDelta(0, 0, 1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
deltaOfDelta := int64(unixtime-s.lastUnixtime) - int64(s.baseDelta)
|
// type ReverseTimeDeltaOfDeltaDecompressor struct {
|
||||||
s.lastUnixtime = unixtime
|
// buf *conbuf.ContinuousBuffer
|
||||||
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
|
// size int
|
||||||
|
// pos int
|
||||||
|
// lastUnixtime uint32
|
||||||
|
// lastDelta uint32
|
||||||
|
// //lastDeltaOfDelta int64
|
||||||
|
// isRun bool
|
||||||
|
// pending int
|
||||||
|
// }
|
||||||
|
|
||||||
if deltaOfDelta == s.lastDeltaOfDelta {
|
// func NewReverseTimeDeltaOfDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaDecompressor {
|
||||||
if s.length == 0 {
|
// return &ReverseTimeDeltaOfDeltaDecompressor{
|
||||||
s.length = 2
|
// buf: buf,
|
||||||
s.shiftOnePosToRight()
|
// size: size,
|
||||||
s.buf.SetByte(s.numIdx-1, 0)
|
// pos: size, // last elem
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
// }
|
||||||
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
|
// }
|
||||||
} else {
|
|
||||||
s.length++
|
|
||||||
if s.length < 130 {
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
|
|
||||||
} else if s.length == 130 {
|
|
||||||
s.shiftOnePosToRight()
|
|
||||||
s.encode2bLength()
|
|
||||||
} else {
|
|
||||||
if s.length < 32769 {
|
|
||||||
s.encode2bLength()
|
|
||||||
} else {
|
|
||||||
s.appendNewDeltaOfDelta(deltaOfDelta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.appendNewDeltaOfDelta(deltaOfDelta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) appendNewDeltaOfDelta(deltaOfDelta int64) {
|
// func (s *ReverseTimeDeltaOfDeltaDecompressor) NextValue() (value uint32, done bool) {
|
||||||
s.length = 0
|
// fmt.Printf("\nNextValue(): pos: %d, size: %d, pending: %d\n", s.pos, s.size, s.pending)
|
||||||
s8 := s.buf.GetByte(s.pos - 2)
|
// if s.pos < s.size {
|
||||||
s8q := s.buf.GetByte(s.pos - 1)
|
// if s.pending > 0 {
|
||||||
|
// //fmt.Println("pending", )
|
||||||
|
// // якщо в серії залишаються елементи
|
||||||
|
// fmt.Println("s.lastUnixtime -= s.lastDelta", s.lastUnixtime, s.lastDelta)
|
||||||
|
// s.lastUnixtime -= s.lastDelta
|
||||||
|
// if !s.isRun {
|
||||||
|
// s.readValue()
|
||||||
|
// }
|
||||||
|
// s.pending--
|
||||||
|
// return s.lastUnixtime, false
|
||||||
|
// }
|
||||||
|
// // FIX - читаю lastUnixtime
|
||||||
|
// if s.pos > 0 {
|
||||||
|
// s.lastUnixtime -= s.lastDelta
|
||||||
|
// // читаю заголовок наступної серії
|
||||||
|
// s.readHeader()
|
||||||
|
// s.readValue()
|
||||||
|
// s.pending--
|
||||||
|
// return s.lastUnixtime, false
|
||||||
|
// }
|
||||||
|
// // серія завершена - перевіряю чи є ще серії
|
||||||
|
// return 0, true
|
||||||
|
// } else {
|
||||||
|
// s.pos -= 4
|
||||||
|
// s.lastUnixtime = s.buf.GetUint32(s.pos)
|
||||||
|
// fmt.Println("lastUnix", s.lastUnixtime)
|
||||||
|
// if s.pos > 0 {
|
||||||
|
// s.pos--
|
||||||
|
// u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatalln(err)
|
||||||
|
// }
|
||||||
|
// s.pos -= n
|
||||||
|
// s.lastDelta = uint32(u64)
|
||||||
|
// fmt.Println("lastDelta", s.lastDelta)
|
||||||
|
// }
|
||||||
|
// return s.lastUnixtime, false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if s8q == 8 {
|
// func (s *ReverseTimeDeltaOfDeltaDecompressor) readHeader() {
|
||||||
s.pos -= 1
|
// h := s.buf.GetByte(s.pos)
|
||||||
s8 = 0
|
// s.pos--
|
||||||
s8q = 1
|
// s.isRun = h < 128
|
||||||
} else {
|
// if s.isRun {
|
||||||
s.pos -= 2
|
// s.pending = int(h&127) + 2
|
||||||
s8q++
|
// } else {
|
||||||
}
|
// s.pending = int(h&127) + 1
|
||||||
|
// }
|
||||||
s.encodeNewDeltaOfDelta(deltaOfDelta, s8, s8q)
|
// fmt.Println("h:", h)
|
||||||
}
|
// fmt.Println("isRun:", s.isRun)
|
||||||
|
// fmt.Println("pending:", s.pending)
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) encodeNewDeltaOfDelta(deltaOfDelta int64, s8 byte, s8q byte) {
|
// }
|
||||||
s.lastDeltaOfDelta = deltaOfDelta
|
// func (s *ReverseTimeDeltaOfDeltaDecompressor) readValue() {
|
||||||
s.numIdx = s.pos
|
// //s.lastUnixtime -= s.lastDelta
|
||||||
n := s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
|
// // var (
|
||||||
s.pos += n
|
// // err error
|
||||||
s.buf.SetByte(s.pos, s8)
|
// // n int
|
||||||
s.pos++
|
// // )
|
||||||
s.buf.SetByte(s.pos, s8q)
|
// lastDeltaOfDelta, n, err := s.buf.ReverseGetVarInt64(s.pos)
|
||||||
s.pos++
|
// if err != nil {
|
||||||
}
|
// log.Fatalln(err)
|
||||||
|
// }
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) shiftOnePosToRight() {
|
// fmt.Println()
|
||||||
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
|
// fmt.Println("read Value from pos:", s.pos)
|
||||||
s.pos++
|
// fmt.Println("delta of delta:", lastDeltaOfDelta)
|
||||||
s.numIdx++
|
// //fmt.Println("delta of delta n:", n)
|
||||||
}
|
// s.pos -= n
|
||||||
|
// s.lastDelta = uint32(int64(s.lastDelta) - lastDeltaOfDelta)
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) encode2bLength() {
|
// fmt.Println("last delta:", s.lastDelta)
|
||||||
num := s.length - 2
|
// fmt.Println()
|
||||||
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
|
// }
|
||||||
s.buf.SetByte(s.numIdx-2, byte(num>>7))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseTimeDeltaOfDeltaCompressor) DeleteLast() {
|
|
||||||
var (
|
|
||||||
s8q = s.buf.GetByte(s.pos - 1)
|
|
||||||
s8 = s.buf.GetByte(s.pos - 2)
|
|
||||||
flag byte = 1 << uint(8-s8q)
|
|
||||||
)
|
|
||||||
|
|
||||||
if s.length > 0 {
|
|
||||||
if s.length == 2 {
|
|
||||||
s.length = 0
|
|
||||||
s.buf.UnsetFlag(s.pos-2, flag)
|
|
||||||
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
|
||||||
s.numIdx--
|
|
||||||
s.pos--
|
|
||||||
} else if s.length < 130 {
|
|
||||||
s.length--
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
|
||||||
} else if s.length == 130 {
|
|
||||||
s.length--
|
|
||||||
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
|
||||||
s.numIdx--
|
|
||||||
s.pos--
|
|
||||||
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
|
||||||
} else {
|
|
||||||
s.length--
|
|
||||||
s.encode2bLength()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if s8q > 1 {
|
|
||||||
s8q--
|
|
||||||
flag = 1 << uint(8-s8q)
|
|
||||||
s.pos = s.numIdx + 2
|
|
||||||
s.buf.SetByte(s.pos-2, s8)
|
|
||||||
s.buf.SetByte(s.pos-1, s8q)
|
|
||||||
} else {
|
|
||||||
s.pos = s.numIdx + 1
|
|
||||||
s.buf.SetByte(s.pos-1, 8)
|
|
||||||
s8 = s.buf.GetByte(s.pos - 2)
|
|
||||||
flag = 1
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
pos = s.pos - 3
|
|
||||||
n int
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(pos)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
s.numIdx = pos - n
|
|
||||||
if (s8 & flag) == flag {
|
|
||||||
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
|
|
||||||
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
|
|
||||||
s.buf.PutUint32(lastUnixtimeIdx, s.lastUnixtime)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReverseTimeDeltaOfDeltaDecompressor struct {
|
|
||||||
step byte
|
|
||||||
buf *conbuf.ContinuousBuffer
|
|
||||||
pos int
|
|
||||||
bound int
|
|
||||||
lastUnixtime uint32
|
|
||||||
baseDelta uint32
|
|
||||||
lastDeltaOfDelta int64
|
|
||||||
length uint16
|
|
||||||
idxOf8 uint
|
|
||||||
s8 byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewReverseTimeDeltaOfDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaDecompressor {
|
|
||||||
return &ReverseTimeDeltaOfDeltaDecompressor{
|
|
||||||
buf: buf,
|
|
||||||
pos: size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseTimeDeltaOfDeltaDecompressor) NextValue() (value uint32, done bool) {
|
|
||||||
if s.step == 0 {
|
|
||||||
if s.pos == 0 {
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
|
|
||||||
s.step = 1
|
|
||||||
return s.lastUnixtime, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.step == 1 {
|
|
||||||
if s.pos == baseDeltaIdx {
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
u64, n, err := s.buf.GetVarUint64(baseDeltaIdx)
|
|
||||||
if err != nil {
|
|
||||||
panic("EOF")
|
|
||||||
}
|
|
||||||
s.bound = baseDeltaIdx + n
|
|
||||||
s.baseDelta = uint32(u64)
|
|
||||||
|
|
||||||
s.pos--
|
|
||||||
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
|
|
||||||
s.pos--
|
|
||||||
s.s8 = s.buf.GetByte(s.pos)
|
|
||||||
s.pos--
|
|
||||||
|
|
||||||
s.readVar()
|
|
||||||
if s.length > 0 {
|
|
||||||
s.length--
|
|
||||||
}
|
|
||||||
s.step = 2
|
|
||||||
return s.lastUnixtime, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.length > 0 {
|
|
||||||
s.length--
|
|
||||||
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
|
|
||||||
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
|
|
||||||
return s.lastUnixtime, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.pos < s.bound {
|
|
||||||
return 0, true
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.idxOf8 == 0 {
|
|
||||||
s.s8 = s.buf.GetByte(s.pos)
|
|
||||||
s.pos--
|
|
||||||
}
|
|
||||||
s.readVar()
|
|
||||||
if s.length > 0 {
|
|
||||||
s.length--
|
|
||||||
}
|
|
||||||
return s.lastUnixtime, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ReverseTimeDeltaOfDeltaDecompressor) readVar() {
|
|
||||||
var (
|
|
||||||
n int
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(s.pos)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
s.pos -= n
|
|
||||||
|
|
||||||
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
|
|
||||||
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
|
|
||||||
|
|
||||||
var flag byte = 1 << s.idxOf8
|
|
||||||
if (s.s8 & flag) == flag {
|
|
||||||
s.length, n = s.buf.DecodeRunLength(s.pos)
|
|
||||||
s.pos -= n
|
|
||||||
}
|
|
||||||
if s.idxOf8 == 7 {
|
|
||||||
s.idxOf8 = 0
|
|
||||||
} else {
|
|
||||||
s.idxOf8++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
3
chunkenc2/chunckenc.go
Normal file
3
chunkenc2/chunckenc.go
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package chunkenc2
|
||||||
|
|
||||||
|
const eps = 0.000001
|
||||||
330
chunkenc2/cumdelta.go
Normal file
330
chunkenc2/cumdelta.go
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
package chunkenc2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/qb/bin"
|
||||||
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// REVERSE
|
||||||
|
|
||||||
|
type ReverseCumulativeDeltaCompressor struct {
|
||||||
|
buf *conbuf.ContinuousBuffer
|
||||||
|
coef float64
|
||||||
|
pos int
|
||||||
|
firstValue float64
|
||||||
|
lastDelta uint64
|
||||||
|
length uint16
|
||||||
|
numIdx int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReverseCumulativeDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaCompressor {
|
||||||
|
var coef float64 = 1
|
||||||
|
if fracDigits > 0 {
|
||||||
|
coef = math.Pow(10, float64(fracDigits))
|
||||||
|
}
|
||||||
|
s := &ReverseCumulativeDeltaCompressor{
|
||||||
|
buf: buf,
|
||||||
|
pos: size,
|
||||||
|
coef: coef,
|
||||||
|
}
|
||||||
|
if size > 0 {
|
||||||
|
s.restoreState()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) restoreState() {
|
||||||
|
u64, n, err := s.buf.GetVarUint64(0)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("bug: get first value: %s", err))
|
||||||
|
}
|
||||||
|
s.firstValue = float64(u64) / s.coef
|
||||||
|
|
||||||
|
if s.pos > n {
|
||||||
|
pos := s.pos - 1
|
||||||
|
idxOf8 := uint(8 - s.buf.GetByte(pos))
|
||||||
|
pos--
|
||||||
|
s8 := s.buf.GetByte(pos)
|
||||||
|
pos--
|
||||||
|
|
||||||
|
var n int
|
||||||
|
s.lastDelta, n, err = s.buf.ReverseGetVarUint64(pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("bug: get last delta: %s", err))
|
||||||
|
}
|
||||||
|
pos -= n
|
||||||
|
s.numIdx = pos + 1
|
||||||
|
|
||||||
|
var flag byte = 1 << idxOf8
|
||||||
|
if (s8 & flag) == flag {
|
||||||
|
s.length, _ = s.buf.DecodeRunLength(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) Size() int {
|
||||||
|
return s.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) CalcRequiredSpace(value float64) int {
|
||||||
|
if s.pos == 0 {
|
||||||
|
n := bin.CalcVarUint64Length(uint64(value * s.coef))
|
||||||
|
return n + 3
|
||||||
|
}
|
||||||
|
delta := uint64((value-s.firstValue)*s.coef + eps)
|
||||||
|
if delta == s.lastDelta {
|
||||||
|
if s.length == 0 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
newLength := s.length + 1
|
||||||
|
if newLength < 130 {
|
||||||
|
return 0
|
||||||
|
} else if newLength == 130 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
if newLength < 32769 {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
n := bin.CalcVarUint64Length(delta)
|
||||||
|
n += 2
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
if s8q == 8 {
|
||||||
|
n -= 1
|
||||||
|
} else {
|
||||||
|
n -= 2
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n := bin.CalcVarUint64Length(delta)
|
||||||
|
n += 2
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
if s8q == 8 {
|
||||||
|
n -= 1
|
||||||
|
} else {
|
||||||
|
n -= 2
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) Append(value float64) {
|
||||||
|
if s.pos == 0 {
|
||||||
|
n := s.buf.PutVarUint64(s.pos, uint64(value*s.coef))
|
||||||
|
s.pos += n
|
||||||
|
s.firstValue = value
|
||||||
|
s.encodeNewDelta(0, 0, 1)
|
||||||
|
} else {
|
||||||
|
delta := uint64((value-s.firstValue)*s.coef + eps)
|
||||||
|
if delta == s.lastDelta {
|
||||||
|
if s.length == 0 {
|
||||||
|
s.length = 2
|
||||||
|
s.shiftOnePosToRight()
|
||||||
|
s.buf.SetByte(s.numIdx-1, 0)
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
|
||||||
|
} else {
|
||||||
|
s.length++
|
||||||
|
if s.length < 130 {
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
|
||||||
|
} else if s.length == 130 {
|
||||||
|
s.shiftOnePosToRight()
|
||||||
|
s.encode2bLength()
|
||||||
|
} else {
|
||||||
|
if s.length < 32769 {
|
||||||
|
s.encode2bLength()
|
||||||
|
} else {
|
||||||
|
s.appendNewDelta(delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.appendNewDelta(delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) appendNewDelta(delta uint64) {
|
||||||
|
s.length = 0
|
||||||
|
|
||||||
|
s8 := s.buf.GetByte(s.pos - 2)
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
|
||||||
|
if s8q == 8 {
|
||||||
|
s.pos -= 1
|
||||||
|
s8 = 0
|
||||||
|
s8q = 1
|
||||||
|
} else {
|
||||||
|
s.pos -= 2
|
||||||
|
s8q++
|
||||||
|
}
|
||||||
|
|
||||||
|
s.encodeNewDelta(delta, s8, s8q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) encodeNewDelta(delta uint64, s8 byte, s8q byte) {
|
||||||
|
s.lastDelta = delta
|
||||||
|
s.numIdx = s.pos
|
||||||
|
n := s.buf.ReversePutVarUint64(s.pos, s.lastDelta)
|
||||||
|
s.pos += n
|
||||||
|
s.buf.SetByte(s.pos, s8)
|
||||||
|
s.pos++
|
||||||
|
s.buf.SetByte(s.pos, s8q)
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) shiftOnePosToRight() {
|
||||||
|
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
|
||||||
|
s.pos++
|
||||||
|
s.numIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) encode2bLength() {
|
||||||
|
num := s.length - 2
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
|
||||||
|
s.buf.SetByte(s.numIdx-2, byte(num>>7))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaCompressor) DeleteLast() {
|
||||||
|
var (
|
||||||
|
s8q = s.buf.GetByte(s.pos - 1)
|
||||||
|
s8 = s.buf.GetByte(s.pos - 2)
|
||||||
|
flag byte = 1 << uint(8-s8q)
|
||||||
|
)
|
||||||
|
|
||||||
|
if s.length > 0 {
|
||||||
|
if s.length == 2 {
|
||||||
|
s.length = 0
|
||||||
|
s.buf.UnsetFlag(s.pos-2, flag)
|
||||||
|
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
||||||
|
s.numIdx--
|
||||||
|
s.pos--
|
||||||
|
} else if s.length < 130 {
|
||||||
|
s.length--
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
||||||
|
} else if s.length == 130 {
|
||||||
|
s.length--
|
||||||
|
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
||||||
|
s.numIdx--
|
||||||
|
s.pos--
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
||||||
|
} else {
|
||||||
|
s.length--
|
||||||
|
s.encode2bLength()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s8q > 1 {
|
||||||
|
s8q--
|
||||||
|
flag = 1 << uint(8-s8q)
|
||||||
|
s.pos = s.numIdx + 2
|
||||||
|
s.buf.SetByte(s.pos-2, s8)
|
||||||
|
s.buf.SetByte(s.pos-1, s8q)
|
||||||
|
} else {
|
||||||
|
s.pos = s.numIdx + 1
|
||||||
|
s.buf.SetByte(s.pos-1, 8)
|
||||||
|
s8 = s.buf.GetByte(s.pos - 2)
|
||||||
|
flag = 1
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
pos = s.pos - 3
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
s.lastDelta, n, err = s.buf.ReverseGetVarUint64(pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.numIdx = pos - n
|
||||||
|
if (s8 & flag) == flag {
|
||||||
|
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReverseCumulativeDeltaDecompressor struct {
|
||||||
|
buf *conbuf.ContinuousBuffer
|
||||||
|
pos int
|
||||||
|
bound int
|
||||||
|
firstValue float64
|
||||||
|
lastValue float64
|
||||||
|
length uint16
|
||||||
|
coef float64
|
||||||
|
idxOf8 uint
|
||||||
|
s8 byte
|
||||||
|
step byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReverseCumulativeDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseCumulativeDeltaDecompressor {
|
||||||
|
var coef float64 = 1
|
||||||
|
if fracDigits > 0 {
|
||||||
|
coef = math.Pow(10, float64(fracDigits))
|
||||||
|
}
|
||||||
|
return &ReverseCumulativeDeltaDecompressor{
|
||||||
|
buf: buf,
|
||||||
|
coef: coef,
|
||||||
|
pos: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaDecompressor) NextValue() (value float64, done bool) {
|
||||||
|
if s.step > 0 {
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
return s.lastValue, false
|
||||||
|
}
|
||||||
|
if s.pos < s.bound {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if s.idxOf8 == 0 {
|
||||||
|
s.s8 = s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
}
|
||||||
|
s.readVar()
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
}
|
||||||
|
return s.lastValue, false
|
||||||
|
}
|
||||||
|
u64, n, err := s.buf.GetVarUint64(0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.firstValue = float64(u64) / s.coef
|
||||||
|
s.bound = n
|
||||||
|
s.pos--
|
||||||
|
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
|
||||||
|
s.pos--
|
||||||
|
s.s8 = s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
s.readVar()
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
}
|
||||||
|
s.step = 1
|
||||||
|
return s.lastValue, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseCumulativeDeltaDecompressor) readVar() {
|
||||||
|
u64, n, err := s.buf.ReverseGetVarUint64(s.pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.pos -= n
|
||||||
|
s.lastValue = s.firstValue + float64(u64)/s.coef
|
||||||
|
|
||||||
|
var flag byte = 1 << s.idxOf8
|
||||||
|
if (s.s8 & flag) == flag {
|
||||||
|
s.length, n = s.buf.DecodeRunLength(s.pos)
|
||||||
|
s.pos -= n
|
||||||
|
}
|
||||||
|
if s.idxOf8 == 7 {
|
||||||
|
s.idxOf8 = 0
|
||||||
|
} else {
|
||||||
|
s.idxOf8++
|
||||||
|
}
|
||||||
|
}
|
||||||
345
chunkenc2/insdelta.go
Normal file
345
chunkenc2/insdelta.go
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
package chunkenc2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/qb/bin"
|
||||||
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// REVERSE
|
||||||
|
|
||||||
|
type ReverseInstantDeltaCompressor struct {
|
||||||
|
buf *conbuf.ContinuousBuffer
|
||||||
|
coef float64
|
||||||
|
pos int
|
||||||
|
firstValue float64
|
||||||
|
lastDelta int64
|
||||||
|
length uint16
|
||||||
|
numIdx int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReverseInstantDeltaCompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaCompressor {
|
||||||
|
var coef float64 = 1
|
||||||
|
if fracDigits > 0 {
|
||||||
|
coef = math.Pow(10, float64(fracDigits))
|
||||||
|
}
|
||||||
|
s := &ReverseInstantDeltaCompressor{
|
||||||
|
buf: buf,
|
||||||
|
pos: size,
|
||||||
|
coef: coef,
|
||||||
|
}
|
||||||
|
if size > 0 {
|
||||||
|
s.restoreState()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) restoreState() {
|
||||||
|
i64, n, err := s.buf.GetVarInt64(0)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("bug: get first value: %s", err))
|
||||||
|
}
|
||||||
|
s.firstValue = float64(i64) / s.coef
|
||||||
|
|
||||||
|
if s.pos > n {
|
||||||
|
pos := s.pos - 1
|
||||||
|
idxOf8 := uint(8 - s.buf.GetByte(pos))
|
||||||
|
pos--
|
||||||
|
s8 := s.buf.GetByte(pos)
|
||||||
|
pos--
|
||||||
|
|
||||||
|
var n int
|
||||||
|
s.lastDelta, n, err = s.buf.ReverseGetVarInt64(pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("bug: get last delta: %s", err))
|
||||||
|
}
|
||||||
|
pos -= n
|
||||||
|
s.numIdx = pos + 1
|
||||||
|
var flag byte = 1 << idxOf8
|
||||||
|
if (s8 & flag) == flag {
|
||||||
|
s.length, _ = s.buf.DecodeRunLength(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) Size() int {
|
||||||
|
return s.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) CalcRequiredSpace(value float64) int {
|
||||||
|
if s.pos == 0 {
|
||||||
|
n := bin.CalcVarInt64Length(int64(value * s.coef))
|
||||||
|
return n + 3
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := (value - s.firstValue) * s.coef
|
||||||
|
if tmp > 0 {
|
||||||
|
tmp += eps
|
||||||
|
} else {
|
||||||
|
tmp -= eps
|
||||||
|
}
|
||||||
|
delta := int64(tmp)
|
||||||
|
|
||||||
|
if delta == s.lastDelta {
|
||||||
|
if s.length == 0 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
newLength := s.length + 1
|
||||||
|
if newLength < 130 {
|
||||||
|
return 0
|
||||||
|
} else if newLength == 130 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
if newLength < 32769 {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
n := bin.CalcVarInt64Length(delta)
|
||||||
|
n += 2
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
if s8q == 8 {
|
||||||
|
n -= 1
|
||||||
|
} else {
|
||||||
|
n -= 2
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n := bin.CalcVarInt64Length(delta)
|
||||||
|
n += 2
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
if s8q == 8 {
|
||||||
|
n -= 1
|
||||||
|
} else {
|
||||||
|
n -= 2
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// В начале буфера кодирую базовое значение.
|
||||||
|
func (s *ReverseInstantDeltaCompressor) Append(value float64) {
|
||||||
|
if s.pos == 0 {
|
||||||
|
n := s.buf.PutVarInt64(s.pos, int64(value*s.coef))
|
||||||
|
s.pos += n
|
||||||
|
s.firstValue = value
|
||||||
|
s.encodeNewDelta(0, 0, 1)
|
||||||
|
} else {
|
||||||
|
tmp := (value - s.firstValue) * s.coef
|
||||||
|
if tmp > 0 {
|
||||||
|
tmp += eps
|
||||||
|
} else {
|
||||||
|
tmp -= eps
|
||||||
|
}
|
||||||
|
delta := int64(tmp)
|
||||||
|
|
||||||
|
if delta == s.lastDelta {
|
||||||
|
if s.length == 0 {
|
||||||
|
s.length = 2
|
||||||
|
s.shiftOnePosToRight()
|
||||||
|
s.buf.SetByte(s.numIdx-1, 0)
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
|
||||||
|
} else {
|
||||||
|
s.length++
|
||||||
|
if s.length < 130 {
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
|
||||||
|
} else if s.length == 130 {
|
||||||
|
s.shiftOnePosToRight()
|
||||||
|
s.encode2bLength()
|
||||||
|
} else {
|
||||||
|
if s.length < 32769 {
|
||||||
|
s.encode2bLength()
|
||||||
|
} else {
|
||||||
|
s.appendNewDelta(delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.appendNewDelta(delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) appendNewDelta(delta int64) {
|
||||||
|
s.length = 0
|
||||||
|
|
||||||
|
s8 := s.buf.GetByte(s.pos - 2)
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
|
||||||
|
if s8q == 8 {
|
||||||
|
s.pos -= 1
|
||||||
|
s8 = 0
|
||||||
|
s8q = 1
|
||||||
|
} else {
|
||||||
|
s.pos -= 2
|
||||||
|
s8q++
|
||||||
|
}
|
||||||
|
s.encodeNewDelta(delta, s8, s8q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) encodeNewDelta(delta int64, s8 byte, s8q byte) {
|
||||||
|
s.lastDelta = delta
|
||||||
|
s.numIdx = s.pos
|
||||||
|
n := s.buf.ReversePutVarInt64(s.pos, s.lastDelta)
|
||||||
|
s.pos += n
|
||||||
|
s.buf.SetByte(s.pos, s8)
|
||||||
|
s.pos++
|
||||||
|
s.buf.SetByte(s.pos, s8q)
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) shiftOnePosToRight() {
|
||||||
|
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
|
||||||
|
s.pos++
|
||||||
|
s.numIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) encode2bLength() {
|
||||||
|
num := s.length - 2
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
|
||||||
|
s.buf.SetByte(s.numIdx-2, byte(num>>7))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaCompressor) DeleteLast() {
|
||||||
|
var (
|
||||||
|
s8q = s.buf.GetByte(s.pos - 1)
|
||||||
|
s8 = s.buf.GetByte(s.pos - 2)
|
||||||
|
flag byte = 1 << uint(8-s8q)
|
||||||
|
)
|
||||||
|
|
||||||
|
if s.length > 0 {
|
||||||
|
if s.length == 2 {
|
||||||
|
s.length = 0
|
||||||
|
s.buf.UnsetFlag(s.pos-2, flag)
|
||||||
|
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
||||||
|
s.numIdx--
|
||||||
|
s.pos--
|
||||||
|
} else if s.length < 130 {
|
||||||
|
s.length--
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
||||||
|
} else if s.length == 130 {
|
||||||
|
s.length--
|
||||||
|
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
||||||
|
s.numIdx--
|
||||||
|
s.pos--
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
||||||
|
} else {
|
||||||
|
s.length--
|
||||||
|
s.encode2bLength()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s8q > 1 {
|
||||||
|
s8q--
|
||||||
|
flag = 1 << uint(8-s8q)
|
||||||
|
s.pos = s.numIdx + 2
|
||||||
|
s.buf.SetByte(s.pos-2, s8)
|
||||||
|
s.buf.SetByte(s.pos-1, s8q)
|
||||||
|
} else {
|
||||||
|
s.pos = s.numIdx + 1
|
||||||
|
s.buf.SetByte(s.pos-1, 8)
|
||||||
|
s8 = s.buf.GetByte(s.pos - 2)
|
||||||
|
flag = 1
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
pos = s.pos - 3
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
s.lastDelta, n, err = s.buf.ReverseGetVarInt64(pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.numIdx = pos - n
|
||||||
|
if (s8 & flag) == flag {
|
||||||
|
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReverseInstantDeltaDecompressor struct {
|
||||||
|
step byte
|
||||||
|
buf *conbuf.ContinuousBuffer
|
||||||
|
pos int
|
||||||
|
bound int
|
||||||
|
firstValue float64
|
||||||
|
lastValue float64
|
||||||
|
length uint16
|
||||||
|
coef float64
|
||||||
|
idxOf8 uint
|
||||||
|
s8 byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor {
|
||||||
|
var coef float64 = 1
|
||||||
|
if fracDigits > 0 {
|
||||||
|
coef = math.Pow(10, float64(fracDigits))
|
||||||
|
}
|
||||||
|
return &ReverseInstantDeltaDecompressor{
|
||||||
|
buf: buf,
|
||||||
|
coef: coef,
|
||||||
|
pos: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) {
|
||||||
|
if s.step > 0 {
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
return s.lastValue, false
|
||||||
|
}
|
||||||
|
if s.pos < s.bound {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.idxOf8 == 0 {
|
||||||
|
s.s8 = s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
}
|
||||||
|
s.readVar()
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
}
|
||||||
|
return s.lastValue, false
|
||||||
|
}
|
||||||
|
i64, n, err := s.buf.GetVarInt64(0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.firstValue = float64(i64) / s.coef
|
||||||
|
s.bound = n
|
||||||
|
s.pos--
|
||||||
|
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
|
||||||
|
s.pos--
|
||||||
|
s.s8 = s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
s.readVar()
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
}
|
||||||
|
s.step = 1
|
||||||
|
return s.lastValue, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseInstantDeltaDecompressor) readVar() {
|
||||||
|
i64, n, err := s.buf.ReverseGetVarInt64(s.pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.pos -= n
|
||||||
|
s.lastValue = s.firstValue + float64(i64)/s.coef
|
||||||
|
|
||||||
|
var flag byte = 1 << s.idxOf8
|
||||||
|
if (s.s8 & flag) == flag {
|
||||||
|
s.length, n = s.buf.DecodeRunLength(s.pos)
|
||||||
|
s.pos -= n
|
||||||
|
}
|
||||||
|
if s.idxOf8 == 7 {
|
||||||
|
s.idxOf8 = 0
|
||||||
|
} else {
|
||||||
|
s.idxOf8++
|
||||||
|
}
|
||||||
|
}
|
||||||
374
chunkenc2/time_delta_delta.go
Normal file
374
chunkenc2/time_delta_delta.go
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
package chunkenc2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/qb/bin"
|
||||||
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// REVERSE
|
||||||
|
|
||||||
|
const (
|
||||||
|
lastUnixtimeIdx = 0
|
||||||
|
baseDeltaIdx = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReverseTimeDeltaOfDeltaCompressor struct {
|
||||||
|
buf *conbuf.ContinuousBuffer
|
||||||
|
pos int
|
||||||
|
baseDelta uint32
|
||||||
|
lastUnixtime uint32
|
||||||
|
lastDeltaOfDelta int64
|
||||||
|
length uint16
|
||||||
|
numIdx int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReverseTimeDeltaOfDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaCompressor {
|
||||||
|
s := &ReverseTimeDeltaOfDeltaCompressor{
|
||||||
|
buf: buf,
|
||||||
|
pos: size,
|
||||||
|
}
|
||||||
|
if size > 0 {
|
||||||
|
s.restoreState()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) restoreState() {
|
||||||
|
s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
|
||||||
|
if s.pos > 4 {
|
||||||
|
u64, _, err := s.buf.GetVarUint64(baseDeltaIdx)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("bug: get base delta: %s", err))
|
||||||
|
}
|
||||||
|
s.baseDelta = uint32(u64)
|
||||||
|
|
||||||
|
pos := s.pos - 1
|
||||||
|
idxOf8 := uint(8 - s.buf.GetByte(pos))
|
||||||
|
pos--
|
||||||
|
s8 := s.buf.GetByte(pos)
|
||||||
|
pos--
|
||||||
|
|
||||||
|
var n int
|
||||||
|
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pos -= n
|
||||||
|
s.numIdx = pos + 1
|
||||||
|
var flag byte = 1 << idxOf8
|
||||||
|
if (s8 & flag) == flag {
|
||||||
|
s.length, _ = s.buf.DecodeRunLength(pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) Size() int {
|
||||||
|
return s.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) CalcRequiredSpace(unixtime uint32) int {
|
||||||
|
if s.pos == 0 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.baseDelta == 0 {
|
||||||
|
baseDelta := unixtime - s.lastUnixtime
|
||||||
|
n := bin.CalcVarUint64Length(uint64(baseDelta))
|
||||||
|
return n + 3
|
||||||
|
}
|
||||||
|
|
||||||
|
deltaOfDelta := int64(unixtime-s.lastUnixtime) - int64(s.baseDelta)
|
||||||
|
if deltaOfDelta == s.lastDeltaOfDelta {
|
||||||
|
if s.length == 0 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
newLength := s.length + 1
|
||||||
|
if newLength < 130 {
|
||||||
|
return 0
|
||||||
|
} else if newLength == 130 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
if newLength < 32769 {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
n := bin.CalcVarInt64Length(deltaOfDelta)
|
||||||
|
n += 2
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
if s8q == 8 {
|
||||||
|
n -= 1
|
||||||
|
} else {
|
||||||
|
n -= 2
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n := bin.CalcVarInt64Length(deltaOfDelta)
|
||||||
|
n += 2
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
if s8q == 8 {
|
||||||
|
n -= 1
|
||||||
|
} else {
|
||||||
|
n -= 2
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) Append(unixtime uint32) {
|
||||||
|
if s.pos == 0 {
|
||||||
|
s.lastUnixtime = unixtime
|
||||||
|
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
|
||||||
|
s.pos += 4
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.baseDelta == 0 {
|
||||||
|
s.baseDelta = unixtime - s.lastUnixtime
|
||||||
|
s.lastDeltaOfDelta = 0
|
||||||
|
s.lastUnixtime = unixtime
|
||||||
|
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
|
||||||
|
|
||||||
|
n := s.buf.PutVarUint64(s.pos, uint64(s.baseDelta))
|
||||||
|
s.pos += n
|
||||||
|
s.encodeNewDeltaOfDelta(0, 0, 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
deltaOfDelta := int64(unixtime-s.lastUnixtime) - int64(s.baseDelta)
|
||||||
|
s.lastUnixtime = unixtime
|
||||||
|
s.buf.PutUint32(lastUnixtimeIdx, unixtime)
|
||||||
|
|
||||||
|
if deltaOfDelta == s.lastDeltaOfDelta {
|
||||||
|
if s.length == 0 {
|
||||||
|
s.length = 2
|
||||||
|
s.shiftOnePosToRight()
|
||||||
|
s.buf.SetByte(s.numIdx-1, 0)
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
s.buf.SetFlag(s.pos-2, 1<<(8-s8q))
|
||||||
|
} else {
|
||||||
|
s.length++
|
||||||
|
if s.length < 130 {
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length-2))
|
||||||
|
} else if s.length == 130 {
|
||||||
|
s.shiftOnePosToRight()
|
||||||
|
s.encode2bLength()
|
||||||
|
} else {
|
||||||
|
if s.length < 32769 {
|
||||||
|
s.encode2bLength()
|
||||||
|
} else {
|
||||||
|
s.appendNewDeltaOfDelta(deltaOfDelta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.appendNewDeltaOfDelta(deltaOfDelta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) appendNewDeltaOfDelta(deltaOfDelta int64) {
|
||||||
|
s.length = 0
|
||||||
|
s8 := s.buf.GetByte(s.pos - 2)
|
||||||
|
s8q := s.buf.GetByte(s.pos - 1)
|
||||||
|
|
||||||
|
if s8q == 8 {
|
||||||
|
s.pos -= 1
|
||||||
|
s8 = 0
|
||||||
|
s8q = 1
|
||||||
|
} else {
|
||||||
|
s.pos -= 2
|
||||||
|
s8q++
|
||||||
|
}
|
||||||
|
|
||||||
|
s.encodeNewDeltaOfDelta(deltaOfDelta, s8, s8q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) encodeNewDeltaOfDelta(deltaOfDelta int64, s8 byte, s8q byte) {
|
||||||
|
s.lastDeltaOfDelta = deltaOfDelta
|
||||||
|
s.numIdx = s.pos
|
||||||
|
n := s.buf.ReversePutVarInt64(s.pos, deltaOfDelta)
|
||||||
|
s.pos += n
|
||||||
|
s.buf.SetByte(s.pos, s8)
|
||||||
|
s.pos++
|
||||||
|
s.buf.SetByte(s.pos, s8q)
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) shiftOnePosToRight() {
|
||||||
|
s.buf.ShiftOnePosToRight(s.numIdx, s.pos)
|
||||||
|
s.pos++
|
||||||
|
s.numIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) encode2bLength() {
|
||||||
|
num := s.length - 2
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(num&127)|128)
|
||||||
|
s.buf.SetByte(s.numIdx-2, byte(num>>7))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaCompressor) DeleteLast() {
|
||||||
|
var (
|
||||||
|
s8q = s.buf.GetByte(s.pos - 1)
|
||||||
|
s8 = s.buf.GetByte(s.pos - 2)
|
||||||
|
flag byte = 1 << uint(8-s8q)
|
||||||
|
)
|
||||||
|
|
||||||
|
if s.length > 0 {
|
||||||
|
if s.length == 2 {
|
||||||
|
s.length = 0
|
||||||
|
s.buf.UnsetFlag(s.pos-2, flag)
|
||||||
|
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
||||||
|
s.numIdx--
|
||||||
|
s.pos--
|
||||||
|
} else if s.length < 130 {
|
||||||
|
s.length--
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
||||||
|
} else if s.length == 130 {
|
||||||
|
s.length--
|
||||||
|
s.buf.ShiftOnePosToLeft(s.numIdx, s.pos)
|
||||||
|
s.numIdx--
|
||||||
|
s.pos--
|
||||||
|
s.buf.SetByte(s.numIdx-1, byte(s.length)-2)
|
||||||
|
} else {
|
||||||
|
s.length--
|
||||||
|
s.encode2bLength()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s8q > 1 {
|
||||||
|
s8q--
|
||||||
|
flag = 1 << uint(8-s8q)
|
||||||
|
s.pos = s.numIdx + 2
|
||||||
|
s.buf.SetByte(s.pos-2, s8)
|
||||||
|
s.buf.SetByte(s.pos-1, s8q)
|
||||||
|
} else {
|
||||||
|
s.pos = s.numIdx + 1
|
||||||
|
s.buf.SetByte(s.pos-1, 8)
|
||||||
|
s8 = s.buf.GetByte(s.pos - 2)
|
||||||
|
flag = 1
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
pos = s.pos - 3
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.numIdx = pos - n
|
||||||
|
if (s8 & flag) == flag {
|
||||||
|
s.length, _ = s.buf.DecodeRunLength(s.numIdx - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
|
||||||
|
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
|
||||||
|
s.buf.PutUint32(lastUnixtimeIdx, s.lastUnixtime)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReverseTimeDeltaOfDeltaDecompressor struct {
|
||||||
|
step byte
|
||||||
|
buf *conbuf.ContinuousBuffer
|
||||||
|
pos int
|
||||||
|
bound int
|
||||||
|
lastUnixtime uint32
|
||||||
|
baseDelta uint32
|
||||||
|
lastDeltaOfDelta int64
|
||||||
|
length uint16
|
||||||
|
idxOf8 uint
|
||||||
|
s8 byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReverseTimeDeltaOfDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaDecompressor {
|
||||||
|
return &ReverseTimeDeltaOfDeltaDecompressor{
|
||||||
|
buf: buf,
|
||||||
|
pos: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaDecompressor) NextValue() (value uint32, done bool) {
|
||||||
|
if s.step == 0 {
|
||||||
|
if s.pos == 0 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx)
|
||||||
|
s.step = 1
|
||||||
|
return s.lastUnixtime, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.step == 1 {
|
||||||
|
if s.pos == baseDeltaIdx {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
u64, n, err := s.buf.GetVarUint64(baseDeltaIdx)
|
||||||
|
if err != nil {
|
||||||
|
panic("EOF")
|
||||||
|
}
|
||||||
|
s.bound = baseDeltaIdx + n
|
||||||
|
s.baseDelta = uint32(u64)
|
||||||
|
|
||||||
|
s.pos--
|
||||||
|
s.idxOf8 = uint(8 - s.buf.GetByte(s.pos))
|
||||||
|
s.pos--
|
||||||
|
s.s8 = s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
|
||||||
|
s.readVar()
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
}
|
||||||
|
s.step = 2
|
||||||
|
return s.lastUnixtime, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
|
||||||
|
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
|
||||||
|
return s.lastUnixtime, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.pos < s.bound {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.idxOf8 == 0 {
|
||||||
|
s.s8 = s.buf.GetByte(s.pos)
|
||||||
|
s.pos--
|
||||||
|
}
|
||||||
|
s.readVar()
|
||||||
|
if s.length > 0 {
|
||||||
|
s.length--
|
||||||
|
}
|
||||||
|
return s.lastUnixtime, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ReverseTimeDeltaOfDeltaDecompressor) readVar() {
|
||||||
|
var (
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
s.lastDeltaOfDelta, n, err = s.buf.ReverseGetVarInt64(s.pos)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
s.pos -= n
|
||||||
|
|
||||||
|
delta := int64(s.baseDelta) + s.lastDeltaOfDelta
|
||||||
|
s.lastUnixtime = uint32(int64(s.lastUnixtime) - delta)
|
||||||
|
|
||||||
|
var flag byte = 1 << s.idxOf8
|
||||||
|
if (s.s8 & flag) == flag {
|
||||||
|
s.length, n = s.buf.DecodeRunLength(s.pos)
|
||||||
|
s.pos -= n
|
||||||
|
}
|
||||||
|
if s.idxOf8 == 7 {
|
||||||
|
s.idxOf8 = 0
|
||||||
|
} else {
|
||||||
|
s.idxOf8++
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const chunkSize = 512
|
const chunkSize = 512
|
||||||
|
const ChunkSize = 512
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrOutOfRange = errors.New("out of range")
|
ErrOutOfRange = errors.New("out of range")
|
||||||
|
|||||||
235
database/api.go
235
database/api.go
@@ -215,7 +215,7 @@ func (s *Database) AddMetric(req proto.AddMetricReq) uint16 {
|
|||||||
|
|
||||||
switch resultCode {
|
switch resultCode {
|
||||||
case Succeed:
|
case Succeed:
|
||||||
waitCh := s.txlog.WriteAddedMetric(txlog.AddedMetric{
|
waitCh := s.txlog.Append(txlog.AddedMetric{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
MetricType: req.MetricType,
|
MetricType: req.MetricType,
|
||||||
FracDigits: req.FracDigits,
|
FracDigits: req.FracDigits,
|
||||||
@@ -289,21 +289,18 @@ func (s *Database) DeleteMetric(req proto.DeleteMetricReq) uint16 {
|
|||||||
switch result.ResultCode {
|
switch result.ResultCode {
|
||||||
case Succeed:
|
case Succeed:
|
||||||
var (
|
var (
|
||||||
freeDataPages []uint32
|
freePageNumbers []uint32
|
||||||
freeIndexPages []uint32
|
|
||||||
)
|
)
|
||||||
if result.RootPageNo > 0 {
|
if result.RootPageNo > 0 {
|
||||||
pageLists, err := s.atree.GetAllPages(result.RootPageNo)
|
var err error
|
||||||
|
freePageNumbers, err = s.atree.GetAllPages(result.RootPageNo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
diploma.Abort(diploma.FailedAtreeRequest, err)
|
diploma.Abort(diploma.FailedAtreeRequest, err)
|
||||||
}
|
}
|
||||||
freeDataPages = pageLists.DataPages
|
|
||||||
freeIndexPages = pageLists.IndexPages
|
|
||||||
}
|
}
|
||||||
waitCh := s.txlog.WriteDeletedMetric(txlog.DeletedMetric{
|
waitCh := s.txlog.Append(txlog.DeletedMetric{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
FreeDataPages: freeDataPages,
|
FreePageNumbers: freePageNumbers,
|
||||||
FreeIndexPages: freeIndexPages,
|
|
||||||
})
|
})
|
||||||
<-waitCh
|
<-waitCh
|
||||||
|
|
||||||
@@ -348,7 +345,7 @@ func (s *Database) AppendMeasure(req proto.AppendMeasureReq) uint16 {
|
|||||||
|
|
||||||
switch result.ResultCode {
|
switch result.ResultCode {
|
||||||
case CanAppend:
|
case CanAppend:
|
||||||
waitCh := s.txlog.WriteAppendMeasure(txlog.AppendedMeasure{
|
waitCh := s.txlog.Append(txlog.AppendedMeasure{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
Timestamp: req.Timestamp,
|
Timestamp: req.Timestamp,
|
||||||
Value: req.Value,
|
Value: req.Value,
|
||||||
@@ -357,35 +354,58 @@ func (s *Database) AppendMeasure(req proto.AppendMeasureReq) uint16 {
|
|||||||
|
|
||||||
case NewPage:
|
case NewPage:
|
||||||
filled := result.FilledPage
|
filled := result.FilledPage
|
||||||
report, err := s.atree.AppendDataPage(atree.AppendDataPageReq{
|
|
||||||
MetricID: req.MetricID,
|
var path atree.PathToDataPage
|
||||||
Timestamp: req.Timestamp,
|
if filled.RootPageNo > 0 {
|
||||||
Value: req.Value,
|
path, err := s.atree.FindPathToLastPage(filled.RootPageNo)
|
||||||
Since: filled.Since,
|
if err != nil {
|
||||||
RootPageNo: filled.RootPageNo,
|
// FIX
|
||||||
PrevPageNo: filled.PrevPageNo,
|
diploma.Abort(diploma.WriteToAtreeFailed, err)
|
||||||
|
}
|
||||||
|
// for _, leg := range path.Legs {
|
||||||
|
// pagesToRelease = append(pagesToRelease, leg.PageNo)
|
||||||
|
// }
|
||||||
|
|
||||||
|
if path.LastPageNo != filled.PrevPageNo {
|
||||||
|
diploma.Abort(
|
||||||
|
diploma.WrongPrevPageNo,
|
||||||
|
fmt.Errorf("bug: last pageNo %d in tree != prev pageNo %d in _metric",
|
||||||
|
path.LastPageNo, filled.PrevPageNo),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// report, err := s.atree.AppendDataPages(atree.AppendDataPageReq{
|
||||||
|
// MetricID: req.MetricID,
|
||||||
|
// Timestamp: req.Timestamp,
|
||||||
|
// Value: req.Value,
|
||||||
|
// Since: filled.Since,
|
||||||
|
// RootPageNo: filled.RootPageNo,
|
||||||
|
// PrevPageNo: filled.PrevPageNo,
|
||||||
|
// TimestampsChunks: filled.TimestampsChunks,
|
||||||
|
// TimestampsSize: filled.TimestampsSize,
|
||||||
|
// ValuesChunks: filled.ValuesChunks,
|
||||||
|
// ValuesSize: filled.ValuesSize,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// diploma.Abort(diploma.WriteToAtreeFailed, err)
|
||||||
|
// }
|
||||||
|
// _ = report
|
||||||
|
|
||||||
|
//waitCh := s.txlog.WriteAppended(path, report)
|
||||||
|
|
||||||
|
waitCh := s.txlog.Append(txlog.AppendedPagesReq{
|
||||||
|
Legs: path.Legs,
|
||||||
|
MetricID: req.MetricID,
|
||||||
|
Timestamp: req.Timestamp,
|
||||||
|
Value: req.Value,
|
||||||
|
LastPageNo: filled.PrevPageNo,
|
||||||
|
//Pages: ,
|
||||||
TimestampsChunks: filled.TimestampsChunks,
|
TimestampsChunks: filled.TimestampsChunks,
|
||||||
TimestampsSize: filled.TimestampsSize,
|
TimestampsSize: filled.TimestampsSize,
|
||||||
ValuesChunks: filled.ValuesChunks,
|
ValuesChunks: filled.ValuesChunks,
|
||||||
ValuesSize: filled.ValuesSize,
|
ValuesSize: filled.ValuesSize,
|
||||||
})
|
},
|
||||||
if err != nil {
|
|
||||||
diploma.Abort(diploma.WriteToAtreeFailed, err)
|
|
||||||
}
|
|
||||||
_ = report
|
|
||||||
waitCh := s.txlog.WriteAppendedMeasureWithOverflow(
|
|
||||||
txlog.AppendedMeasureWithOverflow{
|
|
||||||
// FIX
|
|
||||||
// MetricID: req.MetricID,
|
|
||||||
// Timestamp: req.Timestamp,
|
|
||||||
// Value: req.Value,
|
|
||||||
// IsDataPageReused: report.IsDataPageReused,
|
|
||||||
// DataPageNo: report.DataPageNo,
|
|
||||||
// IsRootChanged: report.IsRootChanged,
|
|
||||||
// RootPageNo: report.NewRootPageNo,
|
|
||||||
// ReusedIndexPages: report.ReusedIndexPages,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
<-waitCh
|
<-waitCh
|
||||||
|
|
||||||
@@ -442,7 +462,9 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
|
|||||||
until = result.Until
|
until = result.Until
|
||||||
untilValue = result.UntilValue
|
untilValue = result.UntilValue
|
||||||
//
|
//
|
||||||
toAppendMeasures []proto.Measure
|
//toAppendMeasures []proto.Measure
|
||||||
|
|
||||||
|
dataPages []atree.NotLinkedDataPage
|
||||||
)
|
)
|
||||||
|
|
||||||
for idx, measure := range req.Measures {
|
for idx, measure := range req.Measures {
|
||||||
@@ -451,30 +473,32 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
|
|||||||
since = measure.Timestamp
|
since = measure.Timestamp
|
||||||
} else {
|
} else {
|
||||||
if measure.Timestamp <= until {
|
if measure.Timestamp <= until {
|
||||||
if len(toAppendMeasures) > 0 {
|
// пишу в БД що вже встиг закодувати FIX
|
||||||
waitCh := s.txlog.WriteAppendMeasures(
|
// if len(toAppendMeasures) > 0 {
|
||||||
txlog.AppendedMeasures{
|
// waitCh := s.txlog.WriteAppendMeasures(
|
||||||
MetricID: req.MetricID,
|
// txlog.AppendedMeasures{
|
||||||
Measures: toAppendMeasures,
|
// MetricID: req.MetricID,
|
||||||
},
|
// Measures: toAppendMeasures,
|
||||||
false,
|
// },
|
||||||
)
|
// false,
|
||||||
<-waitCh
|
// )
|
||||||
}
|
// <-waitCh
|
||||||
|
// }
|
||||||
return proto.ErrExpiredMeasure
|
return proto.ErrExpiredMeasure
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.MetricType == diploma.Cumulative && measure.Value < untilValue {
|
if result.MetricType == diploma.Cumulative && measure.Value < untilValue {
|
||||||
if len(toAppendMeasures) > 0 {
|
// пишу в БД що вже встиг закодувати FIX
|
||||||
waitCh := s.txlog.WriteAppendMeasures(
|
// if len(toAppendMeasures) > 0 {
|
||||||
txlog.AppendedMeasures{
|
// waitCh := s.txlog.WriteAppendMeasures(
|
||||||
MetricID: req.MetricID,
|
// txlog.AppendedMeasures{
|
||||||
Measures: toAppendMeasures,
|
// MetricID: req.MetricID,
|
||||||
},
|
// Measures: toAppendMeasures,
|
||||||
false,
|
// },
|
||||||
)
|
// false,
|
||||||
<-waitCh
|
// )
|
||||||
}
|
// <-waitCh
|
||||||
|
// }
|
||||||
return proto.ErrNonMonotonicValue
|
return proto.ErrNonMonotonicValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -485,38 +509,40 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
|
|||||||
totalSpace := timestamps.Size() + values.Size() + extraSpace
|
totalSpace := timestamps.Size() + values.Size() + extraSpace
|
||||||
|
|
||||||
if totalSpace <= atree.DataPagePayloadSize {
|
if totalSpace <= atree.DataPagePayloadSize {
|
||||||
|
// накопичую
|
||||||
timestamps.Append(measure.Timestamp)
|
timestamps.Append(measure.Timestamp)
|
||||||
values.Append(measure.Value)
|
values.Append(measure.Value)
|
||||||
toAppendMeasures = append(toAppendMeasures, measure)
|
//toAppendMeasures = append(toAppendMeasures, measure)
|
||||||
} else {
|
} else {
|
||||||
if len(toAppendMeasures) > 0 {
|
// сторінка заповнена
|
||||||
waitCh := s.txlog.WriteAppendMeasures(
|
// if len(toAppendMeasures) > 0 {
|
||||||
txlog.AppendedMeasures{
|
// waitCh := s.txlog.WriteAppendMeasures(
|
||||||
MetricID: req.MetricID,
|
// txlog.AppendedMeasures{
|
||||||
Measures: toAppendMeasures,
|
// MetricID: req.MetricID,
|
||||||
},
|
// Measures: toAppendMeasures,
|
||||||
true,
|
// },
|
||||||
)
|
// true,
|
||||||
<-waitCh
|
// )
|
||||||
|
// <-waitCh
|
||||||
|
|
||||||
toAppendMeasures = nil
|
// toAppendMeasures = nil
|
||||||
}
|
// }
|
||||||
report, err := s.atree.AppendDataPage(atree.AppendDataPageReq{
|
|
||||||
MetricID: req.MetricID,
|
pageData := make([]byte, atree.PageSize)
|
||||||
Timestamp: until,
|
|
||||||
Value: untilValue,
|
atree.ChunksToNotLinkedDataPage(pageData, atree.ChunksToNotLinkedDataPageReq{
|
||||||
Since: since,
|
//PrevPageNo: path.LastPageNo,
|
||||||
RootPageNo: rootPageNo,
|
|
||||||
PrevPageNo: prevPageNo,
|
|
||||||
TimestampsChunks: timestampsBuf.Chunks(),
|
TimestampsChunks: timestampsBuf.Chunks(),
|
||||||
TimestampsSize: uint16(timestamps.Size()),
|
TimestampsSize: uint16(timestamps.Size()),
|
||||||
ValuesChunks: valuesBuf.Chunks(),
|
ValuesChunks: valuesBuf.Chunks(),
|
||||||
ValuesSize: uint16(values.Size()),
|
ValuesSize: uint16(values.Size()),
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
diploma.Abort(diploma.WriteToAtreeFailed, err)
|
dataPages = append(dataPages, atree.NotLinkedDataPage{
|
||||||
}
|
Since: since,
|
||||||
_ = report
|
Data: pageData,
|
||||||
|
})
|
||||||
|
|
||||||
// FIX
|
// FIX
|
||||||
// prevPageNo = report.DataPageNo
|
// prevPageNo = report.DataPageNo
|
||||||
// if report.IsRootChanged {
|
// if report.IsRootChanged {
|
||||||
@@ -540,7 +566,7 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
|
|||||||
timestampsBuf = conbuf.New(nil)
|
timestampsBuf = conbuf.New(nil)
|
||||||
valuesBuf = conbuf.New(nil)
|
valuesBuf = conbuf.New(nil)
|
||||||
|
|
||||||
timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor(
|
timestamps = chunkenc.NewReverseTimeDeltaCompressor(
|
||||||
timestampsBuf, 0)
|
timestampsBuf, 0)
|
||||||
if result.MetricType == diploma.Cumulative {
|
if result.MetricType == diploma.Cumulative {
|
||||||
values = chunkenc.NewReverseCumulativeDeltaCompressor(
|
values = chunkenc.NewReverseCumulativeDeltaCompressor(
|
||||||
@@ -560,16 +586,32 @@ func (s *Database) AppendMeasures(req proto.AppendMeasuresReq) uint16 {
|
|||||||
untilValue = measure.Value
|
untilValue = measure.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(toAppendMeasures) > 0 {
|
waitCh := s.txlog.Append(txlog.AppendedPagesReq{
|
||||||
waitCh := s.txlog.WriteAppendMeasures(
|
MetricID: req.MetricID,
|
||||||
txlog.AppendedMeasures{
|
Timestamp: until,
|
||||||
MetricID: req.MetricID,
|
Value: untilValue,
|
||||||
Measures: toAppendMeasures,
|
LastPageNo: prevPageNo,
|
||||||
},
|
//Measures: toAppendMeasures,
|
||||||
false,
|
},
|
||||||
)
|
)
|
||||||
<-waitCh
|
<-waitCh
|
||||||
}
|
|
||||||
|
// report, err := s.atree.AppendDataPage(atree.AppendDataPageReq{})
|
||||||
|
// if err != nil {
|
||||||
|
// diploma.Abort(diploma.WriteToAtreeFailed, err)
|
||||||
|
// }
|
||||||
|
// _ = report
|
||||||
|
|
||||||
|
// if len(toAppendMeasures) > 0 {
|
||||||
|
// waitCh := s.txlog.WriteAppendMeasures(
|
||||||
|
// txlog.AppendedMeasures{
|
||||||
|
// MetricID: req.MetricID,
|
||||||
|
// Measures: toAppendMeasures,
|
||||||
|
// },
|
||||||
|
// false,
|
||||||
|
// )
|
||||||
|
// <-waitCh
|
||||||
|
// }
|
||||||
|
|
||||||
case NoMetric:
|
case NoMetric:
|
||||||
return proto.ErrNoMetric
|
return proto.ErrNoMetric
|
||||||
@@ -602,22 +644,21 @@ func (s *Database) DeleteMeasures(req proto.DeleteMeasuresReq) uint16 {
|
|||||||
|
|
||||||
case DeleteFromAtreeNotNeeded:
|
case DeleteFromAtreeNotNeeded:
|
||||||
// регистрирую удаление в TransactionLog
|
// регистрирую удаление в TransactionLog
|
||||||
waitCh := s.txlog.WriteDeletedMeasures(txlog.DeletedMeasures{
|
waitCh := s.txlog.Append(txlog.DeletedMeasures{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
})
|
})
|
||||||
<-waitCh
|
<-waitCh
|
||||||
|
|
||||||
case DeleteFromAtreeRequired:
|
case DeleteFromAtreeRequired:
|
||||||
// собираю номера всех data и index страниц метрики (типа запись REDO лога).
|
// собираю номера всех data и index страниц метрики (типа запись REDO лога).
|
||||||
pageLists, err := s.atree.GetAllPages(req.MetricID)
|
pageNumbers, err := s.atree.GetAllPages(req.MetricID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
diploma.Abort(diploma.FailedAtreeRequest, err)
|
diploma.Abort(diploma.FailedAtreeRequest, err)
|
||||||
}
|
}
|
||||||
// регистрирую удаление в TransactionLog
|
// регистрирую удаление в TransactionLog
|
||||||
waitCh := s.txlog.WriteDeletedMeasures(txlog.DeletedMeasures{
|
waitCh := s.txlog.Append(txlog.DeletedMeasures{
|
||||||
MetricID: req.MetricID,
|
MetricID: req.MetricID,
|
||||||
FreeDataPages: pageLists.DataPages,
|
FreePageNumbers: pageNumbers,
|
||||||
FreeIndexPages: pageLists.IndexPages,
|
|
||||||
})
|
})
|
||||||
<-waitCh
|
<-waitCh
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ func (s *Database) ListenAndServe() (err error) {
|
|||||||
s.atree, err = atree.New(atree.Options{
|
s.atree, err = atree.New(atree.Options{
|
||||||
Dir: s.dir,
|
Dir: s.dir,
|
||||||
DatabaseName: s.databaseName,
|
DatabaseName: s.databaseName,
|
||||||
FreeList: s.freeList,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("atree.New: %s", err)
|
return fmt.Errorf("atree.New: %s", err)
|
||||||
@@ -169,6 +168,8 @@ func (s *Database) recovery() {
|
|||||||
Dir: s.dir,
|
Dir: s.dir,
|
||||||
LogNumber: logNumber,
|
LogNumber: logNumber,
|
||||||
AppendToWorkerQueue: s.appendJobToWorkerQueue,
|
AppendToWorkerQueue: s.appendJobToWorkerQueue,
|
||||||
|
FreeList: s.freeList,
|
||||||
|
Atree: s.atree,
|
||||||
ExitCh: s.exitCh,
|
ExitCh: s.exitCh,
|
||||||
WaitGroup: s.waitGroup,
|
WaitGroup: s.waitGroup,
|
||||||
})
|
})
|
||||||
@@ -374,11 +375,8 @@ func (s *Database) replayChangesRecord(untyped any) error {
|
|||||||
|
|
||||||
case txlog.DeletedMetric:
|
case txlog.DeletedMetric:
|
||||||
delete(s.metrics, rec.MetricID)
|
delete(s.metrics, rec.MetricID)
|
||||||
if len(rec.FreeDataPages) > 0 {
|
if len(rec.FreePageNumbers) > 0 {
|
||||||
s.freeList.AddPages(rec.FreeDataPages)
|
s.freeList.AddPages(rec.FreePageNumbers)
|
||||||
}
|
|
||||||
if len(rec.FreeIndexPages) > 0 {
|
|
||||||
s.freeList.AddPages(rec.FreeIndexPages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case txlog.AppendedMeasure:
|
case txlog.AppendedMeasure:
|
||||||
@@ -413,34 +411,31 @@ func (s *Database) replayChangesRecord(untyped any) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case txlog.AppendedMeasureWithOverflow:
|
// case txlog.AppendedMeasureWithOverflow:
|
||||||
metric, ok := s.metrics[rec.MetricID]
|
// metric, ok := s.metrics[rec.MetricID]
|
||||||
if ok {
|
// if ok {
|
||||||
metric.ReinitBy(rec.Timestamp, rec.Value)
|
// metric.ReinitBy(rec.Timestamp, rec.Value)
|
||||||
if rec.IsRootChanged {
|
// if rec.IsRootChanged {
|
||||||
metric.RootPageNo = rec.RootPageNo
|
// metric.RootPageNo = rec.RootPageNo
|
||||||
}
|
// }
|
||||||
metric.LastPageNo = rec.DataPageNo
|
// metric.LastPageNo = rec.DataPageNo
|
||||||
// delete free pages
|
// // delete free pages
|
||||||
if rec.IsDataPageReused {
|
// if rec.IsDataPageReused {
|
||||||
s.freeList.DeleteReservedPages([]uint32{
|
// s.freeList.DeleteReservedPages([]uint32{
|
||||||
rec.DataPageNo,
|
// rec.DataPageNo,
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
if len(rec.ReusedIndexPages) > 0 {
|
// if len(rec.ReusedIndexPages) > 0 {
|
||||||
s.freeList.DeleteReservedPages(rec.ReusedIndexPages)
|
// s.freeList.DeleteReservedPages(rec.ReusedIndexPages)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
case txlog.DeletedMeasures:
|
case txlog.DeletedMeasures:
|
||||||
metric, ok := s.metrics[rec.MetricID]
|
metric, ok := s.metrics[rec.MetricID]
|
||||||
if ok {
|
if ok {
|
||||||
metric.DeleteMeasures()
|
metric.DeleteMeasures()
|
||||||
if len(rec.FreeDataPages) > 0 {
|
if len(rec.FreePageNumbers) > 0 {
|
||||||
s.freeList.AddPages(rec.FreeDataPages)
|
s.freeList.AddPages(rec.FreePageNumbers)
|
||||||
}
|
|
||||||
if len(rec.FreeDataPages) > 0 {
|
|
||||||
s.freeList.AddPages(rec.FreeIndexPages)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
octopus "gordenko.dev/dima/qb"
|
octopus "gordenko.dev/dima/qb"
|
||||||
"gordenko.dev/dima/qb/chunkenc"
|
|
||||||
"gordenko.dev/dima/qb/conbuf"
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,43 +23,44 @@ type _metric struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *_metric) ReinitBy(timestamp uint32, value float64) {
|
func (s *_metric) ReinitBy(timestamp uint32, value float64) {
|
||||||
s.TimestampsBuf = conbuf.New(nil)
|
// s.TimestampsBuf = conbuf.New(nil)
|
||||||
s.ValuesBuf = conbuf.New(nil)
|
// s.ValuesBuf = conbuf.New(nil)
|
||||||
//
|
// //
|
||||||
s.Timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor(
|
// s.Timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor(
|
||||||
s.TimestampsBuf, 0)
|
// s.TimestampsBuf, 0)
|
||||||
|
|
||||||
if s.MetricType == octopus.Cumulative {
|
// if s.MetricType == octopus.Cumulative {
|
||||||
s.Values = chunkenc.NewReverseCumulativeDeltaCompressor(
|
// s.Values = chunkenc.NewReverseCumulativeDeltaCompressor(
|
||||||
s.ValuesBuf, 0, s.FracDigits)
|
// s.ValuesBuf, 0, s.FracDigits)
|
||||||
} else {
|
// } else {
|
||||||
s.Values = chunkenc.NewReverseInstantDeltaCompressor(
|
// s.Values = chunkenc.NewReverseInstantDeltaCompressor(
|
||||||
s.ValuesBuf, 0, s.FracDigits)
|
// s.ValuesBuf, 0, s.FracDigits)
|
||||||
}
|
// }
|
||||||
|
|
||||||
s.Timestamps.Append(timestamp)
|
// s.Timestamps.Append(timestamp)
|
||||||
s.Values.Append(value)
|
// s.Values.Append(value)
|
||||||
|
|
||||||
s.Since = timestamp
|
// s.Since = timestamp
|
||||||
s.SinceValue = value
|
// s.SinceValue = value
|
||||||
s.Until = timestamp
|
// s.Until = timestamp
|
||||||
s.UntilValue = value
|
// s.UntilValue = value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *_metric) DeleteMeasures() {
|
func (s *_metric) DeleteMeasures() {
|
||||||
s.TimestampsBuf = conbuf.New(nil)
|
s.TimestampsBuf = conbuf.New(nil)
|
||||||
s.ValuesBuf = conbuf.New(nil)
|
s.ValuesBuf = conbuf.New(nil)
|
||||||
//
|
// fix
|
||||||
s.Timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor(
|
// s.Timestamps = chunkenc.NewReverseTimeDeltaOfDeltaCompressor(
|
||||||
s.TimestampsBuf, 0)
|
// s.TimestampsBuf, 0)
|
||||||
|
|
||||||
if s.MetricType == octopus.Cumulative {
|
// if s.MetricType == octopus.Cumulative {
|
||||||
s.Values = chunkenc.NewReverseCumulativeDeltaCompressor(
|
// s.Values = chunkenc.NewReverseCumulativeDeltaCompressor(
|
||||||
s.ValuesBuf, 0, s.FracDigits)
|
// s.ValuesBuf, 0, s.FracDigits)
|
||||||
} else {
|
// } else {
|
||||||
s.Values = chunkenc.NewReverseInstantDeltaCompressor(
|
// s.Values = chunkenc.NewReverseInstantDeltaCompressor(
|
||||||
s.ValuesBuf, 0, s.FracDigits)
|
// s.ValuesBuf, 0, s.FracDigits)
|
||||||
}
|
// }
|
||||||
|
// END OF FIX
|
||||||
|
|
||||||
s.RootPageNo = 0
|
s.RootPageNo = 0
|
||||||
s.LastPageNo = 0
|
s.LastPageNo = 0
|
||||||
|
|||||||
@@ -352,6 +352,11 @@ func (s *Database) tryAppendMeasure(req tryAppendMeasureReq) {
|
|||||||
s.startAppendMeasure(metric, req, lockEntry)
|
s.startAppendMeasure(metric, req, lockEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReadBound struct {
|
||||||
|
ValuesPos int
|
||||||
|
ValuesLastValue float64
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Database) startAppendMeasure(metric *_metric, req tryAppendMeasureReq, lockEntry *metricLockEntry) {
|
func (s *Database) startAppendMeasure(metric *_metric, req tryAppendMeasureReq, lockEntry *metricLockEntry) {
|
||||||
if req.Timestamp <= metric.Until {
|
if req.Timestamp <= metric.Until {
|
||||||
req.ResultCh <- tryAppendMeasureResult{
|
req.ResultCh <- tryAppendMeasureResult{
|
||||||
@@ -466,8 +471,7 @@ func (s *Database) appendMeasure(rec txlog.AppendedMeasure) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) appendMeasures(extended txlog.AppendedMeasuresExtended) {
|
func (s *Database) appendMeasures(rec txlog.AppendedMeasures) {
|
||||||
rec := extended.Record
|
|
||||||
metric, ok := s.metrics[rec.MetricID]
|
metric, ok := s.metrics[rec.MetricID]
|
||||||
if !ok {
|
if !ok {
|
||||||
diploma.Abort(diploma.NoMetricBug,
|
diploma.Abort(diploma.NoMetricBug,
|
||||||
@@ -500,10 +504,8 @@ func (s *Database) appendMeasures(extended txlog.AppendedMeasuresExtended) {
|
|||||||
metric.UntilValue = measure.Value
|
metric.UntilValue = measure.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
if !extended.HoldLock {
|
lockEntry.XLock = false
|
||||||
lockEntry.XLock = false
|
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
|
||||||
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Database) appendMeasureAfterOverflow(extended txlog.AppendedMeasureWithOverflowExtended) {
|
func (s *Database) appendMeasureAfterOverflow(extended txlog.AppendedMeasureWithOverflowExtended) {
|
||||||
@@ -544,10 +546,8 @@ func (s *Database) appendMeasureAfterOverflow(extended txlog.AppendedMeasureWith
|
|||||||
s.freeList.DeleteReservedPages(rec.ReusedIndexPages)
|
s.freeList.DeleteReservedPages(rec.ReusedIndexPages)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !extended.HoldLock {
|
lockEntry.XLock = false
|
||||||
lockEntry.XLock = false
|
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
|
||||||
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type tryAppendMeasuresReq struct {
|
type tryAppendMeasuresReq struct {
|
||||||
@@ -880,7 +880,7 @@ func (s *Database) applyChanges(req txlog.Changes) {
|
|||||||
case txlog.AppendedMeasure:
|
case txlog.AppendedMeasure:
|
||||||
s.appendMeasure(rec)
|
s.appendMeasure(rec)
|
||||||
|
|
||||||
case txlog.AppendedMeasuresExtended:
|
case txlog.AppendedMeasures:
|
||||||
s.appendMeasures(rec)
|
s.appendMeasures(rec)
|
||||||
|
|
||||||
case txlog.AppendedMeasureWithOverflowExtended:
|
case txlog.AppendedMeasureWithOverflowExtended:
|
||||||
@@ -1019,11 +1019,8 @@ func (s *Database) deleteMetric(rec txlog.DeletedMetric) {
|
|||||||
delete(s.metrics, rec.MetricID)
|
delete(s.metrics, rec.MetricID)
|
||||||
delete(s.metricLockEntries, rec.MetricID)
|
delete(s.metricLockEntries, rec.MetricID)
|
||||||
|
|
||||||
if len(rec.FreeDataPages) > 0 {
|
if len(rec.FreePageNumbers) > 0 {
|
||||||
s.freeList.AddPages(rec.FreeDataPages)
|
s.freeList.AddPages(rec.FreePageNumbers)
|
||||||
}
|
|
||||||
if len(rec.FreeIndexPages) > 0 {
|
|
||||||
s.freeList.AddPages(rec.FreeIndexPages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(addMetricReqs) > 0 {
|
if len(addMetricReqs) > 0 {
|
||||||
@@ -1053,11 +1050,8 @@ func (s *Database) deleteMeasures(rec txlog.DeletedMeasures) {
|
|||||||
}
|
}
|
||||||
metric.DeleteMeasures()
|
metric.DeleteMeasures()
|
||||||
lockEntry.XLock = false
|
lockEntry.XLock = false
|
||||||
if len(rec.FreeDataPages) > 0 {
|
if len(rec.FreePageNumbers) > 0 {
|
||||||
s.freeList.AddPages(rec.FreeDataPages)
|
s.freeList.AddPages(rec.FreePageNumbers)
|
||||||
}
|
|
||||||
if len(rec.FreeDataPages) > 0 {
|
|
||||||
s.freeList.AddPages(rec.FreeIndexPages)
|
|
||||||
}
|
}
|
||||||
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
|
s.doAfterReleaseXLock(rec.MetricID, metric, lockEntry)
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
database_linux
BIN
database_linux
Binary file not shown.
BIN
database_windows
BIN
database_windows
Binary file not shown.
8
go.mod
8
go.mod
@@ -3,11 +3,7 @@ module gordenko.dev/dima/qb
|
|||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/RoaringBitmap/roaring/v2 v2.14.4
|
|
||||||
gopkg.in/ini.v1 v1.67.1
|
gopkg.in/ini.v1 v1.67.1
|
||||||
)
|
gordenko.dev/dima/bin v0.0.0-20251101161620-7fa2d0f2923a
|
||||||
|
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69
|
||||||
require (
|
|
||||||
github.com/bits-and-blooms/bitset v1.24.2 // indirect
|
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
10
go.sum
10
go.sum
@@ -1,12 +1,6 @@
|
|||||||
github.com/RoaringBitmap/roaring/v2 v2.14.4 h1:4aKySrrg9G/5oRtJ3TrZLObVqxgQ9f1znCRBwEwjuVw=
|
|
||||||
github.com/RoaringBitmap/roaring/v2 v2.14.4/go.mod h1:oMvV6omPWr+2ifRdeZvVJyaz+aoEUopyv5iH0u/+wbY=
|
|
||||||
github.com/bits-and-blooms/bitset v1.24.2 h1:M7/NzVbsytmtfHbumG+K2bremQPMJuqv1JD3vOaFxp0=
|
|
||||||
github.com/bits-and-blooms/bitset v1.24.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
|
||||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -24,3 +18,7 @@ gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gordenko.dev/dima/bin v0.0.0-20251101161620-7fa2d0f2923a h1:IAJvA+4IxPAUVXqXAIGrlfljoAbbzek4jPgrLwWJm9E=
|
||||||
|
gordenko.dev/dima/bin v0.0.0-20251101161620-7fa2d0f2923a/go.mod h1:/I+9fvRUzXHgXSwGEwOK1mBBM4BJ3AtCmoCZ2upDiwk=
|
||||||
|
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69 h1:nyJ3mzTQ46yUeMZCdLyYcs7B5JCS54c67v84miyhq2E=
|
||||||
|
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69/go.mod h1:AxgKDktpqBVyIOhIcP+nlCpK+EsJyjN5kPdqyd8euVU=
|
||||||
|
|||||||
BIN
loadtest_linux
BIN
loadtest_linux
Binary file not shown.
BIN
loadtest_windows
BIN
loadtest_windows
Binary file not shown.
BIN
requests_linux
BIN
requests_linux
Binary file not shown.
BIN
requests_windows
BIN
requests_windows
Binary file not shown.
370
txlog/reader.go
370
txlog/reader.go
@@ -9,9 +9,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
diploma "gordenko.dev/dima/qb"
|
|
||||||
"gordenko.dev/dima/qb/bin"
|
"gordenko.dev/dima/qb/bin"
|
||||||
"gordenko.dev/dima/qb/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
@@ -102,49 +100,43 @@ func (s *Reader) parseRecords(body []byte) ([]any, error) {
|
|||||||
|
|
||||||
switch recordType {
|
switch recordType {
|
||||||
case CodeAddedMetric:
|
case CodeAddedMetric:
|
||||||
var rec AddedMetric
|
rec := new(AddedMetric)
|
||||||
rec, err = s.readAddedMetric(src)
|
if err = rec.Parse(src); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
records = append(records, rec)
|
records = append(records, rec)
|
||||||
|
|
||||||
case CodeDeletedMetric:
|
case CodeDeletedMetric:
|
||||||
var rec DeletedMetric
|
rec := new(DeletedMetric)
|
||||||
rec, err = s.readDeletedMetric(src)
|
if err = rec.Parse(src); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
records = append(records, rec)
|
records = append(records, rec)
|
||||||
|
|
||||||
case CodeAppendedMeasure:
|
case CodeAppendedMeasure:
|
||||||
var rec AppendedMeasure
|
rec := new(AppendedMeasure)
|
||||||
rec, err = s.readAppendedMeasure(src)
|
if err = rec.Parse(src); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
records = append(records, rec)
|
records = append(records, rec)
|
||||||
|
|
||||||
case CodeAppendedMeasures:
|
case CodeAppendedMeasures:
|
||||||
var rec AppendedMeasures
|
rec := new(AppendedMeasures)
|
||||||
rec, err = s.readAppendedMeasures(src)
|
if err = rec.Parse(src); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
records = append(records, rec)
|
records = append(records, rec)
|
||||||
|
|
||||||
case CodeAppendedMeasureWithOverflow:
|
case CodeAppendedPages:
|
||||||
var rec AppendedMeasureWithOverflow
|
rec := new(AppendedPages)
|
||||||
rec, err = s.readAppendedMeasureWithOverflow(src)
|
if err = rec.Parse(src); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
records = append(records, rec)
|
records = append(records, rec)
|
||||||
|
|
||||||
case CodeDeletedMeasures:
|
case CodeDeletedMeasures:
|
||||||
var rec DeletedMeasures
|
rec := new(DeletedMeasures)
|
||||||
rec, err = s.readDeletedMeasures(src)
|
if err = rec.Parse(src); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
records = append(records, rec)
|
records = append(records, rec)
|
||||||
@@ -155,183 +147,183 @@ func (s *Reader) parseRecords(body []byte) ([]any, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Reader) readAddedMetric(src *bytes.Buffer) (_ AddedMetric, err error) {
|
// func (s *Reader) readAddedMetric(src *bytes.Buffer) (_ AddedMetric, err error) {
|
||||||
arr, err := bin.ReadN(src, 6)
|
// arr, err := bin.ReadN(src, 6)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
return AddedMetric{
|
// return AddedMetric{
|
||||||
MetricID: bin.GetUint32(arr),
|
// MetricID: bin.GetUint32(arr),
|
||||||
MetricType: diploma.MetricType(arr[4]),
|
// MetricType: diploma.MetricType(arr[4]),
|
||||||
FracDigits: int(arr[5]),
|
// FracDigits: int(arr[5]),
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Reader) readDeletedMetric(src *bytes.Buffer) (_ DeletedMetric, err error) {
|
// func (s *Reader) readDeletedMetric(src *bytes.Buffer) (_ DeletedMetric, err error) {
|
||||||
var rec DeletedMetric
|
// var rec DeletedMetric
|
||||||
rec.MetricID, err = bin.ReadUint32(src)
|
// rec.MetricID, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// free data pages
|
// // free data pages
|
||||||
dataQty, _, err := bin.ReadVarUint64(src)
|
// dataQty, _, err := bin.ReadVarUint64(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
for range dataQty {
|
// for range dataQty {
|
||||||
var pageNo uint32
|
// var pageNo uint32
|
||||||
pageNo, err = bin.ReadUint32(src)
|
// pageNo, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
rec.FreeDataPages = append(rec.FreeDataPages, pageNo)
|
// rec.FreeDataPages = append(rec.FreeDataPages, pageNo)
|
||||||
}
|
// }
|
||||||
// free index pages
|
// // free index pages
|
||||||
indexQty, _, err := bin.ReadVarUint64(src)
|
// indexQty, _, err := bin.ReadVarUint64(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
for range indexQty {
|
// for range indexQty {
|
||||||
var pageNo uint32
|
// var pageNo uint32
|
||||||
pageNo, err = bin.ReadUint32(src)
|
// pageNo, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
rec.FreeIndexPages = append(rec.FreeIndexPages, pageNo)
|
// rec.FreeIndexPages = append(rec.FreeIndexPages, pageNo)
|
||||||
}
|
// }
|
||||||
return rec, nil
|
// return rec, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Reader) readAppendedMeasure(src *bytes.Buffer) (_ AppendedMeasure, err error) {
|
// func (s *Reader) readAppendedMeasure(src *bytes.Buffer) (_ AppendedMeasure, err error) {
|
||||||
arr, err := bin.ReadN(src, 16)
|
// arr, err := bin.ReadN(src, 16)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
return AppendedMeasure{
|
// return AppendedMeasure{
|
||||||
MetricID: bin.GetUint32(arr[0:]),
|
// MetricID: bin.GetUint32(arr[0:]),
|
||||||
Timestamp: bin.GetUint32(arr[4:]),
|
// Timestamp: bin.GetUint32(arr[4:]),
|
||||||
Value: bin.GetFloat64(arr[8:]),
|
// Value: bin.GetFloat64(arr[8:]),
|
||||||
}, nil
|
// }, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Reader) readAppendedMeasures(src *bytes.Buffer) (_ AppendedMeasures, err error) {
|
// func (s *Reader) readAppendedMeasures(src *bytes.Buffer) (_ AppendedMeasures, err error) {
|
||||||
var rec AppendedMeasures
|
// var rec AppendedMeasures
|
||||||
rec.MetricID, err = bin.ReadUint32(src)
|
// rec.MetricID, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
qty, err := bin.ReadUint16(src)
|
// qty, err := bin.ReadUint16(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
for range qty {
|
// for range qty {
|
||||||
var measure proto.Measure
|
// var measure proto.Measure
|
||||||
measure.Timestamp, err = bin.ReadUint32(src)
|
// measure.Timestamp, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
measure.Value, err = bin.ReadFloat64(src)
|
// measure.Value, err = bin.ReadFloat64(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
rec.Measures = append(rec.Measures, measure)
|
// rec.Measures = append(rec.Measures, measure)
|
||||||
}
|
// }
|
||||||
return rec, nil
|
// return rec, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Reader) readAppendedMeasureWithOverflow(src *bytes.Buffer) (_ AppendedMeasureWithOverflow, err error) {
|
// func (s *Reader) readAppendedMeasureWithOverflow(src *bytes.Buffer) (_ AppendedMeasureWithOverflow, err error) {
|
||||||
var (
|
// var (
|
||||||
b byte
|
// b byte
|
||||||
rec AppendedMeasureWithOverflow
|
// rec AppendedMeasureWithOverflow
|
||||||
)
|
// )
|
||||||
rec.MetricID, err = bin.ReadUint32(src)
|
// rec.MetricID, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
rec.Timestamp, err = bin.ReadUint32(src)
|
// rec.Timestamp, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
rec.Value, err = bin.ReadFloat64(src)
|
// rec.Value, err = bin.ReadFloat64(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
b, err = src.ReadByte()
|
// b, err = src.ReadByte()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
rec.IsDataPageReused = b == 1
|
// rec.IsDataPageReused = b == 1
|
||||||
|
|
||||||
rec.DataPageNo, err = bin.ReadUint32(src)
|
// rec.DataPageNo, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
b, err = src.ReadByte()
|
// b, err = src.ReadByte()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if b == 1 {
|
// if b == 1 {
|
||||||
rec.IsRootChanged = true
|
// rec.IsRootChanged = true
|
||||||
rec.RootPageNo, err = bin.ReadUint32(src)
|
// rec.RootPageNo, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// index pages
|
// // index pages
|
||||||
indexQty, err := src.ReadByte()
|
// indexQty, err := src.ReadByte()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
for range indexQty {
|
// for range indexQty {
|
||||||
var pageNo uint32
|
// var pageNo uint32
|
||||||
pageNo, err = bin.ReadUint32(src)
|
// pageNo, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
rec.ReusedIndexPages = append(rec.ReusedIndexPages, pageNo)
|
// rec.ReusedIndexPages = append(rec.ReusedIndexPages, pageNo)
|
||||||
}
|
// }
|
||||||
return rec, nil
|
// return rec, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Reader) readDeletedMeasures(src *bytes.Buffer) (_ DeletedMeasures, err error) {
|
// func (s *Reader) readDeletedMeasures(src *bytes.Buffer) (_ DeletedMeasures, err error) {
|
||||||
var (
|
// var (
|
||||||
rec DeletedMeasures
|
// rec DeletedMeasures
|
||||||
)
|
// )
|
||||||
rec.MetricID, err = bin.ReadUint32(src)
|
// rec.MetricID, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// free data pages
|
// // free data pages
|
||||||
rec.FreeDataPages, err = s.readFreePageNumbers(src)
|
// rec.FreeDataPages, err = s.readFreePageNumbers(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
// free index pages
|
// // free index pages
|
||||||
rec.FreeIndexPages, err = s.readFreePageNumbers(src)
|
// rec.FreeIndexPages, err = s.readFreePageNumbers(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
return rec, nil
|
// return rec, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
// HELPERS
|
// HELPERS
|
||||||
|
|
||||||
func (s *Reader) readFreePageNumbers(src *bytes.Buffer) ([]uint32, error) {
|
// func (s *Reader) readFreePageNumbers(src *bytes.Buffer) ([]uint32, error) {
|
||||||
var freePages []uint32
|
// var freePages []uint32
|
||||||
qty, _, err := bin.ReadVarUint64(src)
|
// qty, _, err := bin.ReadVarUint64(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
for range qty {
|
// for range qty {
|
||||||
var pageNo uint32
|
// var pageNo uint32
|
||||||
pageNo, err = bin.ReadUint32(src)
|
// pageNo, err = bin.ReadUint32(src)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
freePages = append(freePages, pageNo)
|
// freePages = append(freePages, pageNo)
|
||||||
}
|
// }
|
||||||
return freePages, nil
|
// return freePages, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (s *Reader) Seek(offset int64) error {
|
func (s *Reader) Seek(offset int64) error {
|
||||||
ret, err := s.file.Seek(offset, 0)
|
ret, err := s.file.Seek(offset, 0)
|
||||||
|
|||||||
359
txlog/txlog.go
Normal file
359
txlog/txlog.go
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
package txlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
bin "gordenko.dev/dima/bin/little"
|
||||||
|
"gordenko.dev/dima/qb"
|
||||||
|
"gordenko.dev/dima/qb/atree"
|
||||||
|
"gordenko.dev/dima/qb/conbuf"
|
||||||
|
"gordenko.dev/dima/qb/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddedMetric struct {
|
||||||
|
MetricID uint32
|
||||||
|
MetricType qb.MetricType
|
||||||
|
FracDigits int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s AddedMetric) Pack(w io.Writer) {
|
||||||
|
arr := []byte{
|
||||||
|
CodeAddedMetric,
|
||||||
|
0, 0, 0, 0, //
|
||||||
|
byte(s.MetricType),
|
||||||
|
byte(s.FracDigits),
|
||||||
|
}
|
||||||
|
bin.PutUint32(arr[1:], s.MetricID)
|
||||||
|
w.Write(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AddedMetric) Parse(src io.Reader) (err error) {
|
||||||
|
arr, err := bin.ReadN(src, 6)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.MetricID, _ = bin.GetUint32(arr)
|
||||||
|
s.MetricType = qb.MetricType(arr[4])
|
||||||
|
s.FracDigits = int(arr[5])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeletedMetric struct {
|
||||||
|
MetricID uint32
|
||||||
|
FreePageNumbers []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DeletedMetric) Pack(w io.Writer) {
|
||||||
|
arr := []byte{
|
||||||
|
CodeDeletedMetric,
|
||||||
|
0, 0, 0, 0, // metricID
|
||||||
|
}
|
||||||
|
bin.PutUint32(arr[1:], s.MetricID)
|
||||||
|
w.Write(arr)
|
||||||
|
bin.WriteVarSize(w, len(s.FreePageNumbers))
|
||||||
|
for _, pageNo := range s.FreePageNumbers {
|
||||||
|
bin.WriteUint32(w, pageNo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DeletedMetric) Parse(src io.Reader) (err error) {
|
||||||
|
s.MetricID, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// free pages
|
||||||
|
qty, err := bin.ReadVarSize(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for range qty {
|
||||||
|
var pageNo uint32
|
||||||
|
pageNo, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.FreePageNumbers = append(s.FreePageNumbers, pageNo)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppendedMeasure struct {
|
||||||
|
MetricID uint32
|
||||||
|
Timestamp uint32
|
||||||
|
Value float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s AppendedMeasure) Pack(w io.Writer) {
|
||||||
|
arr := []byte{
|
||||||
|
CodeAppendedMeasure,
|
||||||
|
0, 0, 0, 0, // metricID
|
||||||
|
0, 0, 0, 0, // timestamp
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, // value
|
||||||
|
}
|
||||||
|
bin.PutUint32(arr[1:], s.MetricID)
|
||||||
|
bin.PutUint32(arr[5:], s.Timestamp)
|
||||||
|
bin.PutFloat64(arr[9:], s.Value)
|
||||||
|
w.Write(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AppendedMeasure) Parse(src io.Reader) (err error) {
|
||||||
|
arr, err := bin.ReadN(src, 16)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.MetricID, _ = bin.GetUint32(arr[0:])
|
||||||
|
s.Timestamp, _ = bin.GetUint32(arr[4:])
|
||||||
|
s.Value, _ = bin.GetFloat64(arr[8:])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppendedMeasures struct {
|
||||||
|
MetricID uint32
|
||||||
|
Measures []proto.Measure
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s AppendedMeasures) Pack(w io.Writer) {
|
||||||
|
arr := []byte{
|
||||||
|
CodeAppendedMeasures,
|
||||||
|
0, 0, 0, 0, // metricID
|
||||||
|
0, 0, // qty
|
||||||
|
}
|
||||||
|
bin.PutUint32(arr[1:], s.MetricID)
|
||||||
|
bin.PutUint16(arr[5:], uint16(len(s.Measures)))
|
||||||
|
w.Write(arr)
|
||||||
|
for _, measure := range s.Measures {
|
||||||
|
bin.WriteUint32(w, measure.Timestamp)
|
||||||
|
bin.WriteFloat64(w, measure.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AppendedMeasures) Parse(src io.Reader) (err error) {
|
||||||
|
s.MetricID, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
qty, err := bin.ReadUint16(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for range qty {
|
||||||
|
var measure proto.Measure
|
||||||
|
measure.Timestamp, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
measure.Value, err = bin.ReadFloat64(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Measures = append(s.Measures, measure)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppendedPagesReq struct {
|
||||||
|
Legs []atree.PathLeg
|
||||||
|
MetricID uint32
|
||||||
|
Timestamp uint32 // last measure
|
||||||
|
Value float64 // last measure
|
||||||
|
//RootPageNo uint32
|
||||||
|
LastPageNo uint32
|
||||||
|
Pages []atree.NotLinkedDataPage
|
||||||
|
TimestampsChunks [][]byte
|
||||||
|
TimestampsSize uint16
|
||||||
|
ValuesChunks [][]byte
|
||||||
|
ValuesSize uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
4b metricID
|
||||||
|
4b timestamp
|
||||||
|
8b value
|
||||||
|
4b LastPageNo (0 if no new root page)
|
||||||
|
4b newRootPageNo (0 if no new root page)
|
||||||
|
Nb - varsize (pages qty)
|
||||||
|
[
|
||||||
|
|
||||||
|
4b pageNo
|
||||||
|
1b isReused
|
||||||
|
Xb page (page size)
|
||||||
|
|
||||||
|
]
|
||||||
|
2b - timestamps size (not filled page)
|
||||||
|
Nb - timestamps payload
|
||||||
|
2b - values size (not filled page)
|
||||||
|
Nb - values payload
|
||||||
|
*/
|
||||||
|
type AppendedPages struct {
|
||||||
|
MetricID uint32
|
||||||
|
Timestamp uint32 // last measure
|
||||||
|
Value float64 // last measure
|
||||||
|
NewRootPageNo uint32
|
||||||
|
LastPageNo uint32
|
||||||
|
Pages []atree.PageToWrite
|
||||||
|
TimestampsChunks [][]byte
|
||||||
|
TimestampsSize int
|
||||||
|
ValuesChunks [][]byte
|
||||||
|
ValuesSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AppendedPages) Pack(w io.Writer) {
|
||||||
|
w.Write([]byte{
|
||||||
|
CodeAppendedPages,
|
||||||
|
})
|
||||||
|
bin.WriteUint32(w, s.MetricID)
|
||||||
|
bin.WriteUint32(w, s.Timestamp)
|
||||||
|
bin.WriteFloat64(w, s.Value)
|
||||||
|
bin.WriteUint32(w, s.LastPageNo)
|
||||||
|
bin.WriteUint32(w, s.NewRootPageNo)
|
||||||
|
//
|
||||||
|
bin.WriteVarSize(w, len(s.Pages))
|
||||||
|
for _, p := range s.Pages {
|
||||||
|
bin.WriteUint32(w, p.PageNo)
|
||||||
|
if p.IsReused {
|
||||||
|
w.Write([]byte{1})
|
||||||
|
} else {
|
||||||
|
w.Write([]byte{0})
|
||||||
|
}
|
||||||
|
w.Write(p.Data)
|
||||||
|
}
|
||||||
|
// timestamps
|
||||||
|
writeChunkedPayloadTo(s.TimestampsChunks, int(s.TimestampsSize), w)
|
||||||
|
// values
|
||||||
|
writeChunkedPayloadTo(s.ValuesChunks, int(s.ValuesSize), w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AppendedPages) Parse(src io.Reader) (err error) {
|
||||||
|
s.MetricID, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Timestamp, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Value, err = bin.ReadFloat64(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.LastPageNo, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.NewRootPageNo, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pagesQty, err := bin.ReadVarSize(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for range pagesQty {
|
||||||
|
var page atree.PageToWrite
|
||||||
|
page.PageNo, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
page.IsReused, err = bin.ReadBool(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
page.Data, err = bin.ReadN(src, atree.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Pages = append(s.Pages, page)
|
||||||
|
}
|
||||||
|
s.TimestampsChunks, s.TimestampsSize, err = readChunkedPayload(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.ValuesChunks, s.ValuesSize, err = readChunkedPayload(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeChunkedPayloadTo(chunks [][]byte, size int, w io.Writer) {
|
||||||
|
bin.WriteVarSize(w, size)
|
||||||
|
for _, chunk := range chunks {
|
||||||
|
if size >= len(chunk) {
|
||||||
|
w.Write(chunk)
|
||||||
|
size -= len(chunk)
|
||||||
|
} else {
|
||||||
|
w.Write(chunk[:size])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readChunkedPayload(src io.Reader) (_ [][]byte, _ int, err error) {
|
||||||
|
var (
|
||||||
|
chunks [][]byte
|
||||||
|
size int
|
||||||
|
remainingSize = size
|
||||||
|
)
|
||||||
|
size, err = bin.ReadVarSize(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for remainingSize > 0 {
|
||||||
|
var chunk []byte
|
||||||
|
if remainingSize >= conbuf.ChunkSize {
|
||||||
|
chunk, err = bin.ReadN(src, conbuf.ChunkSize)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chunk = make([]byte, conbuf.ChunkSize)
|
||||||
|
err = bin.ReadNInto(src, chunk[:remainingSize])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunks = append(chunks, chunk)
|
||||||
|
remainingSize -= conbuf.ChunkSize
|
||||||
|
}
|
||||||
|
return chunks, size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix - add changed pages
|
||||||
|
type DeletedMeasures struct {
|
||||||
|
MetricID uint32
|
||||||
|
FreePageNumbers []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s DeletedMeasures) Pack(w io.Writer) {
|
||||||
|
arr := []byte{
|
||||||
|
CodeDeletedMeasures,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
}
|
||||||
|
bin.PutUint32(arr[1:], s.MetricID)
|
||||||
|
w.Write(arr)
|
||||||
|
bin.WriteVarSize(w, len(s.FreePageNumbers))
|
||||||
|
for _, pageNo := range s.FreePageNumbers {
|
||||||
|
bin.WriteUint32(w, pageNo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DeletedMeasures) Parse(src io.Reader) (err error) {
|
||||||
|
s.MetricID, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// free pages
|
||||||
|
qty, err := bin.ReadVarSize(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for range qty {
|
||||||
|
var pageNo uint32
|
||||||
|
pageNo, err = bin.ReadUint32(src)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.FreePageNumbers = append(s.FreePageNumbers, pageNo)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
445
txlog/writer.go
445
txlog/writer.go
@@ -9,9 +9,9 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
bin "gordenko.dev/dima/bin/little"
|
||||||
octopus "gordenko.dev/dima/qb"
|
octopus "gordenko.dev/dima/qb"
|
||||||
"gordenko.dev/dima/qb/bin"
|
"gordenko.dev/dima/qb/atree"
|
||||||
"gordenko.dev/dima/qb/proto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -28,14 +28,19 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CodeAddedMetric byte = 1
|
CodeAddedMetric byte = 1
|
||||||
CodeDeletedMetric byte = 2
|
CodeDeletedMetric byte = 2
|
||||||
CodeAppendedMeasure byte = 4
|
CodeAppendedMeasure byte = 4
|
||||||
CodeAppendedMeasures byte = 5
|
CodeAppendedMeasures byte = 5
|
||||||
CodeAppendedMeasureWithOverflow byte = 6
|
CodeAppendedPages byte = 6
|
||||||
CodeDeletedMeasures byte = 7
|
CodeDeletedMeasures byte = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FreeList interface {
|
||||||
|
// використовується в allocPage
|
||||||
|
ReservePage() uint32
|
||||||
|
}
|
||||||
|
|
||||||
func JoinChangesFileName(dir string, logNumber int) string {
|
func JoinChangesFileName(dir string, logNumber int) string {
|
||||||
return filepath.Join(dir, fmt.Sprintf("%d.changes", logNumber))
|
return filepath.Join(dir, fmt.Sprintf("%d.changes", logNumber))
|
||||||
}
|
}
|
||||||
@@ -49,11 +54,16 @@ type Changes struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
logNumber int
|
freelist FreeList
|
||||||
dir string
|
atree *atree.Atree
|
||||||
file *os.File
|
logNumber int
|
||||||
buf *bytes.Buffer
|
dir string
|
||||||
|
dataFile *os.File
|
||||||
|
file *os.File
|
||||||
|
input []any
|
||||||
|
//buf *bytes.Buffer
|
||||||
|
pagesToWrite []atree.PageToWrite
|
||||||
workerReqs []any
|
workerReqs []any
|
||||||
waitCh chan struct{}
|
waitCh chan struct{}
|
||||||
appendToWorkerQueue func(any)
|
appendToWorkerQueue func(any)
|
||||||
@@ -69,6 +79,8 @@ type WriterOptions struct {
|
|||||||
Dir string
|
Dir string
|
||||||
LogNumber int // номер журнала
|
LogNumber int // номер журнала
|
||||||
AppendToWorkerQueue func(any)
|
AppendToWorkerQueue func(any)
|
||||||
|
FreeList FreeList
|
||||||
|
Atree *atree.Atree
|
||||||
ExitCh chan struct{}
|
ExitCh chan struct{}
|
||||||
WaitGroup *sync.WaitGroup
|
WaitGroup *sync.WaitGroup
|
||||||
}
|
}
|
||||||
@@ -80,6 +92,12 @@ func NewWriter(opt WriterOptions) (*Writer, error) {
|
|||||||
if opt.AppendToWorkerQueue == nil {
|
if opt.AppendToWorkerQueue == nil {
|
||||||
return nil, errors.New("AppendToWorkerQueue option is required")
|
return nil, errors.New("AppendToWorkerQueue option is required")
|
||||||
}
|
}
|
||||||
|
if opt.FreeList == nil {
|
||||||
|
return nil, errors.New("FreeList option is required")
|
||||||
|
}
|
||||||
|
if opt.Atree == nil {
|
||||||
|
return nil, errors.New("Atree option is required")
|
||||||
|
}
|
||||||
if opt.ExitCh == nil {
|
if opt.ExitCh == nil {
|
||||||
return nil, errors.New("ExitCh option is required")
|
return nil, errors.New("ExitCh option is required")
|
||||||
}
|
}
|
||||||
@@ -89,12 +107,14 @@ func NewWriter(opt WriterOptions) (*Writer, error) {
|
|||||||
|
|
||||||
s := &Writer{
|
s := &Writer{
|
||||||
dir: opt.Dir,
|
dir: opt.Dir,
|
||||||
buf: bytes.NewBuffer(nil),
|
|
||||||
appendToWorkerQueue: opt.AppendToWorkerQueue,
|
appendToWorkerQueue: opt.AppendToWorkerQueue,
|
||||||
|
freelist: opt.FreeList,
|
||||||
|
atree: opt.Atree,
|
||||||
logNumber: opt.LogNumber,
|
logNumber: opt.LogNumber,
|
||||||
exitCh: opt.ExitCh,
|
exitCh: opt.ExitCh,
|
||||||
waitGroup: opt.WaitGroup,
|
waitGroup: opt.WaitGroup,
|
||||||
signalCh: make(chan struct{}, 1),
|
signalCh: make(chan struct{}, 1),
|
||||||
|
waitCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -119,8 +139,6 @@ func NewWriter(opt WriterOptions) (*Writer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.reset()
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +146,7 @@ func (s *Writer) Run() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-s.signalCh:
|
case <-s.signalCh:
|
||||||
if err := s.flush(); err != nil {
|
if err := s.packAndWrite(); err != nil {
|
||||||
octopus.Abort(octopus.FailedWriteToTxLog, err)
|
octopus.Abort(octopus.FailedWriteToTxLog, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,21 +157,67 @@ func (s *Writer) Run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Writer) reset() {
|
func (s *Writer) packAndWrite() error {
|
||||||
s.buf.Reset()
|
s.mutex.Lock()
|
||||||
s.buf.Write([]byte{
|
input := s.input
|
||||||
0, 0, 0, 0, // packet length
|
waitCh := s.waitCh
|
||||||
0, 0, 0, 0, // crc32
|
s.input = nil
|
||||||
0, 0, 0, 0, // lsn
|
|
||||||
|
|
||||||
})
|
|
||||||
s.workerReqs = nil
|
|
||||||
s.waitCh = make(chan struct{})
|
s.waitCh = make(chan struct{})
|
||||||
|
s.mutex.Unlock()
|
||||||
|
|
||||||
|
w := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
for _, untyped := range input {
|
||||||
|
switch x := untyped.(type) {
|
||||||
|
case AppendedPagesReq:
|
||||||
|
s.packWriteAppended(w, x)
|
||||||
|
case AddedMetric:
|
||||||
|
case DeletedMetric:
|
||||||
|
if len(x.FreePageNumbers) > 0 {
|
||||||
|
//s.freeList.AddPages(x.FreePageNumbers)
|
||||||
|
}
|
||||||
|
case AppendedMeasure:
|
||||||
|
case AppendedMeasures:
|
||||||
|
case DeletedMeasures:
|
||||||
|
//case DeletedMeasuresSince:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// flush
|
||||||
|
close(waitCh)
|
||||||
|
return s.flush(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Writer) flush() error {
|
func (s *Writer) Append(req any) chan struct{} {
|
||||||
|
s.mutex.Lock()
|
||||||
|
s.input = append(s.input, req)
|
||||||
|
s.workerReqs = append(s.workerReqs, req)
|
||||||
|
waitCh := s.waitCh
|
||||||
|
s.mutex.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case s.signalCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return waitCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (s *Writer) reset() {
|
||||||
|
// s.buf.Reset()
|
||||||
|
// s.buf.Write([]byte{
|
||||||
|
// 0, 0, 0, 0, // packet length
|
||||||
|
// 0, 0, 0, 0, // crc32
|
||||||
|
// 0, 0, 0, 0, // lsn
|
||||||
|
|
||||||
|
// })
|
||||||
|
// s.pagesToWrite = nil
|
||||||
|
// s.workerReqs = nil
|
||||||
|
// s.waitCh = make(chan struct{})
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (s *Writer) flush(w *bytes.Buffer) error {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
|
|
||||||
|
pagesToWrite := s.pagesToWrite
|
||||||
workerReqs := s.workerReqs
|
workerReqs := s.workerReqs
|
||||||
waitCh := s.waitCh
|
waitCh := s.waitCh
|
||||||
isExited := s.isExited
|
isExited := s.isExited
|
||||||
@@ -163,12 +227,12 @@ func (s *Writer) flush() error {
|
|||||||
exitWaitGroup = s.waitGroup
|
exitWaitGroup = s.waitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.buf.Len() > packetPrefixSize {
|
if w.Len() > packetPrefixSize {
|
||||||
s.lsn++
|
s.lsn++
|
||||||
lsn := s.lsn
|
lsn := s.lsn
|
||||||
packet := make([]byte, s.buf.Len())
|
packet := make([]byte, w.Len())
|
||||||
copy(packet, s.buf.Bytes())
|
copy(packet, w.Bytes())
|
||||||
s.reset()
|
//s.reset() fix
|
||||||
|
|
||||||
s.written += int64(len(packet)) + 12
|
s.written += int64(len(packet)) + 12
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
@@ -181,14 +245,17 @@ func (s *Writer) flush() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("TxLog write: %s", err)
|
return fmt.Errorf("TxLog write: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n != len(packet) {
|
if n != len(packet) {
|
||||||
return fmt.Errorf("TxLog written %d != packet size %d", n, len(packet))
|
return fmt.Errorf("TxLog written %d != packet size %d", n, len(packet))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.file.Sync(); err != nil {
|
if err := s.file.Sync(); err != nil {
|
||||||
return fmt.Errorf("TxLog sync: %s", err)
|
return fmt.Errorf("TxLog sync: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = s.writePagesToAtree(pagesToWrite)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("TxLog writePagesToAtree: %s", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
s.waitCh = make(chan struct{})
|
s.waitCh = make(chan struct{})
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
@@ -232,263 +299,48 @@ func (s *Writer) flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Writer) writePagesToAtree(pages []atree.PageToWrite) error {
|
||||||
|
for _, p := range pages {
|
||||||
|
if len(p.Data) != atree.PageSize {
|
||||||
|
return fmt.Errorf("wrong page %d size: %d",
|
||||||
|
p.PageNo, len(p.Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
off := (p.PageNo - 1) * atree.PageSize
|
||||||
|
n, err := s.dataFile.WriteAt(p.Data, int64(off))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n != len(p.Data) {
|
||||||
|
return fmt.Errorf("write %d instead of %d", n, len(p.Data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Writer) exit() {
|
func (s *Writer) exit() {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
s.isExited = true
|
s.isExited = true
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
|
||||||
if err := s.flush(); err != nil {
|
if err := s.packAndWrite(); err != nil {
|
||||||
octopus.Abort(octopus.FailedWriteToTxLog, err)
|
octopus.Abort(octopus.FailedWriteToTxLog, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// API
|
// API
|
||||||
|
|
||||||
type AddedMetric struct {
|
// FIX - add
|
||||||
MetricID uint32
|
// type DeletedMeasuresSince struct {
|
||||||
MetricType octopus.MetricType
|
// MetricID uint32
|
||||||
FracDigits int
|
// LastPageNo uint32
|
||||||
}
|
// IsRootChanged bool
|
||||||
|
// RootPageNo uint32
|
||||||
func (s *Writer) WriteAddedMetric(req AddedMetric) chan struct{} {
|
// FreeDataPages []uint32
|
||||||
arr := []byte{
|
// FreeIndexPages []uint32
|
||||||
CodeAddedMetric,
|
// TimestampsBuf []byte
|
||||||
0, 0, 0, 0, //
|
// ValuesBuf []byte
|
||||||
byte(req.MetricType),
|
// }
|
||||||
byte(req.FracDigits),
|
|
||||||
}
|
|
||||||
bin.PutUint32(arr[1:], req.MetricID)
|
|
||||||
// пишу в буфер
|
|
||||||
s.mutex.Lock()
|
|
||||||
s.buf.Write(arr)
|
|
||||||
s.workerReqs = append(s.workerReqs, req)
|
|
||||||
s.mutex.Unlock()
|
|
||||||
|
|
||||||
s.sendSignal()
|
|
||||||
return s.waitCh
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeletedMetric struct {
|
|
||||||
MetricID uint32
|
|
||||||
FreeDataPages []uint32
|
|
||||||
FreeIndexPages []uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Writer) WriteDeletedMetric(req DeletedMetric) chan struct{} {
|
|
||||||
arr := []byte{
|
|
||||||
CodeDeletedMetric,
|
|
||||||
0, 0, 0, 0, // metricID
|
|
||||||
}
|
|
||||||
bin.PutUint32(arr[1:], req.MetricID)
|
|
||||||
|
|
||||||
// пишу в буфер
|
|
||||||
s.mutex.Lock()
|
|
||||||
defer s.mutex.Unlock()
|
|
||||||
|
|
||||||
s.buf.Write(arr)
|
|
||||||
s.packFreeDataAndIndexPages(req.FreeDataPages, req.FreeIndexPages)
|
|
||||||
s.workerReqs = append(s.workerReqs, req)
|
|
||||||
|
|
||||||
s.sendSignal()
|
|
||||||
return s.waitCh
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppendedMeasure struct {
|
|
||||||
MetricID uint32
|
|
||||||
Timestamp uint32
|
|
||||||
Value float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Writer) WriteAppendMeasure(req AppendedMeasure) chan struct{} {
|
|
||||||
arr := []byte{
|
|
||||||
CodeAppendedMeasure,
|
|
||||||
0, 0, 0, 0, // metricID
|
|
||||||
0, 0, 0, 0, // timestamp
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, // value
|
|
||||||
}
|
|
||||||
bin.PutUint32(arr[1:], req.MetricID)
|
|
||||||
bin.PutUint32(arr[5:], req.Timestamp)
|
|
||||||
bin.PutFloat64(arr[9:], req.Value)
|
|
||||||
//
|
|
||||||
s.mutex.Lock()
|
|
||||||
s.buf.Write(arr)
|
|
||||||
s.workerReqs = append(s.workerReqs, req)
|
|
||||||
s.mutex.Unlock()
|
|
||||||
|
|
||||||
s.sendSignal()
|
|
||||||
return s.waitCh
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppendedMeasures struct {
|
|
||||||
MetricID uint32
|
|
||||||
Measures []proto.Measure
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppendedMeasuresExtended struct {
|
|
||||||
Record AppendedMeasures
|
|
||||||
HoldLock bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Writer) WriteAppendMeasures(req AppendedMeasures, holdLock bool) chan struct{} {
|
|
||||||
arr := []byte{
|
|
||||||
CodeAppendedMeasures,
|
|
||||||
0, 0, 0, 0, // metricID
|
|
||||||
0, 0, // qty
|
|
||||||
}
|
|
||||||
bin.PutUint32(arr[1:], req.MetricID)
|
|
||||||
bin.PutUint16(arr[5:], uint16(len(req.Measures)))
|
|
||||||
//
|
|
||||||
s.mutex.Lock()
|
|
||||||
s.buf.Write(arr)
|
|
||||||
for _, measure := range req.Measures {
|
|
||||||
bin.WriteUint32(s.buf, measure.Timestamp)
|
|
||||||
bin.WriteFloat64(s.buf, measure.Value)
|
|
||||||
}
|
|
||||||
s.workerReqs = append(s.workerReqs, AppendedMeasuresExtended{
|
|
||||||
Record: req,
|
|
||||||
HoldLock: holdLock,
|
|
||||||
})
|
|
||||||
s.mutex.Unlock()
|
|
||||||
|
|
||||||
s.sendSignal()
|
|
||||||
return s.waitCh
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppendedMeasureWithOverflow struct {
|
|
||||||
MetricID uint32
|
|
||||||
Timestamp uint32
|
|
||||||
Value float64
|
|
||||||
IsDataPageReused bool
|
|
||||||
DataPageNo uint32
|
|
||||||
IsRootChanged bool
|
|
||||||
RootPageNo uint32
|
|
||||||
ReusedIndexPages []uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppendedMeasureWithOverflowExtended struct {
|
|
||||||
Record AppendedMeasureWithOverflow
|
|
||||||
HoldLock bool
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Формат:
|
|
||||||
1b code
|
|
||||||
4b metricID
|
|
||||||
4b timestamp
|
|
||||||
8b value
|
|
||||||
1b isReusedDataPage
|
|
||||||
4b dataPageNo
|
|
||||||
1b isRootChanged
|
|
||||||
[4b] newRootPageNo
|
|
||||||
1b reusedIndexPages length
|
|
||||||
[N * 4b] reusedIndexPages
|
|
||||||
------------------
|
|
||||||
|
|
||||||
4b metricID
|
|
||||||
4b timestamp
|
|
||||||
8b value
|
|
||||||
4b newRootPageNo (0 if no new root page)
|
|
||||||
|
|
||||||
Nb - varsize (N pages)
|
|
||||||
[
|
|
||||||
|
|
||||||
4b pageNo
|
|
||||||
1b isReused
|
|
||||||
Xb page (page size)
|
|
||||||
|
|
||||||
]
|
|
||||||
Nb - varsize (last data page size)
|
|
||||||
Nb - payload
|
|
||||||
*/
|
|
||||||
func (s *Writer) WriteAppendedMeasureWithOverflow(req AppendedMeasureWithOverflow, holdLock bool) chan struct{} {
|
|
||||||
size := 24 + len(req.ReusedIndexPages)*4
|
|
||||||
if req.IsRootChanged {
|
|
||||||
size += 4
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp := make([]byte, size)
|
|
||||||
|
|
||||||
tmp[0] = CodeAppendedMeasureWithOverflow
|
|
||||||
bin.PutUint32(tmp[1:], req.MetricID)
|
|
||||||
bin.PutUint32(tmp[5:], req.Timestamp)
|
|
||||||
bin.PutFloat64(tmp[9:], req.Value)
|
|
||||||
if req.IsDataPageReused {
|
|
||||||
tmp[17] = 1
|
|
||||||
}
|
|
||||||
bin.PutUint32(tmp[18:], req.DataPageNo)
|
|
||||||
|
|
||||||
pos := 22
|
|
||||||
if req.IsRootChanged {
|
|
||||||
tmp[pos] = 1
|
|
||||||
bin.PutUint32(tmp[pos+1:], req.RootPageNo)
|
|
||||||
pos += 5
|
|
||||||
} else {
|
|
||||||
tmp[pos] = 0
|
|
||||||
pos += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp[pos] = byte(len(req.ReusedIndexPages))
|
|
||||||
pos += 1
|
|
||||||
for _, indexPageNo := range req.ReusedIndexPages {
|
|
||||||
bin.PutUint32(tmp[pos:], indexPageNo)
|
|
||||||
pos += 4
|
|
||||||
}
|
|
||||||
|
|
||||||
s.mutex.Lock()
|
|
||||||
s.buf.Write(tmp)
|
|
||||||
s.workerReqs = append(s.workerReqs, AppendedMeasureWithOverflowExtended{
|
|
||||||
Record: req,
|
|
||||||
HoldLock: holdLock,
|
|
||||||
})
|
|
||||||
s.mutex.Unlock()
|
|
||||||
|
|
||||||
s.sendSignal()
|
|
||||||
return s.waitCh
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeletedMeasures struct {
|
|
||||||
MetricID uint32
|
|
||||||
FreeDataPages []uint32
|
|
||||||
FreeIndexPages []uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Формат:
|
|
||||||
1b code
|
|
||||||
4b metricID
|
|
||||||
1b freeDataPages length
|
|
||||||
[N * 4b] freeDataPages
|
|
||||||
1b freeIndexPages length
|
|
||||||
[N * 4b] freeIndexPages
|
|
||||||
*/
|
|
||||||
func (s *Writer) WriteDeletedMeasures(op DeletedMeasures) chan struct{} {
|
|
||||||
tmp := []byte{
|
|
||||||
CodeDeletedMeasures,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
}
|
|
||||||
bin.PutUint32(tmp[1:], op.MetricID)
|
|
||||||
|
|
||||||
// записываю часть фиксированного размера
|
|
||||||
s.mutex.Lock()
|
|
||||||
s.buf.Write(tmp)
|
|
||||||
s.packFreeDataAndIndexPages(op.FreeDataPages, op.FreeIndexPages)
|
|
||||||
s.workerReqs = append(s.workerReqs, op)
|
|
||||||
s.mutex.Unlock()
|
|
||||||
|
|
||||||
s.sendSignal()
|
|
||||||
return s.waitCh
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeletedMeasuresSince struct {
|
|
||||||
MetricID uint32
|
|
||||||
LastPageNo uint32
|
|
||||||
IsRootChanged bool
|
|
||||||
RootPageNo uint32
|
|
||||||
FreeDataPages []uint32
|
|
||||||
FreeIndexPages []uint32
|
|
||||||
TimestampsBuf []byte
|
|
||||||
ValuesBuf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Writer) sendSignal() {
|
func (s *Writer) sendSignal() {
|
||||||
select {
|
select {
|
||||||
@@ -497,17 +349,30 @@ func (s *Writer) sendSignal() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper
|
// Якщо (Pages) > 0 - незаповнену сторінку кодую в стисненому вигляді.
|
||||||
|
// Якщо (Pages) = 0 - кодую просто пари timestamp / value
|
||||||
|
func (s *Writer) packWriteAppended(w *bytes.Buffer, req AppendedPagesReq) {
|
||||||
|
// завантажений path. Отже просто додаємо в дерево data сторінки, створюємо нові індексні
|
||||||
|
// без звернення до диску.
|
||||||
|
report := s.atree.AppendDataPages(atree.AppendDataPagesReq{
|
||||||
|
LastPageNo: req.LastPageNo,
|
||||||
|
Legs: req.Legs,
|
||||||
|
DataPages: req.Pages,
|
||||||
|
})
|
||||||
|
|
||||||
func (s *Writer) packFreeDataAndIndexPages(freeDataPages, freeIndexPages []uint32) {
|
m := AppendedPages{
|
||||||
// записываю data pages
|
MetricID: req.MetricID,
|
||||||
bin.WriteVarUint64(s.buf, uint64(len(freeDataPages)))
|
Timestamp: req.Timestamp,
|
||||||
for _, dataPageNo := range freeDataPages {
|
Value: req.Value,
|
||||||
bin.WriteUint32(s.buf, dataPageNo)
|
NewRootPageNo: report.NewRootPageNo,
|
||||||
}
|
LastPageNo: report.LastPageNo,
|
||||||
// записываю index pages
|
Pages: report.Pages,
|
||||||
bin.WriteVarUint64(s.buf, uint64(len(freeIndexPages)))
|
TimestampsChunks: req.TimestampsChunks,
|
||||||
for _, indexPageNo := range freeIndexPages {
|
TimestampsSize: int(req.TimestampsSize),
|
||||||
bin.WriteUint32(s.buf, indexPageNo)
|
ValuesChunks: req.ValuesChunks,
|
||||||
|
ValuesSize: int(req.ValuesSize),
|
||||||
}
|
}
|
||||||
|
m.Pack(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
|||||||
Reference in New Issue
Block a user