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) Chunks() [][]byte { return s.buf.Chunks() } func (s *ReverseTimeDeltaCompressor) CalcRequiredSpace(value uint32) int { return 0 } 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 { // fix replace by Lock 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 } func (s *ReverseTimeDeltaCompressor) Lock() { // fix } func (s *ReverseTimeDeltaCompressor) Unlock() { //s.state = nil // fix } func (s *ReverseTimeDeltaCompressor) Renew() { // s.buf = conbuf.New(nil) // s.pos = 0 // // // s.baseValue = 0 // s.lastDelta = 0 // s.lastDeltaSize = 0 // s.h = 0 } // 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) }