package enc import ( "bytes" "fmt" "math" "slices" "testing" "time" bin "gordenko.dev/dima/bin/little" "gordenko.dev/dima/qb" "gordenko.dev/dima/textutil" ) func equalFloatSlices(a, b []float64, epsilon float64) bool { return slices.EqualFunc(a, b, func(x, y float64) bool { return math.Abs(x-y) <= epsilon }) } // CUMULATIVE var ( cumulativeTestCases = []struct { Nums []float64 Name string RewindOffset int ChangeSize int Buf []byte BaseValue float64 LastDelta uint64 }{ { Nums: []float64{ 0, }, Name: "add 1st value zero", RewindOffset: 0, ChangeSize: 3, Buf: []byte{ 0x80, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 0, LastDelta: 0, }, { Nums: []float64{ 1.5, }, Name: "add 1st value non zero", RewindOffset: 0, ChangeSize: 3, Buf: []byte{ 0x8f, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: []float64{ 1.5, 1.5, }, Name: "literal switch to run", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x8f, // base value 0x80, // delta 0 0x00, // run (len = 2) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: []float64{ 1.5, 1.6, 1.6, }, Name: "literal decrease by 1 and switch to run", RewindOffset: 2, ChangeSize: 3, Buf: []byte{ 0x8f, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x81, // delta 1 0x00, // run (len = 2) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 1, }, { Nums: []float64{ 1.5, 1.5, 1.5, }, Name: "increment run", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x8f, // base value 0x80, // delta 0 0x01, // run (len = 3) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: []float64{ 1.5, 1.5, 1.5, 1.6, }, Name: "run switch to literal", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x8f, // base value 0x80, // delta 0 0x01, // run (len = 3) 0x81, // delta 1 0x80, // literal (len = 1) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 1, }, { Nums: repeatFloat64(1.5, 129), Name: "increment run to full fill h-byte", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x8f, // base value 0x80, // delta 0 0x7f, // run (len = 129) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: repeatFloat64(1.5, 130), Name: "run switch to literal after h-byte overflow", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x8f, // base value 0x80, // delta 0 0x7f, // run (len = 129) 0x80, // delta 0 0x80, // literal (len = 1) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: []float64{ 1.5, 1.6, 1.7, }, Name: "increment literal", RewindOffset: 1, ChangeSize: 2, Buf: []byte{ 0x8f, // base value 0x80, // delta 0 0x81, // delta 1 0x82, // delta 2 0x82, // literal (len = 3) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 2, }, } ) func TestCumulativeDeltaCompressor(t *testing.T) { for _, testCase := range cumulativeTestCases { var ( tmp = make([]byte, tmpValueSize) metricType = qb.Cumulative fracDigits byte = 1 buf = make([]byte, 8) payloadSize = 0 report qb.ValueEvaluationReport ) c := NewValueDeltaCompressor(metricType, fracDigits, buf, payloadSize) for _, num := range testCase.Nums { report = c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num, report.Delta) } if report.RewindOffset != testCase.RewindOffset { t.Fatalf("%s: got offset %d are not equal expected %d", testCase.Name, report.RewindOffset, testCase.RewindOffset) } if report.ChangeSize != testCase.ChangeSize { t.Fatalf("%s: got changeSize %d are not equal expected %d", testCase.Name, report.ChangeSize, testCase.ChangeSize) } if !bytes.Equal(buf, testCase.Buf) { t.Fatalf("%s: got buf % x are not equal expected % x", testCase.Name, buf, testCase.Buf) } if c.baseValue != testCase.BaseValue { t.Fatalf("%s: got baseValue %v are not equal expected %v", testCase.Name, c.baseValue, testCase.BaseValue) } if c.lastDelta != testCase.LastDelta { t.Fatalf("%s: got lastDelta %d are not equal expected %d", testCase.Name, c.lastDelta, testCase.LastDelta) } } } func TestRestoreCumulativeDeltaCompressor(t *testing.T) { for _, testCase := range cumulativeTestCases { var ( tmp = make([]byte, tmpValueSize) metricType = qb.Cumulative fracDigits byte = 1 buf = make([]byte, 8) payloadSize = 0 report qb.ValueEvaluationReport ) c := NewValueDeltaCompressor(metricType, fracDigits, buf, payloadSize) for _, num := range testCase.Nums { report = c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num, report.Delta) } restored := NewValueDeltaCompressor(metricType, fracDigits, buf, c.Size()) if restored.baseValue != testCase.BaseValue { t.Fatalf("%s: got baseValue %v are not equal expected %v", testCase.Name, restored.baseValue, testCase.BaseValue) } if restored.lastDelta != testCase.LastDelta { t.Fatalf("%s: got lastDelta %d are not equal expected %d", testCase.Name, restored.lastDelta, testCase.LastDelta) } } } func TestCumulativeDeltaDecompressorFromState(t *testing.T) { for _, testCase := range cumulativeTestCases { var ( metricType = qb.Cumulative fracDigits byte = 1 tmp = make([]byte, tmpValueSize) buf = make([]byte, 16) payloadSize = 0 decodedNums []float64 ) c := NewValueDeltaCompressor(metricType, fracDigits, buf, payloadSize) for _, num := range testCase.Nums { report := c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num, report.Delta) } d := c.CreateDecompressor(metricType, fracDigits) for { num, done := d.NextValue() if done { break } decodedNums = append(decodedNums, num) } slices.Reverse(decodedNums) if !equalFloatSlices(testCase.Nums, decodedNums, 0.0000001) { t.Fatalf("%s: got nums %v not equal expected %v", testCase.Name, decodedNums, testCase.Nums) } } } func TestCumulativeDeltaDecompressorFromEnd(t *testing.T) { for _, testCase := range cumulativeTestCases { var ( metricType = qb.Cumulative fracDigits byte = 1 tmp = make([]byte, tmpValueSize) buf = make([]byte, 16) payloadSize = 0 decodedNums []float64 ) c := NewValueDeltaCompressor(metricType, fracDigits, buf, payloadSize) for _, num := range testCase.Nums { report := c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num, report.Delta) } d := NewValueDeltaDecompressor(metricType, fracDigits) d.RestoreFromEnd(buf[:c.Size()]) for { num, done := d.NextValue() if done { break } decodedNums = append(decodedNums, num) } slices.Reverse(decodedNums) if !equalFloatSlices(testCase.Nums, decodedNums, 0.0000001) { t.Fatalf("%s: got nums %v not equal expected %v", testCase.Name, decodedNums, testCase.Nums) } } } // INSTANT var ( instantTestCases = []struct { Nums []float64 Name string RewindOffset int ChangeSize int Buf []byte BaseValue float64 LastDelta uint64 }{ { Nums: []float64{ 1.5, }, Name: "add 1st value", RewindOffset: 0, ChangeSize: 3, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: []float64{ -1.5, }, Name: "add 1st value (negative)", RewindOffset: 0, ChangeSize: 3, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: 0, }, { Nums: []float64{ 1.5, 1.5, }, Name: "literal switch to run", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x00, // run (len = 2) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: []float64{ -1.5, -1.5, }, Name: "literal switch to run (negative)", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x00, // run (len = 2) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: 0, }, { Nums: []float64{ 1.5, 1.6, 1.6, }, Name: "literal decrease by 1 and switch to run", RewindOffset: 2, ChangeSize: 3, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x82, // delta 1 0x00, // run (len = 2) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: bin.EncodeZigZag(1), }, { Nums: []float64{ -1.5, -1.6, -1.6, }, Name: "literal decrease by 1 and switch to run (negative)", RewindOffset: 2, ChangeSize: 3, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x81, // delta -1 0x00, // run (len = 2) 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: bin.EncodeZigZag(-1), }, { Nums: []float64{ 1.5, 1.4, 1.4, }, Name: "literal decrease by 1 and switch to run (negative delta)", RewindOffset: 2, ChangeSize: 3, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x81, // delta -1 0x00, // run (len = 2) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: bin.EncodeZigZag(-1), }, { Nums: []float64{ -1.5, -1.4, -1.4, }, Name: "literal decrease by 1 and switch to run (positive delta, negative)", RewindOffset: 2, ChangeSize: 3, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x80, // literal (len = 1) 0x82, // delta 1 0x00, // run (len = 2) 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: bin.EncodeZigZag(1), }, { Nums: []float64{ 1.5, 1.5, 1.5, }, Name: "increment run", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x01, // run (len = 3) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: []float64{ -1.5, -1.5, -1.5, }, Name: "increment run (negative)", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x01, // run (len = 3) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: 0, }, { Nums: []float64{ 1.5, 1.5, 1.5, 1.6, }, Name: "run switch to literal", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x01, // run (len = 3) 0x82, // delta 1 0x80, // literal (len = 1) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: bin.EncodeZigZag(1), }, { Nums: []float64{ -1.5, -1.5, -1.5, -1.6, }, Name: "run switch to literal (negative)", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x01, // run (len = 3) 0x81, // delta -1 0x80, // literal (len = 1) 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: bin.EncodeZigZag(-1), }, { Nums: []float64{ 1.5, 1.5, 1.5, 1.4, }, Name: "run switch to literal (negative delta)", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x01, // run (len = 3) 0x81, // delta -1 0x80, // literal (len = 1) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: bin.EncodeZigZag(-1), }, { Nums: []float64{ -1.5, -1.5, -1.5, -1.4, }, Name: "run switch to literal (positive delta, negative version)", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x01, // run (len = 3) 0x82, // delta 1 0x80, // literal (len = 1) 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: bin.EncodeZigZag(1), }, { Nums: repeatFloat64(1.5, 129), Name: "increment run to full fill h-byte", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x7f, // run (len = 129) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: repeatFloat64(-1.5, 129), Name: "increment run to full fill h-byte (negative version)", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x7f, // run (len = 129) 0x00, 0x00, 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: 0, }, { Nums: repeatFloat64(1.5, 130), Name: "run switch to literal after h-byte overflow", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x7f, // run (len = 129) 0x80, // delta 0 0x80, // literal (len = 1) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: 0, }, { Nums: repeatFloat64(-1.5, 130), Name: "run switch to literal after h-byte overflow (negative version)", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x7f, // run (len = 129) 0x80, // delta 0 0x80, // literal (len = 1) 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: 0, }, { Nums: []float64{ 1.5, 1.6, 1.7, }, Name: "increment literal", RewindOffset: 1, ChangeSize: 2, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x82, // delta 1 0x84, // delta 2 0x82, // literal (len = 3) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: bin.EncodeZigZag(2), }, { Nums: []float64{ -1.5, -1.6, -1.7, }, Name: "increment literal (negative version)", RewindOffset: 1, ChangeSize: 2, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x81, // delta -1 0x83, // delta -2 0x82, // literal (len = 3) 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: bin.EncodeZigZag(-2), }, { Nums: []float64{ 1.5, 1.4, 1.3, }, Name: "increment literal (negative delta)", RewindOffset: 1, ChangeSize: 2, Buf: []byte{ 0x9e, // base value 0x80, // delta 0 0x81, // delta -1 0x83, // delta -2 0x82, // literal (len = 3) 0x00, 0x00, 0x00, }, BaseValue: 1.5, LastDelta: bin.EncodeZigZag(-2), }, { Nums: []float64{ -1.5, -1.4, -1.3, }, Name: "increment literal (positive delta, negative version)", RewindOffset: 1, ChangeSize: 2, Buf: []byte{ 0x9d, // base value 0x80, // delta 0 0x82, // delta 1 0x84, // delta 2 0x82, // literal (len = 3) 0x00, 0x00, 0x00, }, BaseValue: -1.5, LastDelta: bin.EncodeZigZag(2), }, } ) func TestInstantDeltaCompressor(t *testing.T) { for _, testCase := range instantTestCases { var ( tmp = make([]byte, tmpValueSize) metricType = qb.Instant fracDigits byte = 1 buf = make([]byte, 8) payloadSize = 0 report qb.ValueEvaluationReport ) c := NewValueDeltaCompressor(metricType, fracDigits, buf, payloadSize) for _, num := range testCase.Nums { report = c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num, report.Delta) } if report.RewindOffset != testCase.RewindOffset { t.Fatalf("%s: got rewindOffset %d are not equal expected %d", testCase.Name, report.RewindOffset, testCase.RewindOffset) } if report.ChangeSize != testCase.ChangeSize { t.Fatalf("%s: got changeSize %d are not equal expected %d", testCase.Name, report.ChangeSize, testCase.ChangeSize) } if !bytes.Equal(buf, testCase.Buf) { t.Fatalf("%s: got buf % x are not equal expected % x", testCase.Name, buf, testCase.Buf) } if c.baseValue != testCase.BaseValue { t.Fatalf("%s: got baseValue %v are not equal expected %v", testCase.Name, c.baseValue, testCase.BaseValue) } if c.lastDelta != testCase.LastDelta { t.Fatalf("%s: got lastDelta %d are not equal expected %d", testCase.Name, c.lastDelta, testCase.LastDelta) } } } func TestRestoreInstantDeltaCompressor(t *testing.T) { for _, testCase := range instantTestCases { var ( tmp = make([]byte, tmpValueSize) metricType = qb.Instant fracDigits byte = 1 buf = make([]byte, 8) payloadSize = 0 report qb.ValueEvaluationReport ) c := NewValueDeltaCompressor(metricType, fracDigits, buf, payloadSize) for _, num := range testCase.Nums { report = c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num, report.Delta) } restored := NewValueDeltaCompressor(metricType, fracDigits, buf, c.Size()) if restored.baseValue != testCase.BaseValue { t.Fatalf("%s: got baseValue %v are not equal expected %v", testCase.Name, restored.baseValue, testCase.BaseValue) } if restored.lastDelta != testCase.LastDelta { t.Fatalf("%s: got lastDelta %d are not equal expected %d", testCase.Name, restored.lastDelta, testCase.LastDelta) } } } func TestInstantDeltaDecompressorFromState(t *testing.T) { for _, testCase := range instantTestCases { var ( metricType = qb.Instant fracDigits byte = 1 tmp = make([]byte, tmpValueSize) buf = make([]byte, 16) payloadSize = 0 decodedNums []float64 ) c := NewValueDeltaCompressor(metricType, fracDigits, buf, payloadSize) for _, num := range testCase.Nums { report := c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num, report.Delta) } d := c.CreateDecompressor(metricType, fracDigits) for { num, done := d.NextValue() if done { break } decodedNums = append(decodedNums, num) } slices.Reverse(decodedNums) if !equalFloatSlices(testCase.Nums, decodedNums, 0.0000001) { t.Fatalf("%s: got nums %v not equal expected %v", testCase.Name, decodedNums, testCase.Nums) } } } func TestInstantDeltaDecompressorFromEnd(t *testing.T) { for _, testCase := range instantTestCases { var ( metricType = qb.Instant fracDigits byte = 1 tmp = make([]byte, tmpValueSize) buf = make([]byte, 8) payloadSize = 0 decodedNums []float64 ) c := NewValueDeltaCompressor(metricType, fracDigits, buf, payloadSize) for _, num := range testCase.Nums { report := c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num, report.Delta) } d := NewValueDeltaDecompressor(metricType, fracDigits) d.RestoreFromEnd(buf[:c.Size()]) for { num, done := d.NextValue() if done { break } decodedNums = append(decodedNums, num) } slices.Reverse(decodedNums) if !equalFloatSlices(testCase.Nums, decodedNums, 0.0000001) { t.Fatalf("%s: got nums %v not equal expected %v", testCase.Name, decodedNums, testCase.Nums) } } } // TIME DELTA var ( timeTestCases = []struct { Name string Nums []uint32 Buf []byte LastUnixtime uint32 LastDelta uint32 RewindOffset int ChangeSize int }{ { Nums: []uint32{ 1780777000, }, Name: "add 1st value", RewindOffset: 0, ChangeSize: 4, Buf: []byte{ 0x00, 0x00, 0x00, 0x00, 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777000, LastDelta: 0, }, { Nums: []uint32{ 1780777000, 1780777060, // +60 }, Name: "add 1st delta", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x00, 0x00, 0x80, // h-byte (literal, len=1) 0xbc, // delta (60) 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777060, LastDelta: 60, }, { Nums: []uint32{ 1780777000, 1780777060, // +60 1780777120, // +60 }, Name: "literal changed to run", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x00, 0x00, 0x00, // h-byte (run, len=2) 0xbc, // delta 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777120, LastDelta: 60, }, { Nums: []uint32{ 1780777000, 1780777060, // +60 1780777130, // +70 1780777200, // +70 }, Name: "literal decrease by 1 and switch to run", RewindOffset: 2, ChangeSize: 3, Buf: []byte{ 0x00, // h-byte (run, len=2) 0xc6, // delta 70 0x80, // h-byte (literal, len=1) 0xbc, // delta 60 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777200, LastDelta: 70, }, { Nums: []uint32{ 1780777000, 1780777060, // +60 1780777120, // +60 1780777180, // +60 }, Name: "increment run", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x00, 0x00, 0x01, // h-byte (run, len=2) 0xbc, // delta 60 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777180, LastDelta: 60, }, { Nums: []uint32{ 1780777000, 1780777060, // +60 1780777120, // +60 1780777180, // +60 1780777200, // +20 }, Name: "switch run to literal", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x80, // h-byte (literal, len=1) 0x94, // delta 20 0x01, // h-byte (run, len=3) 0xbc, // delta 60 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777200, LastDelta: 20, }, { Nums: generateProgression(1780777000, 60, 130), Name: "increment run up to full filled h-byte", RewindOffset: 1, ChangeSize: 1, Buf: []byte{ 0x00, 0x00, 0x7f, // h-byte (run, len=129) 0xbc, // delta 60 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777000 + 129*60, LastDelta: 60, }, { Nums: generateProgression(1780777000, 60, 131), Name: "run switch to literal after h-byte overflow", RewindOffset: 0, ChangeSize: 2, Buf: []byte{ 0x80, // h-byte (literal, len=1) 0xbc, // delta 60 0x7f, // h-byte (run, len=129) 0xbc, // delta 60 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777000 + 130*60, LastDelta: 60, }, { Nums: []uint32{ 1780777000, 1780777060, // +60 1780777130, // +70 }, Name: "increment literal", RewindOffset: 1, ChangeSize: 2, Buf: []byte{ 0x00, 0x81, // h-byte (literal, len=2) 0xc6, // delta 70 0xbc, // delta 60 0x28, 0x80, 0x24, 0x6a, // since }, LastUnixtime: 1780777130, LastDelta: 70, }, } ) func TestTimeDeltaCompressor(t *testing.T) { for _, testCase := range timeTestCases { var ( tmp = make([]byte, tmpTimeSize) buf = make([]byte, 8) payloadSize = 0 report qb.TimeEvaluationReport ) c := NewTimeDeltaCompressor(buf, payloadSize) for _, num := range testCase.Nums { report = c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num) } if report.RewindOffset != testCase.RewindOffset { t.Fatalf("%s: got rewindOffset %d are not equal expected %d", testCase.Name, report.RewindOffset, testCase.RewindOffset) } if report.ChangeSize != testCase.ChangeSize { t.Fatalf("%s: got changeSize %d are not equal expected %d", testCase.Name, report.ChangeSize, testCase.ChangeSize) } if !bytes.Equal(buf, testCase.Buf) { t.Fatalf("%s: got buf % x are not equal expected % x", testCase.Name, buf, testCase.Buf) } if c.lastUnixtime != testCase.LastUnixtime { t.Fatalf("%s: got lastUnixtime %d are not equal expected %d", testCase.Name, c.lastUnixtime, testCase.LastUnixtime) } if c.lastDelta != testCase.LastDelta { t.Fatalf("%s: got lastDelta %d are not equal expected %d", testCase.Name, c.lastDelta, testCase.LastDelta) } } } func TestRestoreTimeDeltaCompressor(t *testing.T) { for _, testCase := range timeTestCases { var ( tmp = make([]byte, tmpTimeSize) buf = make([]byte, 8) payloadSize = 0 report qb.TimeEvaluationReport ) c := NewTimeDeltaCompressor(buf, payloadSize) for _, num := range testCase.Nums { report = c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num) } restored := NewTimeDeltaCompressor(buf, c.Size()) if restored.lastUnixtime != testCase.LastUnixtime { t.Fatalf("%s: got lastUnixtime %d are not equal expected %d", testCase.Name, restored.lastUnixtime, testCase.LastUnixtime) } if restored.lastDelta != testCase.LastDelta { t.Fatalf("%s: got lastDelta %d are not equal expected %d", testCase.Name, restored.lastDelta, testCase.LastDelta) } } } func TestTimeDeltaDecompressorFromState(t *testing.T) { for _, testCase := range timeTestCases { var ( tmp = make([]byte, tmpTimeSize) buf = make([]byte, 16) payloadSize = 0 decodedNums []uint32 ) c := NewTimeDeltaCompressor(buf, payloadSize) for _, num := range testCase.Nums { report := c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num) } fmt.Println("-----") d := c.CreateDecompressor() for { num, done := d.NextValue() if done { break } decodedNums = append(decodedNums, num) } slices.Reverse(decodedNums) if !slices.Equal(testCase.Nums, decodedNums) { t.Fatalf("%s: got nums %v not equal expected %v", testCase.Name, decodedNums, testCase.Nums) } } } func TestTimeDeltaDecompressorFromEnd(t *testing.T) { for _, testCase := range timeTestCases { var ( tmp = make([]byte, tmpTimeSize) buf = make([]byte, 8) payloadSize = 0 decodedNums []uint32 ) c := NewTimeDeltaCompressor(buf, payloadSize) for _, num := range testCase.Nums { report := c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num) } c.ReplaceSinceWithUntil() d := NewTimeDeltaDecompressor() d.RestoreFromEnd(buf[len(buf)-c.Size():]) for { num, done := d.NextValue() if done { break } decodedNums = append(decodedNums, num) } slices.Reverse(decodedNums) if !slices.Equal(testCase.Nums, decodedNums) { t.Fatalf("%s: got nums %v not equal expected %v", testCase.Name, decodedNums, testCase.Nums) } } } func TestVarInt64(t *testing.T) { arr := make([]byte, 9) n, err := bin.PutVarInt64(arr, -15) if err != nil { t.Fatal(err) } fmt.Println(arr[:n]) } func repeatFloat64(num float64, n int) []float64 { nums := make([]float64, n) for i := range nums { nums[i] = num } return nums } func generateProgression(start uint32, delta uint32, n int) []uint32 { nums := make([]uint32, n) nums[0] = start for i := 1; i < n; i++ { nums[i] = nums[i-1] + delta } return nums } func generateTimestamps(days int) (timestamps []uint32) { var ( minutes = []int{14, 29, 44, 59} hoursPerDay = 24 totalHours = days * hoursPerDay since = time.Now().AddDate(0, 0, -days) ) for i := range totalHours { hourTime := since.Add(time.Duration(i) * time.Hour) for _, m := range minutes { measureTime := time.Date( hourTime.Year(), hourTime.Month(), hourTime.Day(), hourTime.Hour(), m, // minutes 0, // seconds 0, // nanoseconds time.Local, ) timestamps = append(timestamps, uint32(measureTime.Unix())) } } return } func TestXXX(t *testing.T) { var ( timestamps = generateTimestamps(20)[:1231] tmp = make([]byte, tmpTimeSize) buf = make([]byte, 64) payloadSize = 0 decodedNums []uint32 ) c := NewTimeDeltaCompressor(buf, payloadSize) for _, num := range timestamps { report := c.Evaluate(tmp, num) c.Append(report.RewindOffset, tmp[:report.ChangeSize], num) } d := c.CreateDecompressor() for { num, done := d.NextValue() if done { break } decodedNums = append(decodedNums, num) } slices.Reverse(decodedNums) if !slices.Equal(timestamps, decodedNums) { t.Fatalf("got nums %v not equal expected %v", decodedNums, timestamps) } } func TestYYY(t *testing.T) { buf, err := textutil.ParseHex("50 04 87 ac 6b 05 6a") if err != nil { t.Fatal(err) } //var decodedNums []uint32 var prev uint32 d := NewTimeDeltaDecompressor() d.RestoreFromEnd(buf) for { num, done := d.NextValue() if done { break } fmt.Println(num, prev-num) prev = num //decodedNums = append(decodedNums, num) } // //slices.Reverse(decodedNums) // for _, num := range decodedNums { // } }