package enc import ( "io" "log" "math" bin "gordenko.dev/dima/bin/little" "gordenko.dev/dima/qb" ) type InstantDeltaCompressor struct { buf []byte coef float64 pos int baseValue float64 lastDelta int64 h byte state *InstantDeltaCapturedState } func NewInstantDeltaCompressor(buf []byte, fracDigits byte) *InstantDeltaCompressor { var coef float64 = 1 if fracDigits > 0 { coef = math.Pow(10, float64(fracDigits)) } s := &InstantDeltaCompressor{ buf: buf, coef: coef, } return s } func (s *InstantDeltaCompressor) RestoreState(payloadSize int) { if payloadSize > 0 { s.pos = payloadSize - 1 // завжди показує на h // base value на початку u64, _, err := bin.GetVarUint64(s.buf) if err != nil { log.Fatalf("bug: get base value: %s", err) } s.baseValue = float64(u64) / s.coef s.h = s.buf[s.pos] s.lastDelta, _, err = bin.ReverseGetVarInt64(s.buf[:s.pos-1]) if err != nil { log.Fatalf("bug: get last delta: %s", err) } } } // func (s *InstantDeltaCompressor) Size() int { // return s.pos // } func (s *InstantDeltaCompressor) Evaluate(value float64) (compressionWay int, requiredSpace int) { tmp := (value - s.baseValue) * s.coef if tmp > 0 { tmp += eps } else { tmp -= eps } delta := int64(tmp) if s.pos > 0 { if s.h < 128 { // run if delta == s.lastDelta && s.h < 127 { compressionWay = incrementRun requiredSpace += bin.CountVarInt64(s.lastDelta) + // current delta hSize } else { compressionWay = endSeries requiredSpace += bin.CountVarInt64(s.lastDelta) + // previous delta hSize + // h - end of run bin.CountVarInt64(delta) + // new literal hSize // new h } } else { // literal if delta != s.lastDelta { if s.h < 255 { compressionWay = incrementLiteral requiredSpace += bin.CountVarInt64(s.lastDelta) + // previous delta bin.CountVarInt64(delta) + // new delta hSize } else { compressionWay = endSeries requiredSpace += bin.CountVarInt64(s.lastDelta) + // previous delta hSize + // h - end of literal bin.CountVarInt64(delta) + // new literal hSize // new h } } else { compressionWay = startRun if s.h > 128 { requiredSpace += hSize // h - end of literal } requiredSpace += bin.CountVarInt64(delta) + // new delta hSize // new h } } } else { // encode base value compressionWay = addBaseValue requiredSpace = bin.CountVarInt64(int64(value*s.coef)) + // base value bin.CountVarInt64(delta) + // new delta +hSize // new h } return } func (s *InstantDeltaCompressor) Compress(compressionWay int, value float64) { tmp := (value - s.baseValue) * s.coef if tmp > 0 { tmp += eps } else { tmp -= eps } delta := int64(tmp) switch compressionWay { case incrementRun: s.h++ case incrementLiteral: // write previous delta and increment counter n, _ := bin.ReversePutVarInt64(s.buf[s.pos:], s.lastDelta) s.pos += n s.lastDelta = delta s.h++ case endSeries: n, _ := bin.ReversePutVarInt64(s.buf[s.pos:], s.lastDelta) s.pos += n // write h - end of run s.buf[s.pos] = s.h s.pos++ // start new literal (length=1) s.lastDelta = delta s.h = 128 case startRun: if s.h > 128 { // write h - end of literal (because length > 1) s.h-- s.buf[s.pos] = s.h s.pos++ } // start new run (length=2) s.h = 0 case addBaseValue: // start new literal (length=1) n, _ := bin.PutVarInt64(s.buf[s.pos:], int64(value*s.coef)) s.pos += n s.baseValue = value // start new literal (length=1) s.lastDelta = 0 s.h = 128 } } func (s *InstantDeltaCompressor) DeleteLast() {} type InstantDeltaCapturedState struct { H byte LastDelta int64 Payload []byte } func (s *InstantDeltaCompressor) CaptureState() { if s.state != nil { qb.Abort(qb.RepeatableLock, nil) } // позиція посувається вліво, отже може перескочити на попередній chunk s.state = &InstantDeltaCapturedState{ H: s.h, LastDelta: s.lastDelta, Payload: s.buf[:s.pos], } } // fix - повернути в Pool буфери func (s *InstantDeltaCompressor) ForgetCapturedState() { s.state = nil } func (s *InstantDeltaCompressor) Offset() int { // if s.state != nil { // return s.state.Pos // } return 0 } func (s *InstantDeltaCompressor) Size() int { if s.state == nil { return s.pos } else { return len(s.state.Payload) } } // Snapshot - для створення снапшота. func (s *InstantDeltaCompressor) Payload() []byte { if s.state == nil { return s.buf[:s.pos] } else { return s.state.Payload } } func (s *InstantDeltaCompressor) 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.CountVarInt64(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.WriteVarInt64(w, s.state.LastDelta) if err != nil { return } w.Write([]byte{ s.state.H, }) return } } // func (s *InstantDeltaCompressor) encodeTail(lastDelta int64, h byte) []byte { // tail := make([]byte, 10) // max var uint64 + h // n, _ := bin.PutVarInt64(tail, lastDelta) // tail[n] = h // return tail[:n+1] // } func (s *InstantDeltaCompressor) Rotate(newbuf []byte) { // УВАГА! // state не чіпаємо s.buf = newbuf s.pos = 0 s.baseValue = 0 s.lastDelta = 0 s.h = 0 } func (s *InstantDeltaCompressor) LastValue() float64 { if s.state == nil { return s.baseValue + float64(s.lastDelta)*s.coef } else { return s.baseValue + float64(s.state.LastDelta)*s.coef } } func (s *InstantDeltaCompressor) CreateDecompressor() qb.ValueDecompressor { var ( h = s.h lastDelta = s.lastDelta payload = s.buf[:s.pos] ) if s.state != nil { h = s.state.H lastDelta = s.state.LastDelta payload = s.state.Payload } return NewInstantDeltaDecompressorFromState(InstantDeltaDecompressorFromStateOptions{ Coef: s.coef, H: h, LastDelta: lastDelta, Payload: payload, }) } func (s *InstantDeltaCompressor) Chunks() []byte { return s.buf } // DECOMPRESSOR type InstantDeltaDecompressor struct { buf []byte coef float64 pos int bound int baseValue float64 lastValue float64 isRun bool pending int done bool } func NewInstantDeltaDecompressor(buf []byte, fracDigits byte) *InstantDeltaDecompressor { var coef float64 = 1 if fracDigits > 0 { coef = math.Pow(10, float64(fracDigits)) } i64, n, err := bin.GetVarInt64(buf) 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) s := &InstantDeltaDecompressor{ buf: buf, coef: coef, pos: len(buf), // first free baseValue: float64(i64) / coef, bound: n, } s.readHeader() s.readValue() return s } type InstantDeltaDecompressorFromStateOptions struct { Coef float64 H byte LastDelta int64 Payload []byte } func NewInstantDeltaDecompressorFromState(opt InstantDeltaDecompressorFromStateOptions) *InstantDeltaDecompressor { i64, n, err := bin.GetVarInt64(opt.Payload) 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) s := &InstantDeltaDecompressor{ buf: opt.Payload, coef: opt.Coef, pos: len(opt.Payload), // first free baseValue: float64(i64) / opt.Coef, bound: n, } s.lastValue = s.baseValue + float64(opt.LastDelta)/s.coef s.decodeHeaderByte(opt.H) //fmt.Printf("restore from bound: isRun=%t, pending=%d\n", s.isRun, s.pending) return s } func (s *InstantDeltaDecompressor) 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 } // метод працює як do while - спочатку значення, а потім перевірка умови 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 *InstantDeltaDecompressor) readHeader() { s.pos-- h := s.buf[s.pos] s.decodeHeaderByte(h) // fmt.Println("h:", h) // fmt.Println("isRun:", s.isRun) // fmt.Println("pending:", s.pending) } func (s *InstantDeltaDecompressor) readValue() { i64, n, err := bin.ReverseGetVarInt64(s.buf[: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 *InstantDeltaDecompressor) 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 // } // func (s *InstantDeltaCompressor) Append(value float64) { // if s.pos == 0 { // // base value // n, _ := bin.PutVarInt64(s.buf[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[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) // } // } // } // } // }