package chunkenc import ( "fmt" "log" bin "gordenko.dev/dima/bin/little" "gordenko.dev/dima/pretty" "gordenko.dev/dima/qb" ) /* Payload data файла має таку структуру: vvvvvvvv-> <-ttttttt де v - це values, які додаються зліва направо, а читаються справа наліво; t - це timestamps, які додаються справа наліво, а читаються зліва направо. Timestamps читаються звичайними методами Get*, а писатись мають типу ByRightBound */ type ReverseTimeDeltaCompressor struct { buf []byte pos int lastUnixtime uint32 lastDelta uint32 lastDeltaSize int h byte state *TimeDeltaBound } func NewReverseTimeDeltaCompressor(buf []byte, size int) *ReverseTimeDeltaCompressor { s := &ReverseTimeDeltaCompressor{ buf: buf, pos: size, // перший вільний байт } // if size > 0 { // u64, n, err := bin.ReverseGetVarUint64(s.buf[: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[s.pos-1] // u64, s.lastDeltaSize, err = bin.ReverseGetVarUint64(s.buf[: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 } const hSize = 1 // retrun func (s *ReverseTimeDeltaCompressor) CalcRequiredSpace(unixtime uint32) (code int, size int) { if s.lastUnixtime > 0 { delta := unixtime - s.lastUnixtime if s.lastDelta > 0 { // 3rd value if s.h < 128 { // run if delta == s.lastDelta && s.h < 127 { code = incementRun } else { code = endOfSeries } } else { // literal if delta != s.lastDelta { if s.h < 255 { code = incementLiteral } else { code = endOfSeries } } else { code = startRun } } } else { code = add1stDelta } } else { code = addUnixtime } if code != addUnixtime { size = hSize if code != incementRun { size += bin.CountVarUint64(uint64(unixtime)) if code == endOfSeries { size += hSize } else if code == startRun && s.h > 128 { // need end of literal series size += hSize } } } else { size = 4 } return } const ( addUnixtime = 0 incementRun = 1 incementLiteral = 2 endOfSeries = 3 startRun = 4 add1stDelta = 5 ) func (s *ReverseTimeDeltaCompressor) Append(code int, unixtime uint32) { delta := unixtime - s.lastUnixtime switch code { case incementRun: s.h++ case incementLiteral: // // white s.lastDelta //s.lastDelta = delta s.h++ case endOfSeries: // write h - end of series s.lastDelta = delta s.h = 128 // new literal (length=1) case startRun: if s.h > 128 { // // write h - end literal series if length > 1 } // last literal convert to run (length=2) // write delta s.h = 0 // run, length=2 case add1stDelta: s.lastDelta = delta } // 1st value s.lastUnixtime = unixtime } // func (s *ReverseTimeDeltaCompressor) Append(unixtime uint32) { // if s.lastUnixtime > 0 { // delta := unixtime - s.lastUnixtime // if s.lastDelta > 0 { // // 3rd value // if s.h < 128 { // // run // if delta == s.lastDelta && s.h < 127 { // s.h++ // } else { // //write h - end of run // s.lastDelta = delta // s.h = 128 // new literal (length=1) // } // } else { // // literal // if delta == s.lastDelta { // if s.h > 128 { // // // write h - end literal series if length > 1 // } // // last literal convert to run (length=2) // // write delta // s.h = 0 // run, length=2 // } else { // // white s.lastDelta // if s.h < 255 { // //s.lastDelta = delta // s.h++ // } else { // // write h - end of literal // s.lastDelta = delta // s.h = 128 // new literal (length=1) // } // } // } // } else { // // 2nd value // s.lastDelta = delta // } // } // // 1st value // s.lastUnixtime = unixtime // } // func (s *ReverseTimeDeltaCompressor) convertLastFromLiteralToRun() { // // Зменшую кількість елементів в literal блоці // s.h-- // s.pos -= 1 + s.lastDeltaSize // s.buf[s.pos] = s.h // закриваю literal блок // s.pos++ // s.lastDeltaSize, _ = bin.ReversePutVarUint64(s.buf[s.pos:], uint64(s.lastDelta)) // s.pos += s.lastDeltaSize // s.h = 0 // run блок, довжини 2 // s.buf[s.pos] = s.h // s.pos++ // } // func (s *ReverseTimeDeltaCompressor) convertLiteralToRun() { // // Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії. // s.h = 0 // s.buf[s.pos-1] = s.h // } // func (s *ReverseTimeDeltaCompressor) appendDeltaToLiteral(delta uint32) { // s.h++ // збільшую к-сть дельт // s.lastDelta = delta // s.pos-- // s.lastDeltaSize, _ = bin.ReversePutVarUint64(s.buf[s.pos:], uint64(delta)) // s.pos += s.lastDeltaSize // s.buf[s.pos] = s.h // s.pos++ // } // func (s *ReverseTimeDeltaCompressor) appendNewLiteral(delta uint32) { // //fmt.Println("appendNewLiteral", delta) // s.h = flagLiteral // s.lastDelta = delta // s.lastDeltaSize, _ = bin.ReversePutVarUint64(s.buf[s.pos:], uint64(delta)) // s.pos += s.lastDeltaSize // s.buf[s.pos] = flagLiteral // literal, length = 1 // s.pos++ // } func (s *ReverseTimeDeltaCompressor) DeleteLast() { } func (s *ReverseTimeDeltaCompressor) Sync() { n, _ := bin.ReversePutVarUint64(s.buf[s.pos:], uint64(s.lastUnixtime)) s.pos += n } // FIX - check methods type TimeDeltaBound struct { Pos int H byte LastUnixtime uint32 LastDelta uint32 Chunks []byte } // 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 // } // delta h func (s *ReverseTimeDeltaCompressor) Lock() { if s.state != nil { qb.Abort(qb.RepeatableLock, nil) } // позиція посувається вліво, отже може перескочити на попередній chunk pos := s.pos - 1 - s.lastDeltaSize s.state = &TimeDeltaBound{ Pos: pos, H: s.h, LastUnixtime: s.lastUnixtime, // fix LastDelta: s.lastDelta, Chunks: s.buf[:s.pos], // fix check pos? } } // fix - повернути в Pool буфери func (s *ReverseTimeDeltaCompressor) Unlock() { s.state = nil } func (s *ReverseTimeDeltaCompressor) Offset() int { if s.state != nil { return s.state.Pos } return 0 } // fix - func (s *ReverseTimeDeltaCompressor) Snapshot() ([]byte, int) { // if s.state == nil { // return s.buf, s.Size() // } // // ВАЖЛИВО! // // Треба відтворити стан останнього чанка // 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.ReversePutVarUint64(pos, uint64(s.state.LastDelta)) // buf.SetByte(pos, s.state.H) // pos++ // return chunks, pos return nil, 0 } func (s *ReverseTimeDeltaCompressor) CreateDecompressor() qb.TimestampDecompressor { if s.state == nil { d := NewReverseTimeDeltaDecompressor(s.buf, s.Size()) d.RestoreFromEnd() return d } d := NewReverseTimeDeltaDecompressor(s.buf, s.Size()) d.RestoreFromBound(*s.state) return d } func (s *ReverseTimeDeltaCompressor) Renew() { // УВАГА! // state не чіпаємо s.buf = make([]byte, minBufferSize) s.pos = 0 // //s.baseValue = 0 s.lastDelta = 0 s.lastDeltaSize = 0 s.h = 0 } func (s *ReverseTimeDeltaCompressor) Chunks() []byte { return s.buf } // DECOMPRESSOR type ReverseTimeDeltaDecompressor struct { buf []byte pos int lastDelta uint32 lastUnixtime uint32 isRegular bool isRun bool pending int done bool } func NewReverseTimeDeltaDecompressor(buf []byte, 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 := bin.ReverseGetVarUint64(s.buf[: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[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 := bin.ReverseGetVarUint64(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.lastDelta = uint32(u64) }