2016-09-02 14:52:11 +00:00
|
|
|
package tsi1
|
|
|
|
|
2016-09-26 15:56:12 +00:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"io"
|
2016-10-18 14:34:51 +00:00
|
|
|
"sort"
|
2016-09-26 15:56:12 +00:00
|
|
|
|
|
|
|
"github.com/influxdata/influxdb/pkg/rhh"
|
|
|
|
)
|
|
|
|
|
|
|
|
// MeasurementBlockVersion is the version of the measurement block.
|
|
|
|
const MeasurementBlockVersion = 1
|
|
|
|
|
|
|
|
// Measurement flag constants.
|
|
|
|
const (
|
|
|
|
MeasurementTombstoneFlag = 0x01
|
|
|
|
)
|
|
|
|
|
|
|
|
// Measurement field size constants.
|
|
|
|
const (
|
2016-10-18 14:34:51 +00:00
|
|
|
// 1 byte offset for the block to ensure non-zero offsets.
|
|
|
|
MeasurementFillSize = 1
|
|
|
|
|
2016-09-26 15:56:12 +00:00
|
|
|
// Measurement trailer fields
|
2016-11-17 18:57:13 +00:00
|
|
|
MeasurementTrailerSize = 0 +
|
|
|
|
2 + // version
|
|
|
|
8 + 8 + // data offset/size
|
|
|
|
8 + 8 // hash index offset/size
|
2016-09-26 15:56:12 +00:00
|
|
|
|
|
|
|
// Measurement key block fields.
|
|
|
|
MeasurementNSize = 4
|
|
|
|
MeasurementOffsetSize = 8
|
|
|
|
)
|
|
|
|
|
|
|
|
// Measurement errors.
|
|
|
|
var (
|
|
|
|
ErrUnsupportedMeasurementBlockVersion = errors.New("unsupported meaurement block version")
|
|
|
|
ErrMeasurementBlockSizeMismatch = errors.New("meaurement block size mismatch")
|
|
|
|
)
|
|
|
|
|
|
|
|
// MeasurementBlock represents a collection of all measurements in an index.
|
|
|
|
type MeasurementBlock struct {
|
|
|
|
data []byte
|
|
|
|
hashData []byte
|
|
|
|
|
|
|
|
version int // block version
|
|
|
|
}
|
|
|
|
|
|
|
|
// Version returns the encoding version parsed from the data.
|
|
|
|
// Only valid after UnmarshalBinary() has been successfully invoked.
|
|
|
|
func (blk *MeasurementBlock) Version() int { return blk.version }
|
|
|
|
|
|
|
|
// Elem returns an element for a measurement.
|
2016-10-21 15:31:40 +00:00
|
|
|
func (blk *MeasurementBlock) Elem(name []byte) (e MeasurementBlockElem, ok bool) {
|
2016-09-26 15:56:12 +00:00
|
|
|
n := binary.BigEndian.Uint32(blk.hashData[:MeasurementNSize])
|
|
|
|
hash := hashKey(name)
|
2016-10-07 17:29:22 +00:00
|
|
|
pos := int(hash % n)
|
2016-09-26 15:56:12 +00:00
|
|
|
|
|
|
|
// Track current distance
|
|
|
|
var d int
|
|
|
|
|
|
|
|
for {
|
|
|
|
// Find offset of measurement.
|
|
|
|
offset := binary.BigEndian.Uint64(blk.hashData[MeasurementNSize+(pos*MeasurementOffsetSize):])
|
|
|
|
|
|
|
|
// Evaluate name if offset is not empty.
|
|
|
|
if offset > 0 {
|
|
|
|
// Parse into element.
|
2016-10-21 15:31:40 +00:00
|
|
|
var e MeasurementBlockElem
|
2016-09-26 15:56:12 +00:00
|
|
|
e.UnmarshalBinary(blk.data[offset:])
|
|
|
|
|
|
|
|
// Return if name match.
|
2016-10-31 14:46:07 +00:00
|
|
|
if bytes.Equal(e.name, name) {
|
2016-09-26 15:56:12 +00:00
|
|
|
return e, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we've exceeded the probe distance.
|
2016-10-31 14:46:07 +00:00
|
|
|
if d > dist(hashKey(e.name), pos, int(n)) {
|
2016-10-21 15:31:40 +00:00
|
|
|
return MeasurementBlockElem{}, false
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move position forward.
|
|
|
|
pos = (pos + 1) % int(n)
|
|
|
|
d++
|
2016-10-25 14:36:58 +00:00
|
|
|
|
|
|
|
if uint32(d) > n {
|
|
|
|
panic("empty hash data block")
|
|
|
|
}
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalBinary unpacks data into the block. Block is not copied so data
|
|
|
|
// should be retained and unchanged after being passed into this function.
|
|
|
|
func (blk *MeasurementBlock) UnmarshalBinary(data []byte) error {
|
2016-10-03 15:08:43 +00:00
|
|
|
// Read trailer.
|
|
|
|
t, err := ReadMeasurementBlockTrailer(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
2016-10-03 15:08:43 +00:00
|
|
|
|
|
|
|
// Save data section.
|
|
|
|
blk.data = data[t.Data.Offset:]
|
|
|
|
blk.data = blk.data[:t.Data.Size]
|
|
|
|
|
|
|
|
// Save hash index block.
|
|
|
|
blk.hashData = data[t.HashIndex.Offset:]
|
|
|
|
blk.hashData = blk.hashData[:t.HashIndex.Size]
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-10-18 14:34:51 +00:00
|
|
|
// Iterator returns an iterator over all measurements.
|
|
|
|
func (blk *MeasurementBlock) Iterator() MeasurementIterator {
|
2016-10-21 15:31:40 +00:00
|
|
|
return &blockMeasurementIterator{data: blk.data[MeasurementFillSize:]}
|
2016-10-18 14:34:51 +00:00
|
|
|
}
|
|
|
|
|
2016-11-02 16:09:49 +00:00
|
|
|
// seriesIDIterator returns an iterator for all series ids in a measurement.
|
|
|
|
func (blk *MeasurementBlock) seriesIDIterator(name []byte) seriesIDIterator {
|
|
|
|
// Find measurement element.
|
|
|
|
e, ok := blk.Elem(name)
|
|
|
|
if !ok {
|
|
|
|
return &rawSeriesIDIterator{}
|
|
|
|
}
|
|
|
|
return &rawSeriesIDIterator{data: e.series.data}
|
|
|
|
}
|
|
|
|
|
2016-10-21 15:31:40 +00:00
|
|
|
// blockMeasurementIterator iterates over a list measurements in a block.
|
|
|
|
type blockMeasurementIterator struct {
|
2016-10-31 14:46:07 +00:00
|
|
|
elem MeasurementBlockElem
|
2016-10-18 14:34:51 +00:00
|
|
|
data []byte
|
|
|
|
}
|
|
|
|
|
2016-10-21 15:31:40 +00:00
|
|
|
// Next returns the next measurement. Returns nil when iterator is complete.
|
2016-10-31 14:46:07 +00:00
|
|
|
func (itr *blockMeasurementIterator) Next() MeasurementElem {
|
2016-10-18 14:34:51 +00:00
|
|
|
// Return nil when we run out of data.
|
|
|
|
if len(itr.data) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal the element at the current position.
|
2016-10-31 14:46:07 +00:00
|
|
|
itr.elem.UnmarshalBinary(itr.data)
|
2016-10-18 14:34:51 +00:00
|
|
|
|
|
|
|
// Move the data forward past the record.
|
2016-10-31 14:46:07 +00:00
|
|
|
itr.data = itr.data[itr.elem.size:]
|
2016-10-18 14:34:51 +00:00
|
|
|
|
|
|
|
return &itr.elem
|
|
|
|
}
|
|
|
|
|
2016-11-02 16:09:49 +00:00
|
|
|
// rawSeriesIterator iterates over a list of raw series data.
|
|
|
|
type rawSeriesIDIterator struct {
|
|
|
|
data []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// next returns the next decoded series.
|
|
|
|
func (itr *rawSeriesIDIterator) next() uint32 {
|
|
|
|
if len(itr.data) == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
id := binary.BigEndian.Uint32(itr.data)
|
|
|
|
itr.data = itr.data[SeriesIDSize:]
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
// MeasurementBlockTrailer represents meta data at the end of a MeasurementBlock.
|
|
|
|
type MeasurementBlockTrailer struct {
|
|
|
|
Version int // Encoding version
|
|
|
|
|
|
|
|
// Offset & size of data section.
|
|
|
|
Data struct {
|
|
|
|
Offset int64
|
|
|
|
Size int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// Offset & size of hash map section.
|
|
|
|
HashIndex struct {
|
|
|
|
Offset int64
|
|
|
|
Size int64
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadMeasurementBlockTrailer returns the block trailer from data.
|
2016-10-03 15:08:43 +00:00
|
|
|
func ReadMeasurementBlockTrailer(data []byte) (MeasurementBlockTrailer, error) {
|
|
|
|
var t MeasurementBlockTrailer
|
|
|
|
|
|
|
|
// Read version.
|
2016-11-17 18:57:13 +00:00
|
|
|
t.Version = int(binary.BigEndian.Uint16(data[len(data)-2:]))
|
2016-10-03 15:08:43 +00:00
|
|
|
if t.Version != MeasurementBlockVersion {
|
2016-11-17 18:57:13 +00:00
|
|
|
return t, ErrUnsupportedIndexFileVersion
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
// Slice trailer data.
|
|
|
|
buf := data[len(data)-IndexFileTrailerSize:]
|
2016-09-26 15:56:12 +00:00
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
// Read data section info.
|
|
|
|
t.Data.Offset, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
|
|
|
t.Data.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
2016-09-26 15:56:12 +00:00
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
// Read measurement block info.
|
|
|
|
t.HashIndex.Offset, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
|
|
|
t.HashIndex.Size, buf = int64(binary.BigEndian.Uint64(buf[0:8])), buf[8:]
|
2016-09-26 15:56:12 +00:00
|
|
|
|
2016-10-03 15:08:43 +00:00
|
|
|
return t, nil
|
|
|
|
}
|
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
// WriteTo writes the trailer to w.
|
|
|
|
func (t *MeasurementBlockTrailer) WriteTo(w io.Writer) (n int64, err error) {
|
|
|
|
// Write data section info.
|
|
|
|
if err := writeUint64To(w, uint64(t.Data.Offset), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
} else if err := writeUint64To(w, uint64(t.Data.Size), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
2016-10-03 15:08:43 +00:00
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
// Write hash index section info.
|
|
|
|
if err := writeUint64To(w, uint64(t.HashIndex.Offset), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
} else if err := writeUint64To(w, uint64(t.HashIndex.Size), &n); err != nil {
|
|
|
|
return n, err
|
2016-10-03 15:08:43 +00:00
|
|
|
}
|
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
// Write index file encoding version.
|
|
|
|
if err := writeUint16To(w, MeasurementBlockVersion, &n); err != nil {
|
|
|
|
return n, err
|
2016-10-03 15:08:43 +00:00
|
|
|
}
|
2016-11-17 18:57:13 +00:00
|
|
|
|
|
|
|
return n, nil
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
|
|
|
|
2016-10-21 15:31:40 +00:00
|
|
|
// MeasurementBlockElem represents an internal measurement element.
|
|
|
|
type MeasurementBlockElem struct {
|
2016-10-31 14:46:07 +00:00
|
|
|
flag byte // flag
|
|
|
|
name []byte // measurement name
|
2016-10-05 15:04:04 +00:00
|
|
|
|
2016-11-02 16:09:49 +00:00
|
|
|
tagBlock struct {
|
2016-10-31 14:46:07 +00:00
|
|
|
offset int64
|
|
|
|
size int64
|
2016-10-05 15:04:04 +00:00
|
|
|
}
|
2016-09-26 15:56:12 +00:00
|
|
|
|
2016-10-31 14:46:07 +00:00
|
|
|
series struct {
|
|
|
|
n uint32 // series count
|
|
|
|
data []byte // serialized series data
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
2016-10-18 14:34:51 +00:00
|
|
|
|
2016-10-31 14:46:07 +00:00
|
|
|
// size in bytes, set after unmarshaling.
|
|
|
|
size int
|
2016-10-18 14:34:51 +00:00
|
|
|
}
|
|
|
|
|
2016-10-31 14:46:07 +00:00
|
|
|
// Name returns the measurement name.
|
|
|
|
func (e *MeasurementBlockElem) Name() []byte { return e.name }
|
|
|
|
|
2016-10-18 14:34:51 +00:00
|
|
|
// Deleted returns true if the tombstone flag is set.
|
2016-10-21 15:31:40 +00:00
|
|
|
func (e *MeasurementBlockElem) Deleted() bool {
|
2016-10-31 14:46:07 +00:00
|
|
|
return (e.flag & MeasurementTombstoneFlag) != 0
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
|
|
|
|
2016-11-02 16:09:49 +00:00
|
|
|
// TagBlockOffset returns the offset of the measurement's tag block.
|
|
|
|
func (e *MeasurementBlockElem) TagBlockOffset() int64 { return e.tagBlock.offset }
|
2016-10-31 14:46:07 +00:00
|
|
|
|
2016-11-02 16:09:49 +00:00
|
|
|
// TagBlockSize returns the size of the measurement's tag block.
|
|
|
|
func (e *MeasurementBlockElem) TagBlockSize() int64 { return e.tagBlock.size }
|
2016-10-31 14:46:07 +00:00
|
|
|
|
2016-09-26 15:56:12 +00:00
|
|
|
// SeriesID returns series ID at an index.
|
2016-10-21 15:31:40 +00:00
|
|
|
func (e *MeasurementBlockElem) SeriesID(i int) uint32 {
|
2016-10-31 14:46:07 +00:00
|
|
|
return binary.BigEndian.Uint32(e.series.data[i*SeriesIDSize:])
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SeriesIDs returns a list of decoded series ids.
|
2016-10-21 15:31:40 +00:00
|
|
|
func (e *MeasurementBlockElem) SeriesIDs() []uint32 {
|
2016-10-31 14:46:07 +00:00
|
|
|
a := make([]uint32, e.series.n)
|
|
|
|
for i := 0; i < int(e.series.n); i++ {
|
2016-09-26 15:56:12 +00:00
|
|
|
a[i] = e.SeriesID(i)
|
|
|
|
}
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalBinary unmarshals data into e.
|
2016-10-21 15:31:40 +00:00
|
|
|
func (e *MeasurementBlockElem) UnmarshalBinary(data []byte) error {
|
2016-10-18 14:34:51 +00:00
|
|
|
start := len(data)
|
|
|
|
|
2016-09-26 15:56:12 +00:00
|
|
|
// Parse flag data.
|
2016-10-31 14:46:07 +00:00
|
|
|
e.flag, data = data[0], data[1:]
|
2016-09-26 15:56:12 +00:00
|
|
|
|
2016-11-02 16:09:49 +00:00
|
|
|
// Parse tag block offset.
|
|
|
|
e.tagBlock.offset, data = int64(binary.BigEndian.Uint64(data)), data[8:]
|
|
|
|
e.tagBlock.size, data = int64(binary.BigEndian.Uint64(data)), data[8:]
|
2016-09-26 15:56:12 +00:00
|
|
|
|
|
|
|
// Parse name.
|
|
|
|
sz, n := binary.Uvarint(data)
|
2016-10-31 14:46:07 +00:00
|
|
|
e.name, data = data[n:n+int(sz)], data[n+int(sz):]
|
2016-09-26 15:56:12 +00:00
|
|
|
|
|
|
|
// Parse series data.
|
|
|
|
v, n := binary.Uvarint(data)
|
2016-10-31 14:46:07 +00:00
|
|
|
e.series.n, data = uint32(v), data[n:]
|
|
|
|
e.series.data, data = data[:e.series.n*SeriesIDSize], data[e.series.n*SeriesIDSize:]
|
2016-10-18 14:34:51 +00:00
|
|
|
|
|
|
|
// Save length of elem.
|
2016-10-31 14:46:07 +00:00
|
|
|
e.size = start - len(data)
|
2016-09-26 15:56:12 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MeasurementBlockWriter writes a measurement block.
|
|
|
|
type MeasurementBlockWriter struct {
|
|
|
|
mms map[string]measurement
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewMeasurementBlockWriter returns a new MeasurementBlockWriter.
|
|
|
|
func NewMeasurementBlockWriter() *MeasurementBlockWriter {
|
|
|
|
return &MeasurementBlockWriter{
|
|
|
|
mms: make(map[string]measurement),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-05 15:04:04 +00:00
|
|
|
// Add adds a measurement with series and tag set offset/size.
|
|
|
|
func (mw *MeasurementBlockWriter) Add(name []byte, offset, size int64, seriesIDs []uint32) {
|
2016-09-26 15:56:12 +00:00
|
|
|
mm := mw.mms[string(name)]
|
2016-11-02 16:09:49 +00:00
|
|
|
mm.tagBlock.offset = offset
|
|
|
|
mm.tagBlock.size = size
|
2016-09-26 15:56:12 +00:00
|
|
|
mm.seriesIDs = seriesIDs
|
|
|
|
mw.mms[string(name)] = mm
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete marks a measurement as tombstoned.
|
|
|
|
func (mw *MeasurementBlockWriter) Delete(name []byte) {
|
|
|
|
mm := mw.mms[string(name)]
|
|
|
|
mm.deleted = true
|
|
|
|
mw.mms[string(name)] = mm
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteTo encodes the measurements to w.
|
|
|
|
func (mw *MeasurementBlockWriter) WriteTo(w io.Writer) (n int64, err error) {
|
2016-11-17 18:57:13 +00:00
|
|
|
var t MeasurementBlockTrailer
|
2016-09-26 15:56:12 +00:00
|
|
|
|
2016-10-18 14:34:51 +00:00
|
|
|
// Sort names.
|
|
|
|
names := make([]string, 0, len(mw.mms))
|
2016-09-26 15:56:12 +00:00
|
|
|
for name := range mw.mms {
|
2016-10-18 14:34:51 +00:00
|
|
|
names = append(names, name)
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
2016-10-18 14:34:51 +00:00
|
|
|
sort.Strings(names)
|
2016-09-26 15:56:12 +00:00
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
// Begin data section.
|
|
|
|
t.Data.Offset = n
|
|
|
|
|
|
|
|
// Write padding byte so no offsets are zero.
|
|
|
|
if err := writeUint8To(w, 0, &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
2016-09-26 15:56:12 +00:00
|
|
|
// Encode key list.
|
2016-10-18 14:34:51 +00:00
|
|
|
for _, name := range names {
|
|
|
|
// Retrieve measurement and save offset.
|
|
|
|
mm := mw.mms[name]
|
|
|
|
mm.offset = n
|
|
|
|
mw.mms[name] = mm
|
2016-09-26 15:56:12 +00:00
|
|
|
|
|
|
|
// Write measurement
|
2016-10-18 14:34:51 +00:00
|
|
|
if err := mw.writeMeasurementTo(w, []byte(name), &mm, &n); err != nil {
|
2016-09-26 15:56:12 +00:00
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
}
|
2016-11-17 18:57:13 +00:00
|
|
|
t.Data.Size = n - t.Data.Offset
|
2016-09-26 15:56:12 +00:00
|
|
|
|
2016-10-18 14:34:51 +00:00
|
|
|
// Build key hash map
|
|
|
|
m := rhh.NewHashMap(rhh.Options{
|
2016-10-25 14:36:58 +00:00
|
|
|
Capacity: len(names),
|
2016-10-18 14:34:51 +00:00
|
|
|
LoadFactor: 90,
|
|
|
|
})
|
|
|
|
for name := range mw.mms {
|
|
|
|
mm := mw.mms[name]
|
|
|
|
m.Put([]byte(name), &mm)
|
|
|
|
}
|
|
|
|
|
2016-11-17 18:57:13 +00:00
|
|
|
t.HashIndex.Offset = n
|
|
|
|
|
2016-09-26 15:56:12 +00:00
|
|
|
// Encode hash map length.
|
|
|
|
if err := writeUint32To(w, uint32(m.Cap()), &n); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Encode hash map offset entries.
|
2016-10-18 14:34:51 +00:00
|
|
|
for i := 0; i < m.Cap(); i++ {
|
|
|
|
_, v := m.Elem(i)
|
|
|
|
|
|
|
|
var offset int64
|
|
|
|
if mm, ok := v.(*measurement); ok {
|
|
|
|
offset = mm.offset
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := writeUint64To(w, uint64(offset), &n); err != nil {
|
2016-09-26 15:56:12 +00:00
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
}
|
2016-11-17 18:57:13 +00:00
|
|
|
t.HashIndex.Size = n - t.HashIndex.Offset
|
2016-09-26 15:56:12 +00:00
|
|
|
|
|
|
|
// Write trailer.
|
2016-11-17 18:57:13 +00:00
|
|
|
nn, err := t.WriteTo(w)
|
|
|
|
n += nn
|
|
|
|
if err != nil {
|
2016-09-26 15:56:12 +00:00
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// writeMeasurementTo encodes a single measurement entry into w.
|
|
|
|
func (mw *MeasurementBlockWriter) writeMeasurementTo(w io.Writer, name []byte, mm *measurement, n *int64) error {
|
2016-11-02 16:09:49 +00:00
|
|
|
// Write flag & tag block offset.
|
2016-09-26 15:56:12 +00:00
|
|
|
if err := writeUint8To(w, mm.flag(), n); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-11-02 16:09:49 +00:00
|
|
|
if err := writeUint64To(w, uint64(mm.tagBlock.offset), n); err != nil {
|
2016-10-05 15:04:04 +00:00
|
|
|
return err
|
2016-11-02 16:09:49 +00:00
|
|
|
} else if err := writeUint64To(w, uint64(mm.tagBlock.size), n); err != nil {
|
2016-09-26 15:56:12 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write measurement name.
|
|
|
|
if err := writeUvarintTo(w, uint64(len(name)), n); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := writeTo(w, name, n); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write series count & ids.
|
|
|
|
if err := writeUvarintTo(w, uint64(len(mm.seriesIDs)), n); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, seriesID := range mm.seriesIDs {
|
|
|
|
if err := writeUint32To(w, seriesID, n); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type measurement struct {
|
2016-11-02 16:09:49 +00:00
|
|
|
deleted bool
|
|
|
|
tagBlock struct {
|
2016-10-05 15:04:04 +00:00
|
|
|
offset int64
|
|
|
|
size int64
|
|
|
|
}
|
2016-09-26 15:56:12 +00:00
|
|
|
seriesIDs []uint32
|
2016-10-18 14:34:51 +00:00
|
|
|
offset int64
|
2016-09-26 15:56:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (mm measurement) flag() byte {
|
|
|
|
var flag byte
|
|
|
|
if mm.deleted {
|
|
|
|
flag |= MeasurementTombstoneFlag
|
|
|
|
}
|
|
|
|
return flag
|
|
|
|
}
|