mirror of https://github.com/prometheus/prometheus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
4.6 KiB
166 lines
4.6 KiB
// Copyright 2017 The Prometheus Authors |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
package chunkenc |
|
|
|
import ( |
|
"fmt" |
|
"math" |
|
"sync" |
|
|
|
"github.com/pkg/errors" |
|
) |
|
|
|
// Encoding is the identifier for a chunk encoding. |
|
type Encoding uint8 |
|
|
|
func (e Encoding) String() string { |
|
switch e { |
|
case EncNone: |
|
return "none" |
|
case EncXOR: |
|
return "XOR" |
|
} |
|
return "<unknown>" |
|
} |
|
|
|
// The different available chunk encodings. |
|
const ( |
|
EncNone Encoding = iota |
|
EncXOR |
|
) |
|
|
|
// Chunk holds a sequence of sample pairs that can be iterated over and appended to. |
|
type Chunk interface { |
|
// Bytes returns the underlying byte slice of the chunk. |
|
Bytes() []byte |
|
|
|
// Encoding returns the encoding type of the chunk. |
|
Encoding() Encoding |
|
|
|
// Appender returns an appender to append samples to the chunk. |
|
Appender() (Appender, error) |
|
|
|
// The iterator passed as argument is for re-use. |
|
// Depending on implementation, the iterator can |
|
// be re-used or a new iterator can be allocated. |
|
Iterator(Iterator) Iterator |
|
|
|
// NumSamples returns the number of samples in the chunk. |
|
NumSamples() int |
|
|
|
// Compact is called whenever a chunk is expected to be complete (no more |
|
// samples appended) and the underlying implementation can eventually |
|
// optimize the chunk. |
|
// There's no strong guarantee that no samples will be appended once |
|
// Compact() is called. Implementing this function is optional. |
|
Compact() |
|
} |
|
|
|
// Appender adds sample pairs to a chunk. |
|
type Appender interface { |
|
Append(int64, float64) |
|
} |
|
|
|
// Iterator is a simple iterator that can only get the next value. |
|
// Iterator iterates over the samples of a time series, in timestamp-increasing order. |
|
type Iterator interface { |
|
// Next advances the iterator by one. |
|
Next() bool |
|
// Seek advances the iterator forward to the first sample with the timestamp equal or greater than t. |
|
// If current sample found by previous `Next` or `Seek` operation already has this property, Seek has no effect. |
|
// Seek returns true, if such sample exists, false otherwise. |
|
// Iterator is exhausted when the Seek returns false. |
|
Seek(t int64) bool |
|
// At returns the current timestamp/value pair. |
|
// Before the iterator has advanced At behaviour is unspecified. |
|
At() (int64, float64) |
|
// Err returns the current error. It should be used only after iterator is |
|
// exhausted, that is `Next` or `Seek` returns false. |
|
Err() error |
|
} |
|
|
|
// NewNopIterator returns a new chunk iterator that does not hold any data. |
|
func NewNopIterator() Iterator { |
|
return nopIterator{} |
|
} |
|
|
|
type nopIterator struct{} |
|
|
|
func (nopIterator) Seek(int64) bool { return false } |
|
func (nopIterator) At() (int64, float64) { return math.MinInt64, 0 } |
|
func (nopIterator) Next() bool { return false } |
|
func (nopIterator) Err() error { return nil } |
|
|
|
// Pool is used to create and reuse chunk references to avoid allocations. |
|
type Pool interface { |
|
Put(Chunk) error |
|
Get(e Encoding, b []byte) (Chunk, error) |
|
} |
|
|
|
// pool is a memory pool of chunk objects. |
|
type pool struct { |
|
xor sync.Pool |
|
} |
|
|
|
// NewPool returns a new pool. |
|
func NewPool() Pool { |
|
return &pool{ |
|
xor: sync.Pool{ |
|
New: func() interface{} { |
|
return &XORChunk{b: bstream{}} |
|
}, |
|
}, |
|
} |
|
} |
|
|
|
func (p *pool) Get(e Encoding, b []byte) (Chunk, error) { |
|
switch e { |
|
case EncXOR: |
|
c := p.xor.Get().(*XORChunk) |
|
c.b.stream = b |
|
c.b.count = 0 |
|
return c, nil |
|
} |
|
return nil, errors.Errorf("invalid encoding %q", e) |
|
} |
|
|
|
func (p *pool) Put(c Chunk) error { |
|
switch c.Encoding() { |
|
case EncXOR: |
|
xc, ok := c.(*XORChunk) |
|
// This may happen often with wrapped chunks. Nothing we can really do about |
|
// it but returning an error would cause a lot of allocations again. Thus, |
|
// we just skip it. |
|
if !ok { |
|
return nil |
|
} |
|
xc.b.stream = nil |
|
xc.b.count = 0 |
|
p.xor.Put(c) |
|
default: |
|
return errors.Errorf("invalid encoding %q", c.Encoding()) |
|
} |
|
return nil |
|
} |
|
|
|
// FromData returns a chunk from a byte slice of chunk data. |
|
// This is there so that users of the library can easily create chunks from |
|
// bytes. |
|
func FromData(e Encoding, d []byte) (Chunk, error) { |
|
switch e { |
|
case EncXOR: |
|
return &XORChunk{b: bstream{count: 0, stream: d}}, nil |
|
} |
|
return nil, fmt.Errorf("unknown chunk encoding: %d", e) |
|
}
|
|
|