288 lines
6.4 KiB
Cheetah
288 lines
6.4 KiB
Cheetah
package tsm1
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/influxdata/influxdb/tsdb"
|
|
)
|
|
|
|
{{range .}}
|
|
|
|
// {{.Name}}Values represents a slice of {{.Name}} values.
|
|
type {{.Name}}Values []{{.Name}}Value
|
|
|
|
{{if ne .Name ""}}
|
|
func New{{.Name}}ArrayFromValues(v {{.Name}}Values) *tsdb.{{.Name}}Array {
|
|
a := tsdb.New{{.Name}}ArrayLen(len(v))
|
|
for i, val := range v {
|
|
a.Timestamps[i] = val.UnixNano()
|
|
a.Values[i] = val.RawValue()
|
|
}
|
|
return a
|
|
}
|
|
{{end}}
|
|
|
|
func (a {{.Name}}Values) MinTime() int64 {
|
|
return a[0].UnixNano()
|
|
}
|
|
|
|
func (a {{.Name}}Values) MaxTime() int64 {
|
|
return a[len(a)-1].UnixNano()
|
|
}
|
|
|
|
func (a {{.Name}}Values) Size() int {
|
|
sz := 0
|
|
for _, v := range a {
|
|
sz += v.Size()
|
|
}
|
|
return sz
|
|
}
|
|
|
|
// Deduplicate returns a new slice with any values that have the same timestamp removed.
|
|
// The Value that appears last in the slice is the one that is kept. The returned
|
|
// Values are sorted if necessary.
|
|
func (a {{.Name}}Values) Deduplicate() {{.Name}}Values {
|
|
if len(a) <= 1 {
|
|
return a
|
|
}
|
|
|
|
// See if we're already sorted and deduped
|
|
var needSort bool
|
|
for i := 1; i < len(a); i++ {
|
|
if a[i-1].UnixNano() >= a[i].UnixNano() {
|
|
needSort = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !needSort {
|
|
return a
|
|
}
|
|
|
|
sort.Stable(a)
|
|
var i int
|
|
for j := 1; j < len(a); j++ {
|
|
v := a[j]
|
|
if v.UnixNano() != a[i].UnixNano() {
|
|
i++
|
|
}
|
|
a[i] = v
|
|
|
|
}
|
|
return a[:i+1]
|
|
}
|
|
|
|
// Exclude returns the subset of values not in [min, max]. The values must
|
|
// be deduplicated and sorted before calling Exclude or the results are undefined.
|
|
func (a {{.Name}}Values) Exclude(min, max int64) {{.Name}}Values {
|
|
rmin, rmax := a.FindRange(min, max)
|
|
if rmin == -1 && rmax == -1 {
|
|
return a
|
|
}
|
|
|
|
// a[rmin].UnixNano() ≥ min
|
|
// a[rmax].UnixNano() ≥ max
|
|
|
|
if rmax < len(a) {
|
|
if a[rmax].UnixNano() == max {
|
|
rmax++
|
|
}
|
|
rest := len(a)-rmax
|
|
if rest > 0 {
|
|
b := a[:rmin+rest]
|
|
copy(b[rmin:], a[rmax:])
|
|
return b
|
|
}
|
|
}
|
|
|
|
return a[:rmin]
|
|
}
|
|
|
|
// Include returns the subset values between min and max inclusive. The values must
|
|
// be deduplicated and sorted before calling Exclude or the results are undefined.
|
|
func (a {{.Name}}Values) Include(min, max int64) {{.Name}}Values {
|
|
rmin, rmax := a.FindRange(min, max)
|
|
if rmin == -1 && rmax == -1 {
|
|
return nil
|
|
}
|
|
|
|
// a[rmin].UnixNano() ≥ min
|
|
// a[rmax].UnixNano() ≥ max
|
|
|
|
if rmax < len(a) && a[rmax].UnixNano() == max {
|
|
rmax++
|
|
}
|
|
|
|
if rmin > -1 {
|
|
b := a[:rmax-rmin]
|
|
copy(b, a[rmin:rmax])
|
|
return b
|
|
}
|
|
|
|
return a[:rmax]
|
|
}
|
|
|
|
// search performs a binary search for UnixNano() v in a
|
|
// and returns the position, i, where v would be inserted.
|
|
// An additional check of a[i].UnixNano() == v is necessary
|
|
// to determine if the value v exists.
|
|
func (a {{.Name}}Values) search(v int64) int {
|
|
// Define: f(x) → a[x].UnixNano() < v
|
|
// Define: f(-1) == true, f(n) == false
|
|
// Invariant: f(lo-1) == true, f(hi) == false
|
|
lo := 0
|
|
hi := len(a)
|
|
for lo < hi {
|
|
mid := int(uint(lo+hi) >> 1)
|
|
if a[mid].UnixNano() < v {
|
|
lo = mid + 1 // preserves f(lo-1) == true
|
|
} else {
|
|
hi = mid // preserves f(hi) == false
|
|
}
|
|
}
|
|
|
|
// lo == hi
|
|
return lo
|
|
}
|
|
|
|
// FindRange returns the positions where min and max would be
|
|
// inserted into the array. If a[0].UnixNano() > max or
|
|
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
|
|
// indicating the array is outside the [min, max]. The values must
|
|
// be deduplicated and sorted before calling Exclude or the results
|
|
// are undefined.
|
|
func (a {{.Name}}Values) FindRange(min, max int64) (int, int) {
|
|
if len(a) == 0 || min > max {
|
|
return -1, -1
|
|
}
|
|
|
|
minVal := a[0].UnixNano()
|
|
maxVal := a[len(a)-1].UnixNano()
|
|
|
|
if maxVal < min || minVal > max {
|
|
return -1, -1
|
|
}
|
|
|
|
return a.search(min), a.search(max)
|
|
}
|
|
|
|
// Merge overlays b to top of a. If two values conflict with
|
|
// the same timestamp, b is used. Both a and b must be sorted
|
|
// in ascending order.
|
|
func (a {{.Name}}Values) Merge(b {{.Name}}Values) {{.Name}}Values {
|
|
if len(a) == 0 {
|
|
return b
|
|
}
|
|
|
|
if len(b) == 0 {
|
|
return a
|
|
}
|
|
|
|
// Normally, both a and b should not contain duplicates. Due to a bug in older versions, it's
|
|
// possible stored blocks might contain duplicate values. Remove them if they exists before
|
|
// merging.
|
|
a = a.Deduplicate()
|
|
b = b.Deduplicate()
|
|
|
|
if a[len(a)-1].UnixNano() < b[0].UnixNano() {
|
|
return append(a, b...)
|
|
}
|
|
|
|
if b[len(b)-1].UnixNano() < a[0].UnixNano() {
|
|
return append(b, a...)
|
|
}
|
|
|
|
out := make({{.Name}}Values, 0, len(a)+len(b))
|
|
for len(a) > 0 && len(b) > 0 {
|
|
if a[0].UnixNano() < b[0].UnixNano() {
|
|
out, a = append(out, a[0]), a[1:]
|
|
} else if len(b) > 0 && a[0].UnixNano() == b[0].UnixNano() {
|
|
a = a[1:]
|
|
} else {
|
|
out, b = append(out, b[0]), b[1:]
|
|
}
|
|
}
|
|
if len(a) > 0 {
|
|
return append(out, a...)
|
|
}
|
|
return append(out, b...)
|
|
}
|
|
|
|
{{ if ne .Name "" }}
|
|
func (a {{.Name}}Values) Encode(buf []byte) ([]byte, error) {
|
|
return encode{{.Name}}ValuesBlock(buf, a)
|
|
}
|
|
|
|
func Encode{{ .Name }}ArrayBlock(a *tsdb.{{ .Name }}Array, b []byte) ([]byte, error) {
|
|
if a.Len() == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// TODO(edd): These need to be pooled.
|
|
var vb []byte
|
|
var tb []byte
|
|
var err error
|
|
|
|
if vb, err = {{ .Name }}ArrayEncodeAll(a.Values, vb); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if tb, err = TimeArrayEncodeAll(a.Timestamps, tb); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Prepend the first timestamp of the block in the first 8 bytes and the block
|
|
// in the next byte, followed by the block
|
|
return packBlock(b, {{ .Type }}, tb, vb), nil
|
|
}
|
|
|
|
func encode{{ .Name }}ValuesBlock(buf []byte, values []{{.Name}}Value) ([]byte, error) {
|
|
if len(values) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
venc := get{{ .Name }}Encoder(len(values))
|
|
tsenc := getTimeEncoder(len(values))
|
|
|
|
var b []byte
|
|
err := func() error {
|
|
for _, v := range values {
|
|
tsenc.Write(v.UnixNano())
|
|
venc.Write({{if .CastType}}{{.CastType}}(v.RawValue()){{else}}v.RawValue(){{end}})
|
|
}
|
|
venc.Flush()
|
|
|
|
// Encoded timestamp values
|
|
tb, err := tsenc.Bytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Encoded values
|
|
vb, err := venc.Bytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Prepend the first timestamp of the block in the first 8 bytes and the block
|
|
// in the next byte, followed by the block
|
|
b = packBlock(buf, {{ .Type }}, tb, vb)
|
|
|
|
return nil
|
|
}()
|
|
|
|
putTimeEncoder(tsenc)
|
|
put{{.Name}}Encoder(venc)
|
|
|
|
return b, err
|
|
}
|
|
|
|
{{ end }}
|
|
|
|
// Sort methods
|
|
func (a {{.Name}}Values) Len() int { return len(a) }
|
|
func (a {{.Name}}Values) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a {{.Name}}Values) Less(i, j int) bool { return a[i].UnixNano() < a[j].UnixNano() }
|
|
|
|
|
|
{{ end }}
|