Files
qb/chunkenc/insdelta.go

421 lines
11 KiB
Go
Raw Normal View History

2026-02-10 14:02:11 +00:00
package chunkenc
import (
"fmt"
2026-05-10 00:59:47 +00:00
"log"
2026-02-10 14:02:11 +00:00
"math"
2026-05-31 20:01:28 +00:00
"gordenko.dev/dima/qb"
2026-06-05 19:43:01 +00:00
"gordenko.dev/dima/qb/bin"
2026-02-10 14:02:11 +00:00
"gordenko.dev/dima/qb/conbuf"
)
type ReverseInstantDeltaCompressor struct {
2026-06-05 19:43:01 +00:00
buf []byte
2026-05-10 00:59:47 +00:00
coef float64
pos int
baseValue float64
lastDelta int64
lastDeltaSize int
h byte
2026-05-31 20:01:28 +00:00
state *InstantDeltaBound
2026-02-10 14:02:11 +00:00
}
2026-06-05 19:43:01 +00:00
func NewReverseInstantDeltaCompressor(buf []byte, size int, fracDigits byte) *ReverseInstantDeltaCompressor {
2026-02-10 14:02:11 +00:00
var coef float64 = 1
if fracDigits > 0 {
coef = math.Pow(10, float64(fracDigits))
}
s := &ReverseInstantDeltaCompressor{
buf: buf,
pos: size,
coef: coef,
}
if size > 0 {
2026-06-05 19:43:01 +00:00
i64, _, err := bin.GetVarInt64(s.buf)
2026-02-10 14:02:11 +00:00
if err != nil {
2026-05-10 00:59:47 +00:00
log.Fatalf("bug: get base value: %s", err)
2026-02-10 14:02:11 +00:00
}
2026-05-10 00:59:47 +00:00
s.baseValue = float64(i64) / s.coef
2026-06-05 19:43:01 +00:00
s.h = s.buf[s.pos-1]
s.lastDelta, s.lastDeltaSize, err = bin.ReverseGetVarInt64(s.buf[:s.pos-2])
2026-05-10 00:59:47 +00:00
if err != nil {
log.Fatalf("bug: get last delta: %s", err)
2026-02-10 14:02:11 +00:00
}
}
2026-05-10 00:59:47 +00:00
return s
2026-02-10 14:02:11 +00:00
}
func (s *ReverseInstantDeltaCompressor) Size() int {
return s.pos
}
func (s *ReverseInstantDeltaCompressor) Append(value float64) {
if s.pos == 0 {
2026-05-10 00:59:47 +00:00
// base value
2026-06-05 19:43:01 +00:00
n, _ := bin.PutVarInt64(s.buf[s.pos:], int64(value*s.coef))
2026-02-10 14:02:11 +00:00
s.pos += n
2026-05-10 00:59:47 +00:00
s.baseValue = value
s.appendNewLiteral(0)
2026-02-10 14:02:11 +00:00
} else {
2026-05-10 00:59:47 +00:00
tmp := (value - s.baseValue) * s.coef
2026-02-10 14:02:11 +00:00
if tmp > 0 {
tmp += eps
} else {
tmp -= eps
}
delta := int64(tmp)
if delta == s.lastDelta {
2026-05-10 00:59:47 +00:00
if s.h < 128 {
// run блок - отже треба збільшити лічильник
if s.h < 127 {
// increase counter
s.h++
2026-06-05 19:43:01 +00:00
s.buf[s.pos-1] = s.h
2026-05-10 00:59:47 +00:00
} else {
// не можу збільшити - буде переповнення. Додаю новий literal блок
// counter overflow
// fix encode delta
s.appendNewLiteral(delta)
}
2026-02-10 14:02:11 +00:00
} else {
2026-05-10 00:59:47 +00:00
// literal блок.
// Якщо в ньому лише одне значення - перетворюю його на run блок.
// Інакше забираю останнє значення із literal блока і додаю новий run блок.
q := s.h & 127
if q == 0 { // 1 кодується як 0
s.convertLiteralToRun()
2026-02-10 14:02:11 +00:00
} else {
2026-05-10 00:59:47 +00:00
s.convertLastFromLiteralToRun()
2026-02-10 14:02:11 +00:00
}
}
} else {
2026-05-10 00:59:47 +00:00
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)
}
}
2026-02-10 14:02:11 +00:00
}
}
}
2026-05-10 00:59:47 +00:00
func (s *ReverseInstantDeltaCompressor) convertLastFromLiteralToRun() {
// Зменшую кількість елементів в literal блоці
s.h--
s.pos -= 1 + s.lastDeltaSize
2026-06-05 19:43:01 +00:00
s.buf[s.pos] = s.h // закриваю literal блок
2026-05-10 00:59:47 +00:00
s.pos++
2026-06-05 19:43:01 +00:00
s.lastDeltaSize, _ = bin.ReversePutVarInt64(s.buf[:s.pos], s.lastDelta)
2026-05-10 00:59:47 +00:00
s.pos += s.lastDeltaSize
s.h = 0 // run блок, довжини 2
2026-06-05 19:43:01 +00:00
s.buf[s.pos] = s.h
2026-05-10 00:59:47 +00:00
s.pos++
}
2026-02-10 14:02:11 +00:00
2026-05-10 00:59:47 +00:00
func (s *ReverseInstantDeltaCompressor) convertLiteralToRun() {
// Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії.
s.h = 0
2026-06-05 19:43:01 +00:00
s.buf[s.pos-1] = s.h
2026-02-10 14:02:11 +00:00
}
2026-05-10 00:59:47 +00:00
func (s *ReverseInstantDeltaCompressor) appendDeltaToLiteral(delta int64) {
s.h++ // збільшую к-сть дельт
2026-02-10 14:02:11 +00:00
s.lastDelta = delta
2026-05-10 00:59:47 +00:00
s.pos--
2026-06-05 19:43:01 +00:00
s.lastDeltaSize, _ = bin.ReversePutVarInt64(s.buf[s.pos:], delta)
2026-05-10 00:59:47 +00:00
s.pos += s.lastDeltaSize
2026-06-05 19:43:01 +00:00
s.buf[s.pos] = s.h
2026-02-10 14:02:11 +00:00
s.pos++
}
2026-05-10 00:59:47 +00:00
func (s *ReverseInstantDeltaCompressor) appendNewLiteral(delta int64) {
s.h = flagLiteral
s.lastDelta = delta
2026-06-05 19:43:01 +00:00
s.lastDeltaSize, _ = bin.ReversePutVarInt64(s.buf[s.pos:], delta)
2026-05-10 00:59:47 +00:00
s.pos += s.lastDeltaSize
2026-06-05 19:43:01 +00:00
s.buf[s.pos] = flagLiteral // literal, length = 1
2026-02-10 14:02:11 +00:00
s.pos++
}
2026-05-10 00:59:47 +00:00
func (s *ReverseInstantDeltaCompressor) DeleteLast() {}
2026-02-10 14:02:11 +00:00
2026-05-10 00:59:47 +00:00
type InstantDeltaBound struct {
Pos int
H byte
LastDelta int64
2026-06-05 19:43:01 +00:00
Chunks []byte
2026-05-10 00:59:47 +00:00
}
2026-02-10 14:02:11 +00:00
2026-05-31 20:01:28 +00:00
// delta h
func (s *ReverseInstantDeltaCompressor) Lock() {
if s.state != nil {
qb.Abort(qb.RepeatableLock, nil)
}
// позиція посувається вліво, отже може перескочити на попередній chunk
pos := s.pos - 1 - s.lastDeltaSize
s.state = &InstantDeltaBound{
Pos: pos,
2026-05-10 00:59:47 +00:00
H: s.h,
LastDelta: s.lastDelta,
2026-06-05 19:43:01 +00:00
Chunks: s.buf[:s.pos], // fix check ?
2026-02-10 14:02:11 +00:00
}
}
2026-05-31 20:01:28 +00:00
// fix - повернути в Pool буфери
func (s *ReverseInstantDeltaCompressor) Unlock() {
s.state = nil
2026-05-21 23:38:52 +03:00
}
2026-05-31 20:01:28 +00:00
func (s *ReverseInstantDeltaCompressor) Offset() int {
if s.state != nil {
return s.state.Pos
}
return 0
}
2026-06-05 19:43:01 +00:00
func (s *ReverseInstantDeltaCompressor) Snapshot() ([]byte, int) {
2026-05-31 20:01:28 +00:00
if s.state == nil {
2026-06-05 19:43:01 +00:00
return s.buf, s.Size()
2026-05-31 20:01:28 +00:00
}
// ВАЖЛИВО!
// Треба відтворити стан останнього чанка
var (
pos = s.state.Pos
chunk = make([]byte, conbuf.ChunkSize)
lastChunkIdx = len(s.state.Chunks) - 1
qtyToCopy = pos % conbuf.ChunkSize
)
copy(chunk, s.state.Chunks[lastChunkIdx][:qtyToCopy])
chunks := append(s.state.Chunks[:lastChunkIdx], chunk)
buf := conbuf.New(chunks)
pos += buf.ReversePutVarInt64(pos, s.state.LastDelta)
buf.SetByte(pos, s.state.H)
pos++
return chunks, pos
}
func (s *ReverseInstantDeltaCompressor) CreateDecompressor(fracDigits byte) qb.ValueDecompressor {
if s.state == nil {
d := NewReverseInstantDeltaDecompressor(s.buf, s.Size(), fracDigits)
d.RestoreFromEnd()
return d
}
d := NewReverseInstantDeltaDecompressor(s.buf, s.Size(), fracDigits)
d.RestoreFromBound(*s.state)
return d
2026-05-21 23:38:52 +03:00
}
func (s *ReverseInstantDeltaCompressor) Renew() {
2026-05-31 20:01:28 +00:00
// УВАГА!
// state не чіпаємо
2026-06-05 19:43:01 +00:00
s.buf = make([]byte, minBufferSize)
2026-05-21 23:38:52 +03:00
s.pos = 0
//
s.baseValue = 0
s.lastDelta = 0
s.lastDeltaSize = 0
s.h = 0
}
func (s *ReverseInstantDeltaCompressor) CalcRequiredSpace(value float64) int {
return 0
}
2026-06-05 19:43:01 +00:00
func (s *ReverseInstantDeltaCompressor) Chunks() []byte {
return s.buf
2026-05-21 23:38:52 +03:00
}
2026-05-10 00:59:47 +00:00
// DECOMPRESSOR
2026-02-10 14:02:11 +00:00
type ReverseInstantDeltaDecompressor struct {
2026-06-05 19:43:01 +00:00
buf []byte
2026-05-10 00:59:47 +00:00
coef float64
pos int
bound int
baseValue float64
lastValue float64
isRun bool
pending int
done bool
2026-02-10 14:02:11 +00:00
}
2026-06-05 19:43:01 +00:00
func NewReverseInstantDeltaDecompressor(buf []byte, size int, fracDigits byte) *ReverseInstantDeltaDecompressor {
2026-02-10 14:02:11 +00:00
var coef float64 = 1
if fracDigits > 0 {
coef = math.Pow(10, float64(fracDigits))
}
2026-06-05 19:43:01 +00:00
i64, n, err := bin.GetVarInt64(buf)
2026-05-10 00:59:47 +00:00
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)
2026-02-10 14:02:11 +00:00
return &ReverseInstantDeltaDecompressor{
2026-05-10 00:59:47 +00:00
buf: buf,
coef: coef,
pos: size - 1, // last elem
baseValue: float64(i64) / coef,
bound: n,
2026-02-10 14:02:11 +00:00
}
}
2026-05-10 00:59:47 +00:00
func (s *ReverseInstantDeltaDecompressor) RestoreFromEnd() {
if s.pos > s.bound {
// читаю заголовок наступної серії
s.readHeader()
s.readValue()
} else {
s.done = true
2026-02-10 14:02:11 +00:00
}
2026-05-10 00:59:47 +00:00
}
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
2026-02-10 14:02:11 +00:00
}
2026-05-10 00:59:47 +00:00
// повертаю значення, що було прочитано в методі 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
2026-02-10 14:02:11 +00:00
}
2026-05-10 00:59:47 +00:00
// серія завершена - перевіряю чи є ще серії
return value, false
2026-02-10 14:02:11 +00:00
}
2026-05-10 00:59:47 +00:00
func (s *ReverseInstantDeltaDecompressor) readHeader() {
2026-06-05 19:43:01 +00:00
h := s.buf[s.pos]
2026-05-10 00:59:47 +00:00
s.pos--
s.decodeHeaderByte(h)
// fmt.Println("h:", h)
// fmt.Println("isRun:", s.isRun)
// fmt.Println("pending:", s.pending)
}
func (s *ReverseInstantDeltaDecompressor) readValue() {
2026-06-05 19:43:01 +00:00
i64, n, err := bin.ReverseGetVarInt64(s.buf[:s.pos])
2026-02-10 14:02:11 +00:00
if err != nil {
2026-05-10 00:59:47 +00:00
log.Fatalln(err)
2026-02-10 14:02:11 +00:00
}
2026-05-10 00:59:47 +00:00
// fmt.Println()
// fmt.Println("read from pos:", s.pos)
// fmt.Println("read delta:", u64)
// fmt.Println("read delta n:", n)
// fmt.Println()
2026-02-10 14:02:11 +00:00
s.pos -= n
2026-05-10 00:59:47 +00:00
s.lastValue = s.baseValue + float64(i64)/s.coef
}
2026-02-10 14:02:11 +00:00
2026-05-10 00:59:47 +00:00
func (s *ReverseInstantDeltaDecompressor) decodeHeaderByte(h byte) {
s.isRun = h < 128
if s.isRun {
s.pending = int(h&127) + 2
2026-02-10 14:02:11 +00:00
} else {
2026-05-10 00:59:47 +00:00
s.pending = int(h&127) + 1
2026-02-10 14:02:11 +00:00
}
}
2026-05-10 00:59:47 +00:00
// 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
// }