2016-11-15 01:33:34 -08:00
|
|
|
|
package chunks
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"math"
|
|
|
|
|
|
2016-11-20 05:33:00 -08:00
|
|
|
|
bits "github.com/dgryski/go-bits"
|
2016-11-15 01:33:34 -08:00
|
|
|
|
"github.com/prometheus/common/model"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// XORChunk holds XOR encoded sample data.
|
|
|
|
|
type XORChunk struct {
|
2016-11-20 05:33:00 -08:00
|
|
|
|
num uint16
|
|
|
|
|
bitChunk
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewXORChunk returns a new chunk with XOR encoding of the given size.
|
|
|
|
|
func NewXORChunk(sz int) *XORChunk {
|
2016-11-20 05:33:00 -08:00
|
|
|
|
return &XORChunk{bitChunk: newBitChunk(sz, EncXOR)}
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Appender implements the Chunk interface.
|
|
|
|
|
func (c *XORChunk) Appender() Appender {
|
2016-11-20 05:33:00 -08:00
|
|
|
|
return &xorAppender{c: c, pos: 1}
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iterator implements the Chunk interface.
|
|
|
|
|
func (c *XORChunk) Iterator() Iterator {
|
2016-11-20 05:33:00 -08:00
|
|
|
|
return &xorIterator{br: c.bitChunk.reader(), numTotal: c.num}
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type xorAppender struct {
|
2016-11-20 05:33:00 -08:00
|
|
|
|
c *XORChunk
|
2016-11-15 01:33:34 -08:00
|
|
|
|
|
2016-11-20 05:33:00 -08:00
|
|
|
|
t int64
|
|
|
|
|
v float64
|
|
|
|
|
buf [20]byte // bits written for current sample. 17 to avoid if condition in hot path.
|
|
|
|
|
pos uint8 // num of bytes in buf
|
|
|
|
|
count uint8 // number of bits in last buf byte
|
|
|
|
|
|
|
|
|
|
leading uint8
|
|
|
|
|
trailing uint8
|
|
|
|
|
finished bool
|
|
|
|
|
|
|
|
|
|
tDelta uint64
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *xorAppender) Append(ts model.Time, v model.SampleValue) error {
|
2016-11-20 05:33:00 -08:00
|
|
|
|
// TODO(fabxc): remove Prometheus types from interface.
|
|
|
|
|
return a.append(int64(ts), float64(v))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *xorAppender) append(t int64, v float64) error {
|
|
|
|
|
// Reset bit buffer.
|
|
|
|
|
a.buf = [20]byte{}
|
|
|
|
|
a.count = 8
|
|
|
|
|
a.pos = 0
|
|
|
|
|
|
|
|
|
|
if a.c.num > 1 {
|
|
|
|
|
tDelta := uint64(t - a.t)
|
|
|
|
|
dod := int64(tDelta - a.tDelta)
|
|
|
|
|
|
|
|
|
|
// Gorilla has a max resolution of seconds, Prometheus milliseconds.
|
|
|
|
|
// Thus we use higher value range steps with larger bit size.
|
|
|
|
|
switch {
|
|
|
|
|
case dod == 0:
|
|
|
|
|
a.writeBit(zero)
|
|
|
|
|
case -8191 <= dod && dod <= 8192:
|
|
|
|
|
a.writeBits(0x02, 2) // '10'
|
|
|
|
|
a.writeBits(uint64(dod), 14)
|
|
|
|
|
case -65535 <= dod && dod <= 65536:
|
|
|
|
|
a.writeBits(0x06, 3) // '110'
|
|
|
|
|
a.writeBits(uint64(dod), 17)
|
|
|
|
|
case -524287 <= dod && dod <= 524288:
|
|
|
|
|
a.writeBits(0x0e, 4) // '1110'
|
|
|
|
|
a.writeBits(uint64(dod), 20)
|
|
|
|
|
default:
|
|
|
|
|
a.writeBits(0x0f, 4) // '1111'
|
|
|
|
|
a.writeBits(uint64(dod), 64)
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|
2016-11-20 05:33:00 -08:00
|
|
|
|
a.tDelta = tDelta
|
|
|
|
|
|
|
|
|
|
a.writeVDelta(v)
|
|
|
|
|
|
|
|
|
|
} else if a.c.num == 0 {
|
|
|
|
|
// TODO: store varint time?
|
|
|
|
|
a.writeBits(uint64(t), 64)
|
|
|
|
|
a.writeBits(math.Float64bits(v), 64)
|
|
|
|
|
} else {
|
|
|
|
|
a.tDelta = uint64(t - a.t)
|
|
|
|
|
// TODO: use varint or other encoding for first delta?
|
|
|
|
|
a.writeBits(uint64(a.tDelta), 64)
|
|
|
|
|
a.writeVDelta(v)
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-11-20 05:33:00 -08:00
|
|
|
|
if err := a.c.append(a.buf, int(a.pos+1)*8-int(a.count)); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
a.t = t
|
|
|
|
|
a.v = v
|
|
|
|
|
a.c.num++
|
|
|
|
|
// TODO: also preserve tDelta – even though it doesn't really matter at this point.
|
2016-11-15 01:33:34 -08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-20 05:33:00 -08:00
|
|
|
|
func (a *xorAppender) writeVDelta(v float64) {
|
|
|
|
|
vDelta := math.Float64bits(v) ^ math.Float64bits(a.v)
|
|
|
|
|
|
|
|
|
|
if vDelta == 0 {
|
|
|
|
|
a.writeBit(zero)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
a.writeBit(one)
|
|
|
|
|
|
|
|
|
|
leading := uint8(bits.Clz(vDelta))
|
|
|
|
|
trailing := uint8(bits.Ctz(vDelta))
|
|
|
|
|
|
|
|
|
|
// clamp number of leading zeros to avoid overflow when encoding
|
|
|
|
|
if leading >= 32 {
|
|
|
|
|
leading = 31
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(dgryski): check if it's 'cheaper' to reset the leading/trailing bits instead
|
|
|
|
|
if a.leading != ^uint8(0) && leading >= a.leading && trailing >= a.trailing {
|
|
|
|
|
a.writeBit(zero)
|
|
|
|
|
a.writeBits(vDelta>>a.trailing, 64-int(a.leading)-int(a.trailing))
|
|
|
|
|
} else {
|
|
|
|
|
a.leading, a.trailing = leading, trailing
|
|
|
|
|
|
|
|
|
|
a.writeBit(one)
|
|
|
|
|
a.writeBits(uint64(leading), 5)
|
|
|
|
|
|
|
|
|
|
// Note that if leading == trailing == 0, then sigbits == 64. But that value doesn't actually fit into the 6 bits we have.
|
|
|
|
|
// Luckily, we never need to encode 0 significant bits, since that would put us in the other case (vdelta == 0).
|
|
|
|
|
// So instead we write out a 0 and adjust it back to 64 on unpacking.
|
|
|
|
|
sigbits := 64 - leading - trailing
|
|
|
|
|
a.writeBits(uint64(sigbits), 6)
|
|
|
|
|
a.writeBits(vDelta>>trailing, int(sigbits))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *xorAppender) writeBits(u uint64, nbits int) {
|
|
|
|
|
u <<= (64 - uint(nbits))
|
|
|
|
|
for nbits >= 8 {
|
|
|
|
|
byt := byte(u >> 56)
|
|
|
|
|
a.writeByte(byt)
|
|
|
|
|
u <<= 8
|
|
|
|
|
nbits -= 8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for nbits > 0 {
|
|
|
|
|
a.writeBit((u >> 63) == 1)
|
|
|
|
|
u <<= 1
|
|
|
|
|
nbits--
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *xorAppender) writeBit(bit bit) {
|
|
|
|
|
if a.count == 0 {
|
|
|
|
|
a.pos++
|
|
|
|
|
a.count = 8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if bit {
|
|
|
|
|
a.buf[a.pos] |= 1 << (a.count - 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a.count--
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *xorAppender) writeByte(byt byte) {
|
|
|
|
|
if a.count == 0 {
|
|
|
|
|
a.pos++
|
|
|
|
|
a.count = 8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fill up b.b with b.count bits from byt
|
|
|
|
|
a.buf[a.pos] |= byt >> (8 - a.count)
|
|
|
|
|
|
|
|
|
|
a.pos++
|
|
|
|
|
a.buf[a.pos] = byt << a.count
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-15 01:33:34 -08:00
|
|
|
|
type xorIterator struct {
|
2016-11-20 05:33:00 -08:00
|
|
|
|
br *bitChunkReader
|
|
|
|
|
numTotal uint16
|
|
|
|
|
numRead uint16
|
|
|
|
|
|
|
|
|
|
t int64
|
|
|
|
|
val float64
|
|
|
|
|
|
|
|
|
|
leading uint8
|
|
|
|
|
trailing uint8
|
|
|
|
|
|
|
|
|
|
tDelta int64
|
|
|
|
|
err error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (it *xorIterator) Values() (int64, float64) {
|
|
|
|
|
return it.t, it.val
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (it *xorIterator) NextB() bool {
|
|
|
|
|
if it.err != nil || it.numRead == it.numTotal {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var d byte
|
|
|
|
|
var dod int32
|
|
|
|
|
var sz uint
|
|
|
|
|
var tDelta int64
|
|
|
|
|
|
|
|
|
|
if it.numRead == 0 {
|
|
|
|
|
t, err := it.br.readBits(64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
v, err := it.br.readBits(64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
it.t = int64(t)
|
|
|
|
|
it.val = math.Float64frombits(v)
|
|
|
|
|
|
|
|
|
|
it.numRead++
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if it.numRead == 1 {
|
|
|
|
|
tDelta, err := it.br.readBits(64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
it.tDelta = int64(tDelta)
|
|
|
|
|
it.t = it.t + it.tDelta
|
|
|
|
|
|
|
|
|
|
goto ReadValue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// read delta-of-delta
|
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
|
d <<= 1
|
|
|
|
|
bit, err := it.br.readBit()
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if bit == zero {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
d |= 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch d {
|
|
|
|
|
case 0x00:
|
|
|
|
|
// dod == 0
|
|
|
|
|
case 0x02:
|
|
|
|
|
sz = 14
|
|
|
|
|
case 0x06:
|
|
|
|
|
sz = 17
|
|
|
|
|
case 0x0e:
|
|
|
|
|
sz = 20
|
|
|
|
|
case 0x0f:
|
|
|
|
|
bits, err := it.br.readBits(64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dod = int32(bits)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sz != 0 {
|
|
|
|
|
bits, err := it.br.readBits(int(sz))
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if bits > (1 << (sz - 1)) {
|
|
|
|
|
// or something
|
|
|
|
|
bits = bits - (1 << sz)
|
|
|
|
|
}
|
|
|
|
|
dod = int32(bits)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tDelta = it.tDelta + int64(dod)
|
|
|
|
|
|
|
|
|
|
it.tDelta = tDelta
|
|
|
|
|
it.t = it.t + it.tDelta
|
|
|
|
|
|
|
|
|
|
ReadValue:
|
|
|
|
|
// read compressed value
|
|
|
|
|
bit, err := it.br.readBit()
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if bit == zero {
|
|
|
|
|
// it.val = it.val
|
|
|
|
|
} else {
|
|
|
|
|
bit, itErr := it.br.readBit()
|
|
|
|
|
if itErr != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if bit == zero {
|
|
|
|
|
// reuse leading/trailing zero bits
|
|
|
|
|
// it.leading, it.trailing = it.leading, it.trailing
|
|
|
|
|
} else {
|
|
|
|
|
bits, err := it.br.readBits(5)
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
it.leading = uint8(bits)
|
|
|
|
|
|
|
|
|
|
bits, err = it.br.readBits(6)
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
mbits := uint8(bits)
|
|
|
|
|
// 0 significant bits here means we overflowed and we actually need 64; see comment in encoder
|
|
|
|
|
if mbits == 0 {
|
|
|
|
|
mbits = 64
|
|
|
|
|
}
|
|
|
|
|
it.trailing = 64 - it.leading - mbits
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mbits := int(64 - it.leading - it.trailing)
|
|
|
|
|
bits, err := it.br.readBits(mbits)
|
|
|
|
|
if err != nil {
|
|
|
|
|
it.err = err
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
vbits := math.Float64bits(it.val)
|
|
|
|
|
vbits ^= (bits << it.trailing)
|
|
|
|
|
it.val = math.Float64frombits(vbits)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
it.numRead++
|
|
|
|
|
return true
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (it *xorIterator) First() (model.SamplePair, bool) {
|
|
|
|
|
return model.SamplePair{}, false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (it *xorIterator) Seek(ts model.Time) (model.SamplePair, bool) {
|
|
|
|
|
return model.SamplePair{}, false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (it *xorIterator) Next() (model.SamplePair, bool) {
|
|
|
|
|
return model.SamplePair{}, false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (it *xorIterator) Err() error {
|
2016-11-20 05:33:00 -08:00
|
|
|
|
return it.err
|
2016-11-15 01:33:34 -08:00
|
|
|
|
}
|