--- /dev/null
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package metric
+
+import (
+ "fmt"
+ "sort"
+ "time"
+
+ "golang.org/x/tools/internal/event/keys"
+ "golang.org/x/tools/internal/event/label"
+)
+
+// Data represents a single point in the time series of a metric.
+// This provides the common interface to all metrics no matter their data
+// format.
+// To get the actual values for the metric you must type assert to a concrete
+// metric type.
+type Data interface {
+ // Handle returns the metric handle this data is for.
+ //TODO: rethink the concept of metric handles
+ Handle() string
+ // Groups reports the rows that currently exist for this metric.
+ Groups() [][]label.Label
+}
+
+// Int64Data is a concrete implementation of Data for int64 scalar metrics.
+type Int64Data struct {
+ // Info holds the original construction information.
+ Info *Scalar
+ // IsGauge is true for metrics that track values, rather than increasing over time.
+ IsGauge bool
+ // Rows holds the per group values for the metric.
+ Rows []int64
+ // End is the last time this metric was updated.
+ EndTime time.Time
+
+ groups [][]label.Label
+ key *keys.Int64
+}
+
+// Float64Data is a concrete implementation of Data for float64 scalar metrics.
+type Float64Data struct {
+ // Info holds the original construction information.
+ Info *Scalar
+ // IsGauge is true for metrics that track values, rather than increasing over time.
+ IsGauge bool
+ // Rows holds the per group values for the metric.
+ Rows []float64
+ // End is the last time this metric was updated.
+ EndTime time.Time
+
+ groups [][]label.Label
+ key *keys.Float64
+}
+
+// HistogramInt64Data is a concrete implementation of Data for int64 histogram metrics.
+type HistogramInt64Data struct {
+ // Info holds the original construction information.
+ Info *HistogramInt64
+ // Rows holds the per group values for the metric.
+ Rows []*HistogramInt64Row
+ // End is the last time this metric was updated.
+ EndTime time.Time
+
+ groups [][]label.Label
+ key *keys.Int64
+}
+
+// HistogramInt64Row holds the values for a single row of a HistogramInt64Data.
+type HistogramInt64Row struct {
+ // Values is the counts per bucket.
+ Values []int64
+ // Count is the total count.
+ Count int64
+ // Sum is the sum of all the values recorded.
+ Sum int64
+ // Min is the smallest recorded value.
+ Min int64
+ // Max is the largest recorded value.
+ Max int64
+}
+
+// HistogramFloat64Data is a concrete implementation of Data for float64 histogram metrics.
+type HistogramFloat64Data struct {
+ // Info holds the original construction information.
+ Info *HistogramFloat64
+ // Rows holds the per group values for the metric.
+ Rows []*HistogramFloat64Row
+ // End is the last time this metric was updated.
+ EndTime time.Time
+
+ groups [][]label.Label
+ key *keys.Float64
+}
+
+// HistogramFloat64Row holds the values for a single row of a HistogramFloat64Data.
+type HistogramFloat64Row struct {
+ // Values is the counts per bucket.
+ Values []int64
+ // Count is the total count.
+ Count int64
+ // Sum is the sum of all the values recorded.
+ Sum float64
+ // Min is the smallest recorded value.
+ Min float64
+ // Max is the largest recorded value.
+ Max float64
+}
+
+func labelListEqual(a, b []label.Label) bool {
+ //TODO: make this more efficient
+ return fmt.Sprint(a) == fmt.Sprint(b)
+}
+
+func labelListLess(a, b []label.Label) bool {
+ //TODO: make this more efficient
+ return fmt.Sprint(a) < fmt.Sprint(b)
+}
+
+func getGroup(lm label.Map, g *[][]label.Label, keys []label.Key) (int, bool) {
+ group := make([]label.Label, len(keys))
+ for i, key := range keys {
+ l := lm.Find(key)
+ if l.Valid() {
+ group[i] = l
+ }
+ }
+ old := *g
+ index := sort.Search(len(old), func(i int) bool {
+ return !labelListLess(old[i], group)
+ })
+ if index < len(old) && labelListEqual(group, old[index]) {
+ // not a new group
+ return index, false
+ }
+ *g = make([][]label.Label, len(old)+1)
+ copy(*g, old[:index])
+ copy((*g)[index+1:], old[index:])
+ (*g)[index] = group
+ return index, true
+}
+
+func (data *Int64Data) Handle() string { return data.Info.Name }
+func (data *Int64Data) Groups() [][]label.Label { return data.groups }
+
+func (data *Int64Data) modify(at time.Time, lm label.Map, f func(v int64) int64) Data {
+ index, insert := getGroup(lm, &data.groups, data.Info.Keys)
+ old := data.Rows
+ if insert {
+ data.Rows = make([]int64, len(old)+1)
+ copy(data.Rows, old[:index])
+ copy(data.Rows[index+1:], old[index:])
+ } else {
+ data.Rows = make([]int64, len(old))
+ copy(data.Rows, old)
+ }
+ data.Rows[index] = f(data.Rows[index])
+ data.EndTime = at
+ frozen := *data
+ return &frozen
+}
+
+func (data *Int64Data) count(at time.Time, lm label.Map, l label.Label) Data {
+ return data.modify(at, lm, func(v int64) int64 {
+ return v + 1
+ })
+}
+
+func (data *Int64Data) sum(at time.Time, lm label.Map, l label.Label) Data {
+ return data.modify(at, lm, func(v int64) int64 {
+ return v + data.key.From(l)
+ })
+}
+
+func (data *Int64Data) latest(at time.Time, lm label.Map, l label.Label) Data {
+ return data.modify(at, lm, func(v int64) int64 {
+ return data.key.From(l)
+ })
+}
+
+func (data *Float64Data) Handle() string { return data.Info.Name }
+func (data *Float64Data) Groups() [][]label.Label { return data.groups }
+
+func (data *Float64Data) modify(at time.Time, lm label.Map, f func(v float64) float64) Data {
+ index, insert := getGroup(lm, &data.groups, data.Info.Keys)
+ old := data.Rows
+ if insert {
+ data.Rows = make([]float64, len(old)+1)
+ copy(data.Rows, old[:index])
+ copy(data.Rows[index+1:], old[index:])
+ } else {
+ data.Rows = make([]float64, len(old))
+ copy(data.Rows, old)
+ }
+ data.Rows[index] = f(data.Rows[index])
+ data.EndTime = at
+ frozen := *data
+ return &frozen
+}
+
+func (data *Float64Data) sum(at time.Time, lm label.Map, l label.Label) Data {
+ return data.modify(at, lm, func(v float64) float64 {
+ return v + data.key.From(l)
+ })
+}
+
+func (data *Float64Data) latest(at time.Time, lm label.Map, l label.Label) Data {
+ return data.modify(at, lm, func(v float64) float64 {
+ return data.key.From(l)
+ })
+}
+
+func (data *HistogramInt64Data) Handle() string { return data.Info.Name }
+func (data *HistogramInt64Data) Groups() [][]label.Label { return data.groups }
+
+func (data *HistogramInt64Data) modify(at time.Time, lm label.Map, f func(v *HistogramInt64Row)) Data {
+ index, insert := getGroup(lm, &data.groups, data.Info.Keys)
+ old := data.Rows
+ var v HistogramInt64Row
+ if insert {
+ data.Rows = make([]*HistogramInt64Row, len(old)+1)
+ copy(data.Rows, old[:index])
+ copy(data.Rows[index+1:], old[index:])
+ } else {
+ data.Rows = make([]*HistogramInt64Row, len(old))
+ copy(data.Rows, old)
+ v = *data.Rows[index]
+ }
+ oldValues := v.Values
+ v.Values = make([]int64, len(data.Info.Buckets))
+ copy(v.Values, oldValues)
+ f(&v)
+ data.Rows[index] = &v
+ data.EndTime = at
+ frozen := *data
+ return &frozen
+}
+
+func (data *HistogramInt64Data) record(at time.Time, lm label.Map, l label.Label) Data {
+ return data.modify(at, lm, func(v *HistogramInt64Row) {
+ value := data.key.From(l)
+ v.Sum += value
+ if v.Min > value || v.Count == 0 {
+ v.Min = value
+ }
+ if v.Max < value || v.Count == 0 {
+ v.Max = value
+ }
+ v.Count++
+ for i, b := range data.Info.Buckets {
+ if value <= b {
+ v.Values[i]++
+ }
+ }
+ })
+}
+
+func (data *HistogramFloat64Data) Handle() string { return data.Info.Name }
+func (data *HistogramFloat64Data) Groups() [][]label.Label { return data.groups }
+
+func (data *HistogramFloat64Data) modify(at time.Time, lm label.Map, f func(v *HistogramFloat64Row)) Data {
+ index, insert := getGroup(lm, &data.groups, data.Info.Keys)
+ old := data.Rows
+ var v HistogramFloat64Row
+ if insert {
+ data.Rows = make([]*HistogramFloat64Row, len(old)+1)
+ copy(data.Rows, old[:index])
+ copy(data.Rows[index+1:], old[index:])
+ } else {
+ data.Rows = make([]*HistogramFloat64Row, len(old))
+ copy(data.Rows, old)
+ v = *data.Rows[index]
+ }
+ oldValues := v.Values
+ v.Values = make([]int64, len(data.Info.Buckets))
+ copy(v.Values, oldValues)
+ f(&v)
+ data.Rows[index] = &v
+ data.EndTime = at
+ frozen := *data
+ return &frozen
+}
+
+func (data *HistogramFloat64Data) record(at time.Time, lm label.Map, l label.Label) Data {
+ return data.modify(at, lm, func(v *HistogramFloat64Row) {
+ value := data.key.From(l)
+ v.Sum += value
+ if v.Min > value || v.Count == 0 {
+ v.Min = value
+ }
+ if v.Max < value || v.Count == 0 {
+ v.Max = value
+ }
+ v.Count++
+ for i, b := range data.Info.Buckets {
+ if value <= b {
+ v.Values[i]++
+ }
+ }
+ })
+}