This commit is contained in:
2026-06-09 08:16:16 +03:00
parent 964fe4b4a8
commit 7007b1b6fd
18 changed files with 1334 additions and 1026 deletions

View File

@@ -1,6 +1,7 @@
package enc
import (
"io"
"log"
bin "gordenko.dev/dima/bin/little"
@@ -27,125 +28,125 @@ type TimeDeltaCompressor struct {
state *TimeDeltaCapturedState
}
// Кодує значення справа наліво футнкціями TailPut*. Читає значення зліва направо функціями Get*
func NewTimeDeltaCompressor(buf []byte, payloadSize int) *TimeDeltaCompressor {
s := &TimeDeltaCompressor{
// Початкове створення, коли даних немає
// Створення після page filled, коли одразу є початкове значення
// Відновлення стану із снапшота. LastTimestamp передаю окермо від payload,
// а lastDelta і h треба прочитати із payload (якщо є)
// Кодує значення справа наліво функціями TailPut*. Читає значення зліва направо функціями Get*
func NewTimeDeltaCompressor(buf []byte) *TimeDeltaCompressor {
return &TimeDeltaCompressor{
buf: buf,
pos: len(buf) - payloadSize,
}
}
func (s *TimeDeltaCompressor) RestoreState(payloadSize int, lastTimestamp uint32) {
if payloadSize > 0 {
var err error
s.lastUnixtime, err = bin.GetUint32(s.buf[s.pos:])
if err != nil {
log.Fatalf("bug: get last unixtime: %s", err)
}
s.pos += 4
if s.pos < len(buf) {
s.lastUnixtime = lastTimestamp
s.pos = len(s.buf) - payloadSize
if payloadSize > 4 {
s.h = s.buf[s.pos]
s.pos++
u64, n, err := bin.GetVarUint64(s.buf[s.pos:])
u64, _, err := bin.GetVarUint64(s.buf[s.pos+1:])
if err != nil {
log.Fatalf("bug: get last delta: %s", err)
}
s.lastDelta = uint32(u64)
s.pos += n
}
}
return s
}
// func (s *TimeDeltaCompressor) Size() int {
// return len(s.buf) - s.pos
// }
// retrun
func (s *TimeDeltaCompressor) Evaluate(unixtime uint32) (compressionWay int, requiredSpace int) {
requiredSpace = 4 // last unixtime
if s.lastUnixtime > 0 {
requiredSpace = len(s.buf) - s.pos
if requiredSpace > 0 {
delta := unixtime - s.lastUnixtime
if s.lastDelta > 0 {
// 3rd value
if s.h < 128 {
// run
if delta == s.lastDelta && s.h < 127 {
compressionWay = incrementRun
requiredSpace += bin.CountVarUint64(uint64(s.lastDelta)) + // current delta
hSize
} else {
compressionWay = endSeries
requiredSpace += bin.CountVarUint64(uint64(s.lastDelta)) + // previous delta
hSize + // h - end of run
bin.CountVarUint64(uint64(delta)) + // new literal
hSize // new h
requiredSpace += bin.CountVarUint64(uint64(delta)) + hSize
}
} else {
// literal
if delta != s.lastDelta {
if s.h < 255 {
compressionWay = incrementLiteral
requiredSpace += bin.CountVarUint64(uint64(s.lastDelta)) + // previous delta
bin.CountVarUint64(uint64(delta)) + // new delta
hSize
requiredSpace += bin.CountVarUint64(uint64(delta))
} else {
compressionWay = endSeries
requiredSpace += bin.CountVarUint64(uint64(s.lastDelta)) + // previous delta
hSize + // h - end of literal
bin.CountVarUint64(uint64(delta)) + // new literal
hSize // new h
requiredSpace += bin.CountVarUint64(uint64(delta)) + hSize
}
} else {
compressionWay = startRun
if s.h > 128 {
requiredSpace += hSize // h - end of literal
requiredSpace += hSize // h - end of run
}
requiredSpace += bin.CountVarUint64(uint64(delta)) + // new delta
hSize // new h
}
}
} else {
compressionWay = add1stDelta
requiredSpace += bin.CountVarUint64(uint64(delta)) + // new literal
hSize // new h
requiredSpace += bin.CountVarUint64(uint64(delta)) + hSize
}
} else {
compressionWay = addUnixtime
requiredSpace += 4
}
return
}
// pos вказує на h byte
func (s *TimeDeltaCompressor) Compress(compressionWay int, unixtime uint32) {
delta := unixtime - s.lastUnixtime
switch compressionWay {
case incrementRun:
s.h++
s.buf[s.pos] = s.h
case incrementLiteral:
// write previous delta and increment counter
n, _ := bin.TailPutVarUint64(s.buf[:s.pos], uint64(s.lastDelta))
s.pos -= n
s.lastDelta = delta
s.h++
case endSeries:
n, _ := bin.TailPutVarUint64(s.buf[:s.pos], uint64(s.lastDelta))
// тому що перезаписую h
s.pos++
n, _ := bin.TailPutVarUint64(s.buf[:s.pos], uint64(delta))
s.pos -= n
// write h - end of run
s.pos--
s.buf[s.pos] = s.h
case endSeries:
// start new literal (length=1)
s.lastDelta = delta
s.h = 128
n, _ := bin.TailPutVarUint64(s.buf[:s.pos], uint64(delta))
s.pos -= n
s.pos--
s.buf[s.pos] = s.h
case startRun:
if s.h > 128 {
// write h - end of literal (because length > 1)
s.h--
s.pos += bin.CountVarUint64(uint64(s.lastDelta)) // забираю останню дельту у literal
s.buf[s.pos] = s.h // write h - end of literal (because length > 1)
s.pos--
// run delta
n, _ := bin.TailPutVarUint64(s.buf[:s.pos], uint64(delta))
s.pos -= n
// для h
s.pos--
s.buf[s.pos] = s.h
}
// start new run (length=2)
s.h = 0
s.buf[s.pos] = s.h
case add1stDelta:
// start new literal (length=1)
s.lastDelta = delta
s.h = 128
n, _ := bin.TailPutVarUint64(s.buf[:s.pos], uint64(delta))
s.pos -= n
s.pos--
s.buf[s.pos] = s.h
case addUnixtime:
s.pos -= 4
bin.PutUint32(s.buf[s.pos:], unixtime)
}
// 1st value or update after every update
// do not write immediatelly because of update after every update
@@ -184,6 +185,14 @@ func (s *TimeDeltaCompressor) ForgetCapturedState() {
s.state = nil
}
// коли сторінка заповнена і відправляється в txlog, since замінюю на until
func (s *TimeDeltaCompressor) ReplaceSinceWithUntil() uint32 {
pos := len(s.buf) - 4
since, _ := bin.GetUint32(s.buf[pos:])
bin.PutUint32(s.buf[pos:], s.lastUnixtime)
return since
}
// fix
func (s *TimeDeltaCompressor) Offset() int {
// if s.state != nil {
@@ -192,35 +201,64 @@ func (s *TimeDeltaCompressor) Offset() int {
return 0
}
// Snapshot - для створення снапшота.
func (s *TimeDeltaCompressor) Snapshot() (left []byte, right []byte) {
func (s *TimeDeltaCompressor) Size() int {
if s.state == nil {
left = s.encodeTail(s.lastUnixtime, s.lastDelta, s.h)
right = s.buf[s.pos:]
return len(s.buf) - s.pos
} else {
left = s.encodeTail(s.state.LastUnixtime, s.state.LastDelta, s.state.H)
right = s.state.Payload
return len(s.state.Payload)
}
return
}
func (s *TimeDeltaCompressor) encodeTail(lastUnixtime, lastDelta uint32, h byte) []byte {
tail := make([]byte, 9)
bin.PutUint32(tail, lastUnixtime)
tail[4] = h
n, _ := bin.PutVarUint64(tail[5:], uint64(lastDelta))
return tail[:n+5]
// Snapshot - для створення снапшота.
// FIX - encode firstUnixtime in 1st 4 bytes
//
// while page not completed - has first unixtime, after - last unixtime
func (s *TimeDeltaCompressor) Payload() []byte {
if s.state == nil {
return s.buf[s.pos:]
} else {
return s.state.Payload
}
}
func (s *TimeDeltaCompressor) Rotate(newbuf []byte) (payloadSize int) {
var n int
n, _ = bin.TailPutVarUint64(s.buf[:s.pos], uint64(s.lastDelta))
s.pos -= n
s.pos--
s.buf[s.pos] = s.h
n, _ = bin.TailPutVarUint64(s.buf[:s.pos], uint64(s.lastUnixtime))
s.pos -= n
payloadSize = len(s.buf) - s.pos
func (s *TimeDeltaCompressor) WritePayloadTo(w io.Writer) (err error) {
if s.state == nil {
err = bin.WriteUint16(w, uint16(s.pos))
if err != nil {
return
}
_, err = w.Write(s.buf[:s.pos])
return
} else {
size := bin.CountVarUint64(uint64(s.state.LastDelta)) + hSize + len(s.state.Payload)
err = bin.WriteUint16(w, uint16(size))
if err != nil {
return
}
_, err = w.Write(s.state.Payload)
if err != nil {
return
}
_, err = bin.WriteVarUint64(w, uint64(s.state.LastDelta))
if err != nil {
return
}
w.Write([]byte{
s.state.H,
})
return
}
}
// func (s *TimeDeltaCompressor) encodeTail(lastUnixtime, lastDelta uint32, h byte) []byte {
// tail := make([]byte, 9)
// bin.PutUint32(tail, lastUnixtime)
// tail[4] = h
// n, _ := bin.PutVarUint64(tail[5:], uint64(lastDelta))
// return tail[:n+5]
// }
func (s *TimeDeltaCompressor) Rotate(newbuf []byte) {
// УВАГА!
// state не чіпаємо
s.buf = newbuf
@@ -228,7 +266,6 @@ func (s *TimeDeltaCompressor) Rotate(newbuf []byte) (payloadSize int) {
s.lastUnixtime = 0
s.lastDelta = 0
s.h = 0
return
}
func (s *TimeDeltaCompressor) CreateDecompressor() qb.TimestampDecompressor {
@@ -252,8 +289,8 @@ func (s *TimeDeltaCompressor) CreateDecompressor() qb.TimestampDecompressor {
})
}
func (s *TimeDeltaCompressor) Chunks() []byte {
return s.buf
func (s *TimeDeltaCompressor) LastTimestamp() uint32 {
return s.lastUnixtime
}
// DECOMPRESSOR