diff --git a/chunkenc/chunkenc_test.go b/chunkenc/chunkenc_test.go index de78d6d..a5b1c0d 100644 --- a/chunkenc/chunkenc_test.go +++ b/chunkenc/chunkenc_test.go @@ -1,7 +1,9 @@ package chunkenc import ( + "bytes" "fmt" + "slices" "testing" ) @@ -12,7 +14,7 @@ func TestCumdelta(t *testing.T) { value float64 done bool ) - c := NewReverseCumulativeDeltaCompressor(buf, 0, fracDigits) + c := NewCumulativeDeltaCompressor(buf, 0, fracDigits) // c.Append(1.55) // c.Append(23) // c.Append(23) @@ -28,7 +30,7 @@ func TestCumdelta(t *testing.T) { //fmt.Printf("pos: %d\n", c.pos) //fmt.Printf("%d\n", buf.Chunks()[0]) - d := NewReverseCumulativeDeltaDecompressor(buf, c.Size(), fracDigits) + d := NewCumulativeDeltaDecompressor(buf, c.Size(), fracDigits) for range 8 { value, done = d.NextValue() @@ -43,7 +45,7 @@ func TestInsdelta(t *testing.T) { value float64 done bool ) - c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits) + c := NewInstantDeltaCompressor(buf, 0, fracDigits) c.Append(-1.55) c.Append(23) @@ -58,7 +60,7 @@ func TestInsdelta(t *testing.T) { //fmt.Printf("pos: %d\n", c.pos) //fmt.Printf("%d\n", buf.Chunks()[0]) - d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits) + d := NewInstantDeltaDecompressor(buf, c.Size(), fracDigits) for range 8 { value, done = d.NextValue() @@ -66,55 +68,366 @@ func TestInsdelta(t *testing.T) { } } -func TestTimeDelta(t *testing.T) { +func TestTimeDeltaCompressor(t *testing.T) { var ( - buf = make([]byte, minBufferSize) - value uint32 - done bool + testCases = []struct { + Nums []uint32 + RepeatLastDeltaNTimes int + Code int + RequiredSize int + Buf []byte + LastUnixtime uint32 + LastDelta uint32 + H byte + }{ + { + Nums: []uint32{ + 1780777000, + }, + Code: addUnixtime, + RequiredSize: 4, + Buf: []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + }, + LastUnixtime: 1780777000, + LastDelta: 0, + H: 0, + }, + { + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + }, + Code: add1stDelta, + RequiredSize: 6, + Buf: []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + }, + LastUnixtime: 1780777060, + LastDelta: 60, + H: 128, + }, + { + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777120, // +60 + }, + Code: startRun, // literal changed by run + RequiredSize: 6, + Buf: []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + }, + LastUnixtime: 1780777120, + LastDelta: 60, + H: 0, // run, length = 2 + }, + { + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777130, // +70 + 1780777200, // +70 + }, + Code: startRun, // literal decreased by 1 + RequiredSize: 7, // unixtime, end of literal, new delta, new h + Buf: []byte{ + 0, 0, 0, 0, 0, 0, 128, 188, + }, + LastUnixtime: 1780777200, + LastDelta: 70, + H: 0, // run, length = 2 + }, + { + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777120, // +60 + 1780777180, // +60 + }, + Code: incrementRun, + RequiredSize: 5, + Buf: []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + }, + LastUnixtime: 1780777180, + LastDelta: 60, + H: 1, // run, length = 3 + }, + { + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777120, // +60 + 1780777180, // +60 + 1780777200, // +20 + }, + Code: endSeries, // run + RequiredSize: 8, + Buf: []byte{ + 0, 0, 0, 0, 0, 0, + 1, // h of run, length = 3 + 188, // varUint64(60) + }, + LastUnixtime: 1780777200, + LastDelta: 20, + H: 128, // literal, length = 1 + }, + { + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + }, + RepeatLastDeltaNTimes: 128, + Code: incrementRun, // h byte full filled + RequiredSize: 5, + Buf: []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, + }, + LastUnixtime: 1780777060 + 128*60, + LastDelta: 60, + H: 127, // run, length = 129 + }, + { + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + }, + RepeatLastDeltaNTimes: 129, + Code: endSeries, // run, h byte overflowed + RequiredSize: 8, + Buf: []byte{ + 0, 0, 0, 0, 0, 0, + 127, // h - end of run (length = 129) + 188, // varUint64(60) + }, + LastUnixtime: 1780777060 + 129*60, + LastDelta: 60, + H: 128, // literal, length = 1 + }, + { + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777122, // +62 + }, + Code: incrementLiteral, + RequiredSize: 7, + Buf: []byte{ + 0, 0, 0, 0, 0, 0, 0, + 188, // varUint64(60) + }, + LastUnixtime: 1780777122, + LastDelta: 62, + H: 129, // literal, length = 2 + }, + } ) - c := NewReverseTimeDeltaCompressor(buf, 0) - c.Append(10) - //c.Append(131) - - //fmt.Printf("pos: %d\n", c.pos) - //fmt.Printf("%d\n", buf.Chunks()[0]) - - c.Append(70) - - c.Append(130) - c.Append(191) - - c.Append(248) - c.Append(305) - c.Append(330) - c.Append(390) - c.Append(450) - - // fmt.Printf("pos: %d\n", c.pos) - // fmt.Printf("%d\n", buf.Chunks()[0]) - - //c.Sync() - - // bound := c.GetState() - - // fmt.Println("AFTER SYNC") - // fmt.Printf("pos: %d\n", c.pos) - // chunks := buf.Chunks() - // if len(chunks) > 0 { - // fmt.Printf("%d\n", chunks[0]) - // } - - d := NewReverseTimeDeltaDecompressor(buf, c.Size()) - // d.RestoreFromBound(bound) - //d.RestoreFromEnd() - - for range 12 { - value, done = d.NextValue() - fmt.Println(value, done) + for caseIdx, testCase := range testCases { + //fmt.Println("----------") + var ( + buf = make([]byte, 8) + code int + requiredSize int + ) + c := NewTimeDeltaCompressor(buf, 0) + for _, num := range testCase.Nums { + code, requiredSize = c.CalcRequiredSpace(num) + c.Append(code, num) + } + if testCase.RepeatLastDeltaNTimes > 0 { + for range testCase.RepeatLastDeltaNTimes { + num := c.lastUnixtime + c.lastDelta + code, requiredSize = c.CalcRequiredSpace(num) + c.Append(code, num) + } + } + if code != testCase.Code { + t.Fatalf("%d: got code %d are not equal expected %d", + caseIdx, code, testCase.Code) + } + if requiredSize != testCase.RequiredSize { + t.Fatalf("%d: got requiredSize %d are not equal expected %d", + caseIdx, requiredSize, testCase.RequiredSize) + } + if !bytes.Equal(buf, testCase.Buf) { + t.Fatalf("%d: got buf %v are not equal expected %v", + caseIdx, buf, testCase.Buf) + } + if c.lastUnixtime != testCase.LastUnixtime { + t.Fatalf("%d: got lastUnixtime %d are not equal expected %d", + caseIdx, c.lastUnixtime, testCase.LastUnixtime) + } + if c.lastDelta != testCase.LastDelta { + t.Fatalf("%d: got lastDelta %d are not equal expected %d", + caseIdx, c.lastDelta, testCase.LastDelta) + } + if c.h != testCase.H { + t.Fatalf("%d: got h %d are not equal expected %d", + caseIdx, c.h, testCase.H) + } } } +func TestTimeDeltaDecompressorFromCapturedState(t *testing.T) { + var ( + testCases = []struct { + Nums []uint32 + //RepeatLastDeltaNTimes int + }{ + { + Nums: []uint32{ + 1780777000, + }, + }, + { + // add1stDelta + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + }, + }, + { + // startRun + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777120, // +60 + }, + }, + { + // incrementRun + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777120, // +60 + 1780777180, // +60 + }, + }, + { + // endRun + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777120, // +60 + 1780777180, // +60 + 1780777200, // +20 + }, + }, + { + // incrementLiteral + Nums: []uint32{ + 1780777000, + 1780777060, // +60 + 1780777122, // +62 + }, + }, + { + // ... + Nums: []uint32{ + 1780777000, + 1780777010, // +10 + 1780777120, // +10 + 1780777130, // +10 + 1780777135, // +5 + 1780777141, // +6 + 1780777148, // +7 + 1780777156, // +8 + 1780777165, // +9 + 1780777174, // +9 + 1780777183, // +9 + 1780777190, // +7 + }, + }, + } + ) + for caseIdx, testCase := range testCases { + var ( + buf = make([]byte, 16) + decodedNums []uint32 + ) + c := NewTimeDeltaCompressor(buf, 0) + for _, num := range testCase.Nums { + code, _ := c.CalcRequiredSpace(num) + c.Append(code, num) + } + + // + c.CaptureState() + + d := c.CreateDecompressor() + + //fmt.Println(testCase.Nums) + // fmt.Println("---------------") + // fmt.Println(buf) + + for { + num, done := d.NextValue() + //fmt.Println(num, done) + if done { + break + } + decodedNums = append(decodedNums, num) + } + + slices.Reverse(decodedNums) + + if !slices.Equal(testCase.Nums, decodedNums) { + t.Fatalf("%d: got nums %v not equal expected %v", caseIdx, decodedNums, testCase.Nums) + } + } +} + +// func TestTimeDelta(t *testing.T) { +// var ( +// buf = make([]byte, minBufferSize) +// value uint32 +// done bool +// ) +// c := NewReverseTimeDeltaCompressor(buf, 0) +// c.Append(10) + +// //c.Append(131) + +// //fmt.Printf("pos: %d\n", c.pos) +// //fmt.Printf("%d\n", buf.Chunks()[0]) + +// c.Append(70) + +// c.Append(130) +// c.Append(191) + +// c.Append(248) +// c.Append(305) +// c.Append(330) +// c.Append(390) +// c.Append(450) + +// // fmt.Printf("pos: %d\n", c.pos) +// // fmt.Printf("%d\n", buf.Chunks()[0]) + +// //c.Sync() + +// // bound := c.GetState() + +// // fmt.Println("AFTER SYNC") +// // fmt.Printf("pos: %d\n", c.pos) +// // chunks := buf.Chunks() +// // if len(chunks) > 0 { +// // fmt.Printf("%d\n", chunks[0]) +// // } + +// d := NewReverseTimeDeltaDecompressor(buf, c.Size()) +// // d.RestoreFromBound(bound) +// //d.RestoreFromEnd() + +// for range 12 { +// value, done = d.NextValue() +// fmt.Println(value, done) +// } +// } + func TestCumdeltaBound(t *testing.T) { var ( fracDigits byte = 0 @@ -122,7 +435,7 @@ func TestCumdeltaBound(t *testing.T) { value float64 done bool ) - c := NewReverseCumulativeDeltaCompressor(buf, 0, fracDigits) + c := NewCumulativeDeltaCompressor(buf, 0, fracDigits) // c.Append(1.55) // c.Append(23) // c.Append(23) @@ -157,7 +470,7 @@ func TestCumdeltaBound(t *testing.T) { fmt.Printf("pos: %d\n", c.pos) fmt.Printf("%d\n", c.Chunks()) - d := NewReverseCumulativeDeltaDecompressor(buf, c.Size(), fracDigits) + d := NewCumulativeDeltaDecompressor(buf, c.Size(), fracDigits) d.RestoreFromEnd() for range 8 { @@ -183,7 +496,7 @@ func TestInsdeltaBound(t *testing.T) { value float64 done bool ) - c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits) + c := NewInstantDeltaCompressor(buf, 0, fracDigits) // c.Append(1.55) // c.Append(23) // c.Append(23) @@ -218,7 +531,7 @@ func TestInsdeltaBound(t *testing.T) { fmt.Printf("pos: %d\n", c.pos) fmt.Printf("%d\n", c.Chunks()) - d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits) + d := NewInstantDeltaDecompressor(buf, c.Size(), fracDigits) d.RestoreFromEnd() for range 8 { @@ -226,7 +539,7 @@ func TestInsdeltaBound(t *testing.T) { fmt.Println(value, done) } - boundDecompressor := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits) + boundDecompressor := NewInstantDeltaDecompressor(buf, c.Size(), fracDigits) //boundDecompressor.RestoreFromBound(bound) fmt.Println("from bound:") @@ -243,7 +556,7 @@ func TestInsdeltaBound2(t *testing.T) { value float64 done bool ) - c := NewReverseInstantDeltaCompressor(buf, 0, fracDigits) + c := NewInstantDeltaCompressor(buf, 0, fracDigits) // c.Append(1.55) // c.Append(23) // c.Append(23) @@ -278,7 +591,7 @@ func TestInsdeltaBound2(t *testing.T) { fmt.Printf("pos: %d\n", c.pos) fmt.Printf("%d\n", c.Chunks()) - d := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits) + d := NewInstantDeltaDecompressor(buf, c.Size(), fracDigits) d.RestoreFromEnd() for range 8 { @@ -286,7 +599,7 @@ func TestInsdeltaBound2(t *testing.T) { fmt.Println(value, done) } - boundDecompressor := NewReverseInstantDeltaDecompressor(buf, c.Size(), fracDigits) + boundDecompressor := NewInstantDeltaDecompressor(buf, c.Size(), fracDigits) //sboundDecompressor.RestoreFromBound(bound) fmt.Println("from bound:") diff --git a/chunkenc/cumdelta.go b/chunkenc/cumdelta.go index 7d517d3..7b90a62 100644 --- a/chunkenc/cumdelta.go +++ b/chunkenc/cumdelta.go @@ -37,7 +37,7 @@ v1 v2 v3 h-byte(literal, 3) <- v3 v1 v2 h-byte(literal, 2) v3 h-byte(run, 2) */ -type ReverseCumulativeDeltaCompressor struct { +type CumulativeDeltaCompressor struct { buf []byte coef float64 pos int @@ -48,12 +48,12 @@ type ReverseCumulativeDeltaCompressor struct { state *CumulativeDeltaBound } -func NewReverseCumulativeDeltaCompressor(buf []byte, size int, fracDigits byte) *ReverseCumulativeDeltaCompressor { +func NewCumulativeDeltaCompressor(buf []byte, size int, fracDigits byte) *CumulativeDeltaCompressor { var coef float64 = 1 if fracDigits > 0 { coef = math.Pow(10, float64(fracDigits)) } - s := &ReverseCumulativeDeltaCompressor{ + s := &CumulativeDeltaCompressor{ buf: buf, pos: size, // перший вільний байт coef: coef, @@ -73,15 +73,15 @@ func NewReverseCumulativeDeltaCompressor(buf []byte, size int, fracDigits byte) return s } -func (s *ReverseCumulativeDeltaCompressor) Size() int { +func (s *CumulativeDeltaCompressor) Size() int { return s.pos } -func (s *ReverseCumulativeDeltaCompressor) CalcRequiredSpace(value float64) int { +func (s *CumulativeDeltaCompressor) CalcRequiredSpace(value float64) int { return 0 } -func (s *ReverseCumulativeDeltaCompressor) Append(value float64) { +func (s *CumulativeDeltaCompressor) Append(value float64) { if s.pos == 0 { // base value n, _ := bin.PutVarUint64(s.buf[s.pos:], uint64(value*s.coef)) @@ -131,7 +131,7 @@ func (s *ReverseCumulativeDeltaCompressor) Append(value float64) { } } -func (s *ReverseCumulativeDeltaCompressor) convertLastFromLiteralToRun() { +func (s *CumulativeDeltaCompressor) convertLastFromLiteralToRun() { // Зменшую кількість елементів в literal блоці s.h-- s.pos -= 1 + s.lastDeltaSize @@ -144,13 +144,13 @@ func (s *ReverseCumulativeDeltaCompressor) convertLastFromLiteralToRun() { s.pos++ } -func (s *ReverseCumulativeDeltaCompressor) convertLiteralToRun() { +func (s *CumulativeDeltaCompressor) convertLiteralToRun() { // Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії. s.h = 0 s.buf[s.pos-1] = s.h } -func (s *ReverseCumulativeDeltaCompressor) appendDeltaToLiteral(delta uint64) { +func (s *CumulativeDeltaCompressor) appendDeltaToLiteral(delta uint64) { s.h++ // збільшую к-сть дельт s.lastDelta = delta s.pos-- @@ -160,7 +160,7 @@ func (s *ReverseCumulativeDeltaCompressor) appendDeltaToLiteral(delta uint64) { s.pos++ } -func (s *ReverseCumulativeDeltaCompressor) appendNewLiteral(delta uint64) { +func (s *CumulativeDeltaCompressor) appendNewLiteral(delta uint64) { s.h = flagLiteral s.lastDelta = delta s.lastDeltaSize, _ = bin.ReversePutVarUint64(s.buf[s.pos:], delta) @@ -169,7 +169,7 @@ func (s *ReverseCumulativeDeltaCompressor) appendNewLiteral(delta uint64) { s.pos++ } -func (s *ReverseCumulativeDeltaCompressor) DeleteLast() { +func (s *CumulativeDeltaCompressor) DeleteLast() { } @@ -181,7 +181,7 @@ type CumulativeDeltaBound struct { } // delta h -func (s *ReverseCumulativeDeltaCompressor) Lock() { +func (s *CumulativeDeltaCompressor) Lock() { if s.state != nil { qb.Abort(qb.RepeatableLock, nil) } @@ -196,18 +196,18 @@ func (s *ReverseCumulativeDeltaCompressor) Lock() { } // fix - повернути в Pool буфери -func (s *ReverseCumulativeDeltaCompressor) Unlock() { +func (s *CumulativeDeltaCompressor) Unlock() { s.state = nil } -func (s *ReverseCumulativeDeltaCompressor) Offset() int { +func (s *CumulativeDeltaCompressor) Offset() int { if s.state != nil { return s.state.Pos } return 0 } -func (s *ReverseCumulativeDeltaCompressor) Snapshot() ([]byte, int) { +func (s *CumulativeDeltaCompressor) Snapshot() ([]byte, int) { // if s.state == nil { // return s.buf, s.Size() // } @@ -230,18 +230,18 @@ func (s *ReverseCumulativeDeltaCompressor) Snapshot() ([]byte, int) { return nil, 0 } -func (s *ReverseCumulativeDeltaCompressor) CreateDecompressor(fracDigits byte) qb.ValueDecompressor { +func (s *CumulativeDeltaCompressor) CreateDecompressor(fracDigits byte) qb.ValueDecompressor { if s.state == nil { - d := NewReverseCumulativeDeltaDecompressor(s.buf, s.Size(), fracDigits) + d := NewCumulativeDeltaDecompressor(s.buf, s.Size(), fracDigits) d.RestoreFromEnd() return d } - d := NewReverseCumulativeDeltaDecompressor(s.buf, s.Size(), fracDigits) + d := NewCumulativeDeltaDecompressor(s.buf, s.Size(), fracDigits) d.RestoreFromBound(*s.state) return d } -func (s *ReverseCumulativeDeltaCompressor) Renew() { +func (s *CumulativeDeltaCompressor) Renew() { // УВАГА! // state не чіпаємо s.buf = make([]byte, minBufferSize) @@ -253,13 +253,13 @@ func (s *ReverseCumulativeDeltaCompressor) Renew() { s.h = 0 } -func (s *ReverseCumulativeDeltaCompressor) Chunks() []byte { +func (s *CumulativeDeltaCompressor) Chunks() []byte { return s.buf } // DECOMPRESSOR -type ReverseCumulativeDeltaDecompressor struct { +type CumulativeDeltaDecompressor struct { buf []byte coef float64 pos int @@ -271,7 +271,7 @@ type ReverseCumulativeDeltaDecompressor struct { done bool } -func NewReverseCumulativeDeltaDecompressor(buf []byte, size int, fracDigits byte) *ReverseCumulativeDeltaDecompressor { +func NewCumulativeDeltaDecompressor(buf []byte, size int, fracDigits byte) *CumulativeDeltaDecompressor { var coef float64 = 1 if fracDigits > 0 { coef = math.Pow(10, float64(fracDigits)) @@ -281,7 +281,7 @@ func NewReverseCumulativeDeltaDecompressor(buf []byte, size int, fracDigits byte log.Fatalf("bug: get base value: %s", err) } //fmt.Printf("baseValue: %.2f, bound: %d, pos: %d\n", float64(u64)/coef, n, size) - return &ReverseCumulativeDeltaDecompressor{ + return &CumulativeDeltaDecompressor{ buf: buf, coef: coef, pos: size - 1, // last elem @@ -290,7 +290,7 @@ func NewReverseCumulativeDeltaDecompressor(buf []byte, size int, fracDigits byte } } -func (s *ReverseCumulativeDeltaDecompressor) RestoreFromEnd() { +func (s *CumulativeDeltaDecompressor) RestoreFromEnd() { if s.pos > s.bound { // читаю заголовок наступної серії s.readHeader() @@ -300,14 +300,14 @@ func (s *ReverseCumulativeDeltaDecompressor) RestoreFromEnd() { } } -func (s *ReverseCumulativeDeltaDecompressor) RestoreFromBound(bound CumulativeDeltaBound) { +func (s *CumulativeDeltaDecompressor) RestoreFromBound(bound CumulativeDeltaBound) { 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 *ReverseCumulativeDeltaDecompressor) NextValue() (value float64, done bool) { +func (s *CumulativeDeltaDecompressor) 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 @@ -332,7 +332,7 @@ func (s *ReverseCumulativeDeltaDecompressor) NextValue() (value float64, done bo return value, false } -func (s *ReverseCumulativeDeltaDecompressor) readHeader() { +func (s *CumulativeDeltaDecompressor) readHeader() { h := s.buf[s.pos] s.pos-- s.decodeHeaderByte(h) @@ -340,7 +340,7 @@ func (s *ReverseCumulativeDeltaDecompressor) readHeader() { // fmt.Println("isRun:", s.isRun) // fmt.Println("pending:", s.pending) } -func (s *ReverseCumulativeDeltaDecompressor) readValue() { +func (s *CumulativeDeltaDecompressor) readValue() { u64, n, err := bin.ReverseGetVarUint64(s.buf[:s.pos]) if err != nil { log.Fatalln(err) @@ -354,7 +354,7 @@ func (s *ReverseCumulativeDeltaDecompressor) readValue() { s.lastValue = s.baseValue + float64(u64)/s.coef } -func (s *ReverseCumulativeDeltaDecompressor) decodeHeaderByte(h byte) { +func (s *CumulativeDeltaDecompressor) decodeHeaderByte(h byte) { s.isRun = h < 128 if s.isRun { s.pending = int(h&127) + 2 diff --git a/chunkenc/insdelta.go b/chunkenc/insdelta.go index 1f24445..35c9336 100644 --- a/chunkenc/insdelta.go +++ b/chunkenc/insdelta.go @@ -9,7 +9,7 @@ import ( "gordenko.dev/dima/qb/bin" ) -type ReverseInstantDeltaCompressor struct { +type InstantDeltaCompressor struct { buf []byte coef float64 pos int @@ -20,12 +20,12 @@ type ReverseInstantDeltaCompressor struct { state *InstantDeltaBound } -func NewReverseInstantDeltaCompressor(buf []byte, size int, fracDigits byte) *ReverseInstantDeltaCompressor { +func NewInstantDeltaCompressor(buf []byte, size int, fracDigits byte) *InstantDeltaCompressor { var coef float64 = 1 if fracDigits > 0 { coef = math.Pow(10, float64(fracDigits)) } - s := &ReverseInstantDeltaCompressor{ + s := &InstantDeltaCompressor{ buf: buf, pos: size, coef: coef, @@ -45,11 +45,11 @@ func NewReverseInstantDeltaCompressor(buf []byte, size int, fracDigits byte) *Re return s } -func (s *ReverseInstantDeltaCompressor) Size() int { +func (s *InstantDeltaCompressor) Size() int { return s.pos } -func (s *ReverseInstantDeltaCompressor) Append(value float64) { +func (s *InstantDeltaCompressor) Append(value float64) { if s.pos == 0 { // base value n, _ := bin.PutVarInt64(s.buf[s.pos:], int64(value*s.coef)) @@ -105,7 +105,7 @@ func (s *ReverseInstantDeltaCompressor) Append(value float64) { } } -func (s *ReverseInstantDeltaCompressor) convertLastFromLiteralToRun() { +func (s *InstantDeltaCompressor) convertLastFromLiteralToRun() { // Зменшую кількість елементів в literal блоці s.h-- s.pos -= 1 + s.lastDeltaSize @@ -118,13 +118,13 @@ func (s *ReverseInstantDeltaCompressor) convertLastFromLiteralToRun() { s.pos++ } -func (s *ReverseInstantDeltaCompressor) convertLiteralToRun() { +func (s *InstantDeltaCompressor) convertLiteralToRun() { // Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії. s.h = 0 s.buf[s.pos-1] = s.h } -func (s *ReverseInstantDeltaCompressor) appendDeltaToLiteral(delta int64) { +func (s *InstantDeltaCompressor) appendDeltaToLiteral(delta int64) { s.h++ // збільшую к-сть дельт s.lastDelta = delta s.pos-- @@ -134,7 +134,7 @@ func (s *ReverseInstantDeltaCompressor) appendDeltaToLiteral(delta int64) { s.pos++ } -func (s *ReverseInstantDeltaCompressor) appendNewLiteral(delta int64) { +func (s *InstantDeltaCompressor) appendNewLiteral(delta int64) { s.h = flagLiteral s.lastDelta = delta s.lastDeltaSize, _ = bin.ReversePutVarInt64(s.buf[s.pos:], delta) @@ -143,7 +143,7 @@ func (s *ReverseInstantDeltaCompressor) appendNewLiteral(delta int64) { s.pos++ } -func (s *ReverseInstantDeltaCompressor) DeleteLast() {} +func (s *InstantDeltaCompressor) DeleteLast() {} type InstantDeltaBound struct { Pos int @@ -153,7 +153,7 @@ type InstantDeltaBound struct { } // delta h -func (s *ReverseInstantDeltaCompressor) Lock() { +func (s *InstantDeltaCompressor) Lock() { if s.state != nil { qb.Abort(qb.RepeatableLock, nil) } @@ -168,18 +168,18 @@ func (s *ReverseInstantDeltaCompressor) Lock() { } // fix - повернути в Pool буфери -func (s *ReverseInstantDeltaCompressor) Unlock() { +func (s *InstantDeltaCompressor) Unlock() { s.state = nil } -func (s *ReverseInstantDeltaCompressor) Offset() int { +func (s *InstantDeltaCompressor) Offset() int { if s.state != nil { return s.state.Pos } return 0 } -func (s *ReverseInstantDeltaCompressor) Snapshot() ([]byte, int) { +func (s *InstantDeltaCompressor) Snapshot() ([]byte, int) { // if s.state == nil { // return s.buf, s.Size() // } @@ -202,18 +202,18 @@ func (s *ReverseInstantDeltaCompressor) Snapshot() ([]byte, int) { return nil, 0 } -func (s *ReverseInstantDeltaCompressor) CreateDecompressor(fracDigits byte) qb.ValueDecompressor { +func (s *InstantDeltaCompressor) CreateDecompressor(fracDigits byte) qb.ValueDecompressor { if s.state == nil { - d := NewReverseInstantDeltaDecompressor(s.buf, s.Size(), fracDigits) + d := NewInstantDeltaDecompressor(s.buf, s.Size(), fracDigits) d.RestoreFromEnd() return d } - d := NewReverseInstantDeltaDecompressor(s.buf, s.Size(), fracDigits) + d := NewInstantDeltaDecompressor(s.buf, s.Size(), fracDigits) d.RestoreFromBound(*s.state) return d } -func (s *ReverseInstantDeltaCompressor) Renew() { +func (s *InstantDeltaCompressor) Renew() { // УВАГА! // state не чіпаємо s.buf = make([]byte, minBufferSize) @@ -225,17 +225,17 @@ func (s *ReverseInstantDeltaCompressor) Renew() { s.h = 0 } -func (s *ReverseInstantDeltaCompressor) CalcRequiredSpace(value float64) int { +func (s *InstantDeltaCompressor) CalcRequiredSpace(value float64) int { return 0 } -func (s *ReverseInstantDeltaCompressor) Chunks() []byte { +func (s *InstantDeltaCompressor) Chunks() []byte { return s.buf } // DECOMPRESSOR -type ReverseInstantDeltaDecompressor struct { +type InstantDeltaDecompressor struct { buf []byte coef float64 pos int @@ -247,7 +247,7 @@ type ReverseInstantDeltaDecompressor struct { done bool } -func NewReverseInstantDeltaDecompressor(buf []byte, size int, fracDigits byte) *ReverseInstantDeltaDecompressor { +func NewInstantDeltaDecompressor(buf []byte, size int, fracDigits byte) *InstantDeltaDecompressor { var coef float64 = 1 if fracDigits > 0 { coef = math.Pow(10, float64(fracDigits)) @@ -257,7 +257,7 @@ func NewReverseInstantDeltaDecompressor(buf []byte, size int, fracDigits byte) * log.Fatalf("bug: get base value: %s", err) } //fmt.Printf("baseValue: %.2f, bound: %d, pos: %d\n", float64(u64)/coef, n, size) - return &ReverseInstantDeltaDecompressor{ + return &InstantDeltaDecompressor{ buf: buf, coef: coef, pos: size - 1, // last elem @@ -266,7 +266,7 @@ func NewReverseInstantDeltaDecompressor(buf []byte, size int, fracDigits byte) * } } -func (s *ReverseInstantDeltaDecompressor) RestoreFromEnd() { +func (s *InstantDeltaDecompressor) RestoreFromEnd() { if s.pos > s.bound { // читаю заголовок наступної серії s.readHeader() @@ -276,14 +276,14 @@ func (s *ReverseInstantDeltaDecompressor) RestoreFromEnd() { } } -func (s *ReverseInstantDeltaDecompressor) RestoreFromBound(bound InstantDeltaBound) { +func (s *InstantDeltaDecompressor) 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) { +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 @@ -308,7 +308,7 @@ func (s *ReverseInstantDeltaDecompressor) NextValue() (value float64, done bool) return value, false } -func (s *ReverseInstantDeltaDecompressor) readHeader() { +func (s *InstantDeltaDecompressor) readHeader() { h := s.buf[s.pos] s.pos-- s.decodeHeaderByte(h) @@ -316,7 +316,7 @@ func (s *ReverseInstantDeltaDecompressor) readHeader() { // fmt.Println("isRun:", s.isRun) // fmt.Println("pending:", s.pending) } -func (s *ReverseInstantDeltaDecompressor) readValue() { +func (s *InstantDeltaDecompressor) readValue() { i64, n, err := bin.ReverseGetVarInt64(s.buf[:s.pos]) if err != nil { log.Fatalln(err) @@ -330,7 +330,7 @@ func (s *ReverseInstantDeltaDecompressor) readValue() { s.lastValue = s.baseValue + float64(i64)/s.coef } -func (s *ReverseInstantDeltaDecompressor) decodeHeaderByte(h byte) { +func (s *InstantDeltaDecompressor) decodeHeaderByte(h byte) { s.isRun = h < 128 if s.isRun { s.pending = int(h&127) + 2 diff --git a/chunkenc/time_delta.go b/chunkenc/time_delta.go index 636644e..088f4cb 100644 --- a/chunkenc/time_delta.go +++ b/chunkenc/time_delta.go @@ -1,11 +1,9 @@ package chunkenc import ( - "fmt" "log" bin "gordenko.dev/dima/bin/little" - "gordenko.dev/dima/pretty" "gordenko.dev/dima/qb" ) @@ -19,48 +17,63 @@ Timestamps читаються звичайними методами Get*, а п */ -type ReverseTimeDeltaCompressor struct { - buf []byte - pos int - lastUnixtime uint32 - lastDelta uint32 - lastDeltaSize int - h byte - state *TimeDeltaBound +const ( + hSize = 1 + + // append scripts + addUnixtime = 0 + incrementRun = 1 + incrementLiteral = 2 + endSeries = 3 + startRun = 5 + add1stDelta = 6 +) + +type TimeDeltaCompressor struct { + buf []byte + // останній записаний байт, якщо рахувати справа наліво + pos int + lastUnixtime uint32 + lastDelta uint32 + h byte + state *TimeDeltaCapturedState } -func NewReverseTimeDeltaCompressor(buf []byte, size int) *ReverseTimeDeltaCompressor { - s := &ReverseTimeDeltaCompressor{ +// Кодує значення справа наліво футнкціями TailPut*. Читає значення зліва направо функціями Get* +func NewTimeDeltaCompressor(buf []byte, size int) *TimeDeltaCompressor { + s := &TimeDeltaCompressor{ buf: buf, - pos: size, // перший вільний байт + pos: len(buf) - size, + } + if size > 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.h = s.buf[s.pos] + s.pos++ + u64, n, err := bin.GetVarUint64(s.buf[s.pos:]) + if err != nil { + log.Fatalf("bug: get last delta: %s", err) + } + s.lastDelta = uint32(u64) + s.pos += n + } } - // 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 +func (s *TimeDeltaCompressor) Size() int { + return len(s.buf) - s.pos } -const hSize = 1 - // retrun -func (s *ReverseTimeDeltaCompressor) CalcRequiredSpace(unixtime uint32) (code int, size int) { +func (s *TimeDeltaCompressor) CalcRequiredSpace(unixtime uint32) (code int, size int) { + //fmt.Println(unixtime) + size = 4 // last unixtime if s.lastUnixtime > 0 { delta := unixtime - s.lastUnixtime if s.lastDelta > 0 { @@ -68,81 +81,286 @@ func (s *ReverseTimeDeltaCompressor) CalcRequiredSpace(unixtime uint32) (code in if s.h < 128 { // run if delta == s.lastDelta && s.h < 127 { - code = incementRun + code = incrementRun + size += hSize } else { - code = endOfSeries + code = endSeries + size += bin.CountVarUint64(uint64(s.lastDelta)) + // previous delta + hSize + // h - end of run + bin.CountVarUint64(uint64(delta)) + // new literal + hSize // new h } } else { // literal if delta != s.lastDelta { if s.h < 255 { - code = incementLiteral + code = incrementLiteral + size += bin.CountVarUint64(uint64(s.lastDelta)) + // previous delta + bin.CountVarUint64(uint64(delta)) + // new delta + hSize } else { - code = endOfSeries + code = endSeries + size += bin.CountVarUint64(uint64(s.lastDelta)) + // previous delta + hSize + // h - end of literal + bin.CountVarUint64(uint64(delta)) + // new literal + hSize // new h } } else { code = startRun + if s.h > 128 { + size += hSize // h - end of literal + } + size += bin.CountVarUint64(uint64(delta)) + // new delta + hSize // new h } } } else { code = add1stDelta + size += bin.CountVarUint64(uint64(delta)) + // new literal + hSize // new h } } else { code = addUnixtime } + return +} - 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 - } +func (s *TimeDeltaCompressor) Append(code int, unixtime uint32) { + delta := unixtime - s.lastUnixtime + switch code { + case incrementRun: + 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)) + s.pos -= n + // write h - end of run + s.pos-- + s.buf[s.pos] = s.h + // 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.pos-- + s.buf[s.pos] = s.h } + // start new run (length=2) + s.h = 0 + case add1stDelta: + // start new literal (length=1) + s.lastDelta = delta + s.h = 128 + } + // 1st value + s.lastUnixtime = unixtime +} + +func (s *TimeDeltaCompressor) DeleteLast() { + +} + +func (s *TimeDeltaCompressor) Sync() { + //n, _ := bin.ReversePutVarUint64(s.buf[s.pos:], uint64(s.lastUnixtime)) + //s.pos += n +} + +// FIX - check methods + +type TimeDeltaCapturedState struct { + H byte + LastUnixtime uint32 + LastDelta uint32 + Buf []byte +} + +// delta h +func (s *TimeDeltaCompressor) CaptureState() { + if s.state != nil { + qb.Abort(qb.RepeatableLock, nil) + } + // позиція посувається вліво, отже може перескочити на попередній chunk + s.state = &TimeDeltaCapturedState{ + H: s.h, + LastUnixtime: s.lastUnixtime, + LastDelta: s.lastDelta, + Buf: s.buf[s.pos:], + } +} + +// fix - повернути в Pool буфери +func (s *TimeDeltaCompressor) ForgetCapturedState() { + s.state = nil +} + +// fix +func (s *TimeDeltaCompressor) Offset() int { + // if s.state != nil { + // return s.state.Pos + // } + return 0 +} + +// Snapshot - для створення снапшота. +// return tail, head +func (s *TimeDeltaCompressor) Snapshot() (tail []byte, head []byte) { + if s.state == nil { + tail = s.encodeTail(s.lastUnixtime, s.lastDelta, s.h) + head = s.buf[s.pos:] } else { - size = 4 + tail = s.encodeTail(s.state.LastUnixtime, s.state.LastDelta, s.state.H) + head = s.state.Buf } return } -const ( - addUnixtime = 0 - incementRun = 1 - incementLiteral = 2 - endOfSeries = 3 - startRun = 4 - add1stDelta = 5 -) +func (s *TimeDeltaCompressor) encodeTail(lastUnixtime, lastDelta uint32, h byte) []byte { + tail := make([]byte, 9) + bin.PutUint32(tail, lastUnixtime) + n, _ := bin.PutVarUint64(tail[4:], uint64(lastDelta)) + tail[n+4] = h + return tail[:n+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 +func (s *TimeDeltaCompressor) CreateDecompressor() qb.TimestampDecompressor { + if s.state == nil { + return NewTimeDeltaDecompressor(s.buf[s.pos:]) } - // 1st value - s.lastUnixtime = unixtime + return NewTimeDeltaDecompressorFromCapturedState(*s.state) +} + +func (s *TimeDeltaCompressor) Renew() { + // УВАГА! + // state не чіпаємо + s.buf = make([]byte, minBufferSize) + s.pos = len(s.buf) + s.lastUnixtime = 0 + s.lastDelta = 0 + s.h = 0 +} + +func (s *TimeDeltaCompressor) Chunks() []byte { + return s.buf +} + +// DECOMPRESSOR + +type TimeDeltaDecompressor struct { + buf []byte + pos int + lastDelta uint32 + lastUnixtime uint32 + isRun bool + pending int + done bool +} + +func NewTimeDeltaDecompressor(buf []byte) *TimeDeltaDecompressor { + s := &TimeDeltaDecompressor{ + buf: buf, + } + if s.pos < len(s.buf) { + var err error + s.lastUnixtime, err = bin.GetUint32(s.buf) + if err != nil { + log.Fatalf("bug: get last unixtime: %s", err) + } + s.pos += 4 + //fmt.Println("restored last unixtime", s.lastUnixtime) + } else { + s.done = true + } + return s +} + +func NewTimeDeltaDecompressorFromCapturedState(state TimeDeltaCapturedState) *TimeDeltaDecompressor { + s := &TimeDeltaDecompressor{ + buf: state.Buf, + lastUnixtime: state.LastUnixtime, + } + if state.LastDelta > 0 { + s.lastDelta = state.LastDelta + s.decodeHeaderByte(state.H) + } + //fmt.Println("-------------------") + //fmt.Printf("restore from bound: isRun=%t, pending=%d, lastUnix=%d, lastDelta=%d\n", + //s.isRun, s.pending, s.lastUnixtime, s.lastDelta) + //fmt.Printf("buf: %d\n", s.buf) + return s +} + +func (s *TimeDeltaDecompressor) 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.lastDelta > 0 { + s.lastUnixtime -= s.lastDelta + s.pending-- + //fmt.Printf("lastUnix: %d, s.pending: %d\n", s.lastUnixtime, s.pending) + if s.pending > 0 { + // якщо в серії залишаються елементи + if !s.isRun { + s.readDelta() + } + } else if s.pos < len(s.buf) { + // в серії більше немає елементів, отже перевіряє чи є ще дані в буфері. + // дані є - читаю заголовок наступної серії + s.readHeader() + s.readDelta() + } else { + //s.done = true + s.lastDelta = 0 + } + } else { + // був закодований лише last unixtime + s.done = true + } + return value, false +} + +func (s *TimeDeltaDecompressor) readHeader() { + h := s.buf[s.pos] + s.pos++ + s.decodeHeaderByte(h) + // fmt.Println() + // fmt.Println("read from pos:", s.pos) + // fmt.Println("h:", h) + // fmt.Println("isRun:", s.isRun) + // fmt.Println("pending:", s.pending) + // fmt.Println() +} + +func (s *TimeDeltaDecompressor) decodeHeaderByte(h byte) { + s.isRun = h < 128 + if s.isRun { + s.pending = int(h) + 2 + } else { + s.pending = int(h&127) + 1 + } +} + +func (s *TimeDeltaDecompressor) readDelta() { + u64, n, err := bin.GetVarUint64(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) } // func (s *ReverseTimeDeltaCompressor) Append(unixtime uint32) { @@ -188,274 +406,3 @@ func (s *ReverseTimeDeltaCompressor) Append(code int, unixtime uint32) { // // 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) -} diff --git a/chunkenc/time_delta_delta.go b/chunkenc/time_delta_delta.go deleted file mode 100644 index aebf6c4..0000000 --- a/chunkenc/time_delta_delta.go +++ /dev/null @@ -1,270 +0,0 @@ -package chunkenc - -// // REVERSE - -// // const ( -// // lastUnixtimeIdx = 0 -// // baseDeltaIdx = 4 -// // ) - -// type ReverseTimeDeltaOfDeltaCompressor struct { -// buf *conbuf.ContinuousBuffer -// pos int -// lastDelta uint32 -// lastUnixtime uint32 -// lastDeltaOfDelta int64 -// lastDeltaOfDeltaSize int -// h byte -// } - -// func NewReverseTimeDeltaOfDeltaCompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaCompressor { -// s := &ReverseTimeDeltaOfDeltaCompressor{ -// buf: buf, -// pos: size, -// } -// if size > 0 { -// // s.lastUnixtime = s.buf.GetUint32(lastUnixtimeIdx) -// // u64, _, err := s.buf.GetVarUint64(baseDeltaIdx) -// // if err != nil { -// // log.Fatalf("bug: get base delta: %s", err) -// // } -// // s.baseDelta = uint32(u64) -// // s.h = s.buf.GetByte(s.pos - 1) -// // s.lastDeltaOfDelta, s.lastDeltaOfDeltaSize, err = s.buf.ReverseGetVarInt64(s.pos - 2) -// // if err != nil { -// // log.Fatalf("bug: get last delta of delta: %s", err) -// // } -// } else { -// //s.baseDelta = baseDelta -// } -// return s -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) Size() int { -// return s.pos -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) Append(unixtime uint32) { -// if s.lastUnixtime > 0 { -// if s.pos == 0 { -// // 2-й unixtime -// s.lastDelta = unixtime - s.lastUnixtime -// s.appendNewLiteral(0) -// } else { -// // 3-й unixtime і наступні -// lastDelta := unixtime - s.lastUnixtime -// deltaOfDelta := int64(lastDelta) - int64(s.lastDelta) -// if deltaOfDelta == s.lastDeltaOfDelta { -// 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 -// // fix encode delta -// s.appendNewLiteral(deltaOfDelta) -// } -// } else { -// // literal блок. -// // Якщо в ньому лише одне значення - перетворюю його на run блок. -// // Інакше забираю останнє значення із literal блока і додаю новий run блок. -// q := s.h & 127 -// if q == 0 { // 1 кодується як 0 -// s.convertLiteralToRun() -// } else { -// s.convertLastFromLiteralToRun(deltaOfDelta) -// } -// } -// } else { -// s.lastDelta = lastDelta -// if s.h < 127 { -// // end of run -// s.appendNewLiteral(deltaOfDelta) -// } else { -// if s.h < 255 { -// // encode value from pos - 1, then append h byte -// s.appendDeltaToLiteral(deltaOfDelta) -// } else { -// // overflowed - encode new -// s.appendNewLiteral(deltaOfDelta) -// } -// } -// } -// } -// } -// // для першого unixtime - лише оновлюємо lastUnixtime, а для наступних - -// // теж оновлюємо, але після запису серій в buf. -// s.lastUnixtime = unixtime -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) convertLastFromLiteralToRun(deltaOfDelta int64) { -// // Зменшую кількість елементів в literal блоці -// s.h-- -// s.pos -= 1 + s.lastDeltaOfDeltaSize -// s.buf.SetByte(s.pos, s.h) // закриваю literal блок -// s.pos++ -// s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta) -// s.pos += s.lastDeltaOfDeltaSize -// s.h = 0 // run блок, довжини 2 -// s.buf.SetByte(s.pos, s.h) -// s.pos++ -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) convertLiteralToRun() { -// // Знімаю flagLiteral, а лічильник 0 дорівнює 2 елементам в серії. -// s.h = 0 -// s.buf.SetByte(s.pos-1, s.h) -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) appendDeltaToLiteral(deltaOfDelta int64) { -// s.h++ // збільшую к-сть дельт -// s.lastDeltaOfDelta = deltaOfDelta -// s.pos-- -// s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta) -// s.pos += s.lastDeltaOfDeltaSize -// s.buf.SetByte(s.pos, s.h) -// s.pos++ -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) appendNewLiteral(deltaOfDelta int64) { -// s.h = flagLiteral -// s.lastDeltaOfDelta = deltaOfDelta -// s.lastDeltaOfDeltaSize = s.buf.ReversePutVarInt64(s.pos, deltaOfDelta) -// s.pos += s.lastDeltaOfDeltaSize -// s.buf.SetByte(s.pos, flagLiteral) // literal, length = 1 -// s.pos++ -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) Sync() { -// // дописую в кінець lastDelta та lastUnixtime -// if s.pos > 0 { -// n := s.buf.ReversePutVarUint64(s.pos, uint64(s.lastDelta)) -// s.pos += n -// } -// if s.lastUnixtime > 0 { -// s.buf.PutUint32(s.pos, s.lastUnixtime) -// s.pos += 4 -// } -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) DeleteLast() { - -// } - -// type TimeDeltaOfDeltaBound struct { -// LastUnixtime uint32 -// LastDelta uint32 -// Pos int -// H byte -// LastDeltaOfDelta int64 -// } - -// func (s *ReverseTimeDeltaOfDeltaCompressor) GetState() TimeDeltaOfDeltaBound { -// return TimeDeltaOfDeltaBound{ -// LastUnixtime: s.lastUnixtime, -// LastDelta: s.lastDelta, -// Pos: s.pos, -// H: s.h, -// LastDeltaOfDelta: s.lastDeltaOfDelta, -// } -// } - -// // DECOMPRESSOR - -// type ReverseTimeDeltaOfDeltaDecompressor struct { -// buf *conbuf.ContinuousBuffer -// size int -// pos int -// lastUnixtime uint32 -// lastDelta uint32 -// //lastDeltaOfDelta int64 -// isRun bool -// pending int -// } - -// func NewReverseTimeDeltaOfDeltaDecompressor(buf *conbuf.ContinuousBuffer, size int) *ReverseTimeDeltaOfDeltaDecompressor { -// return &ReverseTimeDeltaOfDeltaDecompressor{ -// buf: buf, -// size: size, -// pos: size, // last elem -// } -// } - -// func (s *ReverseTimeDeltaOfDeltaDecompressor) NextValue() (value uint32, done bool) { -// fmt.Printf("\nNextValue(): pos: %d, size: %d, pending: %d\n", s.pos, s.size, s.pending) -// if s.pos < s.size { -// if s.pending > 0 { -// //fmt.Println("pending", ) -// // якщо в серії залишаються елементи -// fmt.Println("s.lastUnixtime -= s.lastDelta", s.lastUnixtime, s.lastDelta) -// s.lastUnixtime -= s.lastDelta -// if !s.isRun { -// s.readValue() -// } -// s.pending-- -// return s.lastUnixtime, false -// } -// // FIX - читаю lastUnixtime -// if s.pos > 0 { -// s.lastUnixtime -= s.lastDelta -// // читаю заголовок наступної серії -// s.readHeader() -// s.readValue() -// s.pending-- -// return s.lastUnixtime, false -// } -// // серія завершена - перевіряю чи є ще серії -// return 0, true -// } else { -// s.pos -= 4 -// s.lastUnixtime = s.buf.GetUint32(s.pos) -// fmt.Println("lastUnix", s.lastUnixtime) -// if s.pos > 0 { -// s.pos-- -// u64, n, err := s.buf.ReverseGetVarUint64(s.pos) -// if err != nil { -// log.Fatalln(err) -// } -// s.pos -= n -// s.lastDelta = uint32(u64) -// fmt.Println("lastDelta", s.lastDelta) -// } -// return s.lastUnixtime, false -// } -// } - -// func (s *ReverseTimeDeltaOfDeltaDecompressor) 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 *ReverseTimeDeltaOfDeltaDecompressor) readValue() { -// //s.lastUnixtime -= s.lastDelta -// // var ( -// // err error -// // n int -// // ) -// lastDeltaOfDelta, n, err := s.buf.ReverseGetVarInt64(s.pos) -// if err != nil { -// log.Fatalln(err) -// } -// fmt.Println() -// fmt.Println("read Value from pos:", s.pos) -// fmt.Println("delta of delta:", lastDeltaOfDelta) -// //fmt.Println("delta of delta n:", n) -// s.pos -= n -// s.lastDelta = uint32(int64(s.lastDelta) - lastDeltaOfDelta) -// fmt.Println("last delta:", s.lastDelta) -// fmt.Println() -// } diff --git a/database/proc.go b/database/proc.go index df50a8d..fcb45e2 100644 --- a/database/proc.go +++ b/database/proc.go @@ -484,17 +484,17 @@ func (s *Database) addMetric(rec txlog.AddedMetric) { ) if rec.MetricType == qb.Cumulative { - values = chunkenc.NewReverseCumulativeDeltaCompressor( + values = chunkenc.NewCumulativeDeltaCompressor( valuesBuf, 0, byte(rec.FracDigits)) } else { - values = chunkenc.NewReverseInstantDeltaCompressor( + values = chunkenc.NewInstantDeltaCompressor( valuesBuf, 0, byte(rec.FracDigits)) } s.metrics[rec.MetricID] = &_metric{ MetricType: rec.MetricType, FracDigits: byte(rec.FracDigits), - Timestamps: chunkenc.NewReverseTimeDeltaCompressor(timestampsBuf, 0), + Timestamps: chunkenc.NewTimeDeltaCompressor(timestampsBuf, 0), Values: values, } diff --git a/go.mod b/go.mod index e57b9dc..abff683 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,6 @@ go 1.24.2 require ( gopkg.in/ini.v1 v1.67.1 - gordenko.dev/dima/bin v0.0.0-20260604235618-bff16d774d98 + gordenko.dev/dima/bin v0.0.0-20260606153512-101b10d92a39 gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69 ) diff --git a/go.sum b/go.sum index 2b1421c..0f356ec 100644 --- a/go.sum +++ b/go.sum @@ -18,7 +18,7 @@ gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gordenko.dev/dima/bin v0.0.0-20260604235618-bff16d774d98 h1:pQzJ4wnSrFXn9Hu+v6KvWZ4GQVBmuTljJGZ20Pb+BJA= -gordenko.dev/dima/bin v0.0.0-20260604235618-bff16d774d98/go.mod h1:/I+9fvRUzXHgXSwGEwOK1mBBM4BJ3AtCmoCZ2upDiwk= +gordenko.dev/dima/bin v0.0.0-20260606153512-101b10d92a39 h1:qW3SnQ9HAcJpfj8ottBfPGTzHX+cX2vM8B6TVOgnypE= +gordenko.dev/dima/bin v0.0.0-20260606153512-101b10d92a39/go.mod h1:up64wpJp9xI+HqACtMDBIfPNUH6BYY/b3inKpUfrqDQ= gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69 h1:nyJ3mzTQ46yUeMZCdLyYcs7B5JCS54c67v84miyhq2E= gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69/go.mod h1:AxgKDktpqBVyIOhIcP+nlCpK+EsJyjN5kPdqyd8euVU= diff --git a/qb.go b/qb.go index 337e2b4..1775649 100644 --- a/qb.go +++ b/qb.go @@ -23,17 +23,18 @@ const ( ) type TimestampCompressor interface { - CalcRequiredSpace(uint32) int - Append(uint32) + CalcRequiredSpace(uint32) (int, int) + Append(int, uint32) Size() int Chunks() [][]byte DeleteLast() Renew() - Lock() - Unlock() + CaptureState() + ForgetCapturedState() CreateDecompressor() TimestampDecompressor - Snapshot() ([][]byte, int) // chunks, size + Snapshot() ([]byte, []byte) // payload, additional payload Offset() int + Sync() //LastTimestamp() uint32 }