package chunkenc import ( "fmt" "log" "math" "gordenko.dev/dima/qb/conbuf" ) type ReverseInstantDeltaCompressor struct { buf *conbuf.ContinuousBuffer coef float64 pos int baseValue float64 lastDelta int64 lastDeltaSize int h byte } 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 { 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 } func (s *ReverseInstantDeltaCompressor) Size() int { return s.pos } func (s *ReverseInstantDeltaCompressor) Append(value float64) { if s.pos == 0 { // base value n := s.buf.PutVarInt64(s.pos, int64(value*s.coef)) s.pos += n s.baseValue = value s.appendNewLiteral(0) } else { tmp := (value - s.baseValue) * s.coef if tmp > 0 { tmp += eps } else { tmp -= eps } delta := int64(tmp) 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 // fix encode delta s.appendNewLiteral(delta) } } else { // literal блок. // Якщо в ньому лише одне значення - перетворюю його на run блок. // Інакше забираю останнє значення із literal блока і додаю новий run блок. q := s.h & 127 if q == 0 { // 1 кодується як 0 s.convertLiteralToRun() } else { s.convertLastFromLiteralToRun() } } } else { 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) convertLastFromLiteralToRun() { // Зменшую кількість елементів в literal блоці s.h-- s.pos -= 1 + s.lastDeltaSize s.buf.SetByte(s.pos, s.h) // закриваю literal блок s.pos++ s.lastDeltaSize = s.buf.ReversePutVarInt64(s.pos, s.lastDelta) s.pos += s.lastDeltaSize s.h = 0 // run блок, довжини 2 s.buf.SetByte(s.pos, s.h) s.pos++ } 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.pos-- s.lastDeltaSize = s.buf.ReversePutVarInt64(s.pos, delta) s.pos += s.lastDeltaSize s.buf.SetByte(s.pos, s.h) s.pos++ } func (s *ReverseInstantDeltaCompressor) appendNewLiteral(delta int64) { 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++ } func (s *ReverseInstantDeltaCompressor) DeleteLast() {} type InstantDeltaBound struct { Pos int H byte LastDelta int64 } func (s *ReverseInstantDeltaCompressor) GetState() InstantDeltaBound { return InstantDeltaBound{ Pos: s.pos - 1 - s.lastDeltaSize, H: s.h, LastDelta: s.lastDelta, } } // DECOMPRESSOR type ReverseInstantDeltaDecompressor struct { buf *conbuf.ContinuousBuffer coef float64 pos int bound int baseValue float64 lastValue float64 isRun bool pending int done bool } func NewReverseInstantDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int, fracDigits byte) *ReverseInstantDeltaDecompressor { var coef float64 = 1 if fracDigits > 0 { coef = math.Pow(10, float64(fracDigits)) } i64, 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(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) { //fmt.Printf("NextValue(): bound: %d, pos: %d, pending: %d\n", s.bound, s.pos, s.pending) if s.done { return 0, true } // повертаю значення, що було прочитано в методі RestoreFromBound/RestoreFromEnd value = s.lastValue 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 } // серія завершена - перевіряю чи є ще серії return value, false } 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) 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 } func (s *ReverseInstantDeltaDecompressor) decodeHeaderByte(h byte) { s.isRun = h < 128 if s.isRun { s.pending = int(h&127) + 2 } else { 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 // }