func MarshalJSON(results []*MetricData) []byte {
	var b []byte
	b = append(b, '[')
	var topComma bool
	for _, r := range results {
		if r == nil {
			continue
		}
		if topComma {
			b = append(b, ',')
		}
		topComma = true
		b = append(b, `{"target":`...)
		b = strconv.AppendQuoteToASCII(b, r.Name)
		b = append(b, `,"datapoints":[`...)
		var innerComma bool
		t := r.StartTime
		for _, v := range r.AggregatedValues() {
			if innerComma {
				b = append(b, ',')
			}
			innerComma = true
			b = append(b, '[')
			if math.IsInf(v, 0) || math.IsNaN(v) {
				b = append(b, "null"...)
			} else {
				b = strconv.AppendFloat(b, v, 'f', -1, 64)
			}
			b = append(b, ',')
			b = strconv.AppendInt(b, t, 10)
			b = append(b, ']')
			t += r.AggregatedTimeStep()
		}
		b = append(b, `]}`...)
	}
	b = append(b, ']')
	return b
}
MarshalJSON marshals metric data to JSON
package types
import (
	"bytes"
	"errors"
	"fmt"
	"math"
	"runtime/debug"
	"strconv"
	"strings"
	"time"
	pb "github.com/go-graphite/protocol/carbonapi_v3_pb"
	pickle "github.com/lomik/og-rek"
)
var (
	ErrWildcardNotAllowed = errors.New("found wildcard where series expected")
	ErrTooManyArguments = errors.New("too many arguments")
)
type MetricData struct {
	pb.FetchResponse
	GraphOptions
	ValuesPerPoint    int
	aggregatedValues  []float64
	AggregateFunction func([]float64) float64 `json:"-"`
}
func MakeMetricData(name string, values []float64, step, start int64) *MetricData {
	stop := start + int64(len(values))*step
	return &MetricData{FetchResponse: pb.FetchResponse{
		Name:      name,
		Values:    values,
		StartTime: start,
		StepTime:  step,
		StopTime:  stop,
	}}
}
func MarshalCSV(results []*MetricData) []byte {
	var b []byte
	for _, r := range results {
		step := r.StepTime
		t := r.StartTime
		for _, v := range r.Values {
			b = append(b, '"')
			b = append(b, r.Name...)
			b = append(b, '"')
			b = append(b, ',')
			b = append(b, time.Unix(t, 0).Format("2006-01-02 15:04:05")...)
			b = append(b, ',')
			if !math.IsNaN(v) {
				b = strconv.AppendFloat(b, v, 'f', -1, 64)
			}
			b = append(b, '\n')
			t += step
		}
	}
	return b
}
func ConsolidateJSON(maxDataPoints int, results []*MetricData) {
	startTime := results[0].StartTime
	endTime := results[0].StopTime
	for _, r := range results {
		t := r.StartTime
		if startTime > t {
			startTime = t
		}
		t = r.StopTime
		if endTime < t {
			endTime = t
		}
	}
	timeRange := endTime - startTime
	if timeRange <= 0 {
		return
	}
	for _, r := range results {
		numberOfDataPoints := math.Floor(float64(timeRange) / float64(r.StepTime))
		if numberOfDataPoints > float64(maxDataPoints) {
			valuesPerPoint := math.Ceil(numberOfDataPoints / float64(maxDataPoints))
			r.SetValuesPerPoint(int(valuesPerPoint))
		}
	}
}