|
@@ -14,13 +14,45 @@
|
|
package expfmt
|
|
package expfmt
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
+ "bufio"
|
|
"fmt"
|
|
"fmt"
|
|
"io"
|
|
"io"
|
|
|
|
+ "io/ioutil"
|
|
"math"
|
|
"math"
|
|
|
|
+ "strconv"
|
|
"strings"
|
|
"strings"
|
|
|
|
+ "sync"
|
|
|
|
|
|
- dto "github.com/prometheus/client_model/go"
|
|
|
|
"github.com/prometheus/common/model"
|
|
"github.com/prometheus/common/model"
|
|
|
|
+
|
|
|
|
+ dto "github.com/prometheus/client_model/go"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+// enhancedWriter has all the enhanced write functions needed here. bufio.Writer
|
|
|
|
+// implements it.
|
|
|
|
+type enhancedWriter interface {
|
|
|
|
+ io.Writer
|
|
|
|
+ WriteRune(r rune) (n int, err error)
|
|
|
|
+ WriteString(s string) (n int, err error)
|
|
|
|
+ WriteByte(c byte) error
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const (
|
|
|
|
+ initialNumBufSize = 24
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+var (
|
|
|
|
+ bufPool = sync.Pool{
|
|
|
|
+ New: func() interface{} {
|
|
|
|
+ return bufio.NewWriter(ioutil.Discard)
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
+ numBufPool = sync.Pool{
|
|
|
|
+ New: func() interface{} {
|
|
|
|
+ b := make([]byte, 0, initialNumBufSize)
|
|
|
|
+ return &b
|
|
|
|
+ },
|
|
|
|
+ }
|
|
)
|
|
)
|
|
|
|
|
|
// MetricFamilyToText converts a MetricFamily proto message into text format and
|
|
// MetricFamilyToText converts a MetricFamily proto message into text format and
|
|
@@ -32,37 +64,90 @@ import (
|
|
// will result in invalid text format output.
|
|
// will result in invalid text format output.
|
|
//
|
|
//
|
|
// This method fulfills the type 'prometheus.encoder'.
|
|
// This method fulfills the type 'prometheus.encoder'.
|
|
-func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
|
|
|
- var written int
|
|
|
|
-
|
|
|
|
|
|
+func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error) {
|
|
// Fail-fast checks.
|
|
// Fail-fast checks.
|
|
if len(in.Metric) == 0 {
|
|
if len(in.Metric) == 0 {
|
|
- return written, fmt.Errorf("MetricFamily has no metrics: %s", in)
|
|
|
|
|
|
+ return 0, fmt.Errorf("MetricFamily has no metrics: %s", in)
|
|
}
|
|
}
|
|
name := in.GetName()
|
|
name := in.GetName()
|
|
if name == "" {
|
|
if name == "" {
|
|
- return written, fmt.Errorf("MetricFamily has no name: %s", in)
|
|
|
|
|
|
+ return 0, fmt.Errorf("MetricFamily has no name: %s", in)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Try the interface upgrade. If it doesn't work, we'll use a
|
|
|
|
+ // bufio.Writer from the sync.Pool.
|
|
|
|
+ w, ok := out.(enhancedWriter)
|
|
|
|
+ if !ok {
|
|
|
|
+ b := bufPool.Get().(*bufio.Writer)
|
|
|
|
+ b.Reset(out)
|
|
|
|
+ w = b
|
|
|
|
+ defer func() {
|
|
|
|
+ bErr := b.Flush()
|
|
|
|
+ if err == nil {
|
|
|
|
+ err = bErr
|
|
|
|
+ }
|
|
|
|
+ bufPool.Put(b)
|
|
|
|
+ }()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ var n int
|
|
|
|
+
|
|
// Comments, first HELP, then TYPE.
|
|
// Comments, first HELP, then TYPE.
|
|
if in.Help != nil {
|
|
if in.Help != nil {
|
|
- n, err := fmt.Fprintf(
|
|
|
|
- out, "# HELP %s %s\n",
|
|
|
|
- name, escapeString(*in.Help, false),
|
|
|
|
- )
|
|
|
|
|
|
+ n, err = w.WriteString("# HELP ")
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
- return written, err
|
|
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ n, err = w.WriteString(name)
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ err = w.WriteByte(' ')
|
|
|
|
+ written++
|
|
|
|
+ if err != nil {
|
|
|
|
+ return
|
|
}
|
|
}
|
|
|
|
+ n, err = writeEscapedString(w, *in.Help, false)
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ err = w.WriteByte('\n')
|
|
|
|
+ written++
|
|
|
|
+ if err != nil {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ n, err = w.WriteString("# TYPE ")
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ n, err = w.WriteString(name)
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return
|
|
}
|
|
}
|
|
metricType := in.GetType()
|
|
metricType := in.GetType()
|
|
- n, err := fmt.Fprintf(
|
|
|
|
- out, "# TYPE %s %s\n",
|
|
|
|
- name, strings.ToLower(metricType.String()),
|
|
|
|
- )
|
|
|
|
|
|
+ switch metricType {
|
|
|
|
+ case dto.MetricType_COUNTER:
|
|
|
|
+ n, err = w.WriteString(" counter\n")
|
|
|
|
+ case dto.MetricType_GAUGE:
|
|
|
|
+ n, err = w.WriteString(" gauge\n")
|
|
|
|
+ case dto.MetricType_SUMMARY:
|
|
|
|
+ n, err = w.WriteString(" summary\n")
|
|
|
|
+ case dto.MetricType_UNTYPED:
|
|
|
|
+ n, err = w.WriteString(" untyped\n")
|
|
|
|
+ case dto.MetricType_HISTOGRAM:
|
|
|
|
+ n, err = w.WriteString(" histogram\n")
|
|
|
|
+ default:
|
|
|
|
+ return written, fmt.Errorf("unknown metric type %s", metricType.String())
|
|
|
|
+ }
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
- return written, err
|
|
|
|
|
|
+ return
|
|
}
|
|
}
|
|
|
|
|
|
// Finally the samples, one line for each.
|
|
// Finally the samples, one line for each.
|
|
@@ -75,9 +160,8 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
|
)
|
|
)
|
|
}
|
|
}
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name, metric, "", "",
|
|
|
|
|
|
+ w, name, "", metric, "", 0,
|
|
metric.Counter.GetValue(),
|
|
metric.Counter.GetValue(),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
case dto.MetricType_GAUGE:
|
|
case dto.MetricType_GAUGE:
|
|
if metric.Gauge == nil {
|
|
if metric.Gauge == nil {
|
|
@@ -86,9 +170,8 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
|
)
|
|
)
|
|
}
|
|
}
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name, metric, "", "",
|
|
|
|
|
|
+ w, name, "", metric, "", 0,
|
|
metric.Gauge.GetValue(),
|
|
metric.Gauge.GetValue(),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
case dto.MetricType_UNTYPED:
|
|
case dto.MetricType_UNTYPED:
|
|
if metric.Untyped == nil {
|
|
if metric.Untyped == nil {
|
|
@@ -97,9 +180,8 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
|
)
|
|
)
|
|
}
|
|
}
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name, metric, "", "",
|
|
|
|
|
|
+ w, name, "", metric, "", 0,
|
|
metric.Untyped.GetValue(),
|
|
metric.Untyped.GetValue(),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
case dto.MetricType_SUMMARY:
|
|
case dto.MetricType_SUMMARY:
|
|
if metric.Summary == nil {
|
|
if metric.Summary == nil {
|
|
@@ -109,29 +191,26 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
|
}
|
|
}
|
|
for _, q := range metric.Summary.Quantile {
|
|
for _, q := range metric.Summary.Quantile {
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name, metric,
|
|
|
|
- model.QuantileLabel, fmt.Sprint(q.GetQuantile()),
|
|
|
|
|
|
+ w, name, "", metric,
|
|
|
|
+ model.QuantileLabel, q.GetQuantile(),
|
|
q.GetValue(),
|
|
q.GetValue(),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
- return written, err
|
|
|
|
|
|
+ return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name+"_sum", metric, "", "",
|
|
|
|
|
|
+ w, name, "_sum", metric, "", 0,
|
|
metric.Summary.GetSampleSum(),
|
|
metric.Summary.GetSampleSum(),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
|
|
+ written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
- return written, err
|
|
|
|
|
|
+ return
|
|
}
|
|
}
|
|
- written += n
|
|
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name+"_count", metric, "", "",
|
|
|
|
|
|
+ w, name, "_count", metric, "", 0,
|
|
float64(metric.Summary.GetSampleCount()),
|
|
float64(metric.Summary.GetSampleCount()),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
case dto.MetricType_HISTOGRAM:
|
|
case dto.MetricType_HISTOGRAM:
|
|
if metric.Histogram == nil {
|
|
if metric.Histogram == nil {
|
|
@@ -140,46 +219,42 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
|
)
|
|
)
|
|
}
|
|
}
|
|
infSeen := false
|
|
infSeen := false
|
|
- for _, q := range metric.Histogram.Bucket {
|
|
|
|
|
|
+ for _, b := range metric.Histogram.Bucket {
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name+"_bucket", metric,
|
|
|
|
- model.BucketLabel, fmt.Sprint(q.GetUpperBound()),
|
|
|
|
- float64(q.GetCumulativeCount()),
|
|
|
|
- out,
|
|
|
|
|
|
+ w, name, "_bucket", metric,
|
|
|
|
+ model.BucketLabel, b.GetUpperBound(),
|
|
|
|
+ float64(b.GetCumulativeCount()),
|
|
)
|
|
)
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
- return written, err
|
|
|
|
|
|
+ return
|
|
}
|
|
}
|
|
- if math.IsInf(q.GetUpperBound(), +1) {
|
|
|
|
|
|
+ if math.IsInf(b.GetUpperBound(), +1) {
|
|
infSeen = true
|
|
infSeen = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !infSeen {
|
|
if !infSeen {
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name+"_bucket", metric,
|
|
|
|
- model.BucketLabel, "+Inf",
|
|
|
|
|
|
+ w, name, "_bucket", metric,
|
|
|
|
+ model.BucketLabel, math.Inf(+1),
|
|
float64(metric.Histogram.GetSampleCount()),
|
|
float64(metric.Histogram.GetSampleCount()),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
|
|
+ written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
- return written, err
|
|
|
|
|
|
+ return
|
|
}
|
|
}
|
|
- written += n
|
|
|
|
}
|
|
}
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name+"_sum", metric, "", "",
|
|
|
|
|
|
+ w, name, "_sum", metric, "", 0,
|
|
metric.Histogram.GetSampleSum(),
|
|
metric.Histogram.GetSampleSum(),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
|
|
+ written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
- return written, err
|
|
|
|
|
|
+ return
|
|
}
|
|
}
|
|
- written += n
|
|
|
|
n, err = writeSample(
|
|
n, err = writeSample(
|
|
- name+"_count", metric, "", "",
|
|
|
|
|
|
+ w, name, "_count", metric, "", 0,
|
|
float64(metric.Histogram.GetSampleCount()),
|
|
float64(metric.Histogram.GetSampleCount()),
|
|
- out,
|
|
|
|
)
|
|
)
|
|
default:
|
|
default:
|
|
return written, fmt.Errorf(
|
|
return written, fmt.Errorf(
|
|
@@ -188,116 +263,204 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
|
}
|
|
}
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
- return written, err
|
|
|
|
|
|
+ return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- return written, nil
|
|
|
|
|
|
+ return
|
|
}
|
|
}
|
|
|
|
|
|
-// writeSample writes a single sample in text format to out, given the metric
|
|
|
|
|
|
+// writeSample writes a single sample in text format to w, given the metric
|
|
// name, the metric proto message itself, optionally an additional label name
|
|
// name, the metric proto message itself, optionally an additional label name
|
|
-// and value (use empty strings if not required), and the value. The function
|
|
|
|
-// returns the number of bytes written and any error encountered.
|
|
|
|
|
|
+// with a float64 value (use empty string as label name if not required), and
|
|
|
|
+// the value. The function returns the number of bytes written and any error
|
|
|
|
+// encountered.
|
|
func writeSample(
|
|
func writeSample(
|
|
- name string,
|
|
|
|
|
|
+ w enhancedWriter,
|
|
|
|
+ name, suffix string,
|
|
metric *dto.Metric,
|
|
metric *dto.Metric,
|
|
- additionalLabelName, additionalLabelValue string,
|
|
|
|
|
|
+ additionalLabelName string, additionalLabelValue float64,
|
|
value float64,
|
|
value float64,
|
|
- out io.Writer,
|
|
|
|
) (int, error) {
|
|
) (int, error) {
|
|
var written int
|
|
var written int
|
|
- n, err := fmt.Fprint(out, name)
|
|
|
|
|
|
+ n, err := w.WriteString(name)
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
return written, err
|
|
return written, err
|
|
}
|
|
}
|
|
- n, err = labelPairsToText(
|
|
|
|
- metric.Label,
|
|
|
|
- additionalLabelName, additionalLabelValue,
|
|
|
|
- out,
|
|
|
|
|
|
+ if suffix != "" {
|
|
|
|
+ n, err = w.WriteString(suffix)
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ n, err = writeLabelPairs(
|
|
|
|
+ w, metric.Label, additionalLabelName, additionalLabelValue,
|
|
)
|
|
)
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
return written, err
|
|
return written, err
|
|
}
|
|
}
|
|
- n, err = fmt.Fprintf(out, " %v", value)
|
|
|
|
|
|
+ err = w.WriteByte(' ')
|
|
|
|
+ written++
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ n, err = writeFloat(w, value)
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
return written, err
|
|
return written, err
|
|
}
|
|
}
|
|
if metric.TimestampMs != nil {
|
|
if metric.TimestampMs != nil {
|
|
- n, err = fmt.Fprintf(out, " %v", *metric.TimestampMs)
|
|
|
|
|
|
+ err = w.WriteByte(' ')
|
|
|
|
+ written++
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ n, err = writeInt(w, *metric.TimestampMs)
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
return written, err
|
|
return written, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- n, err = out.Write([]byte{'\n'})
|
|
|
|
- written += n
|
|
|
|
|
|
+ err = w.WriteByte('\n')
|
|
|
|
+ written++
|
|
if err != nil {
|
|
if err != nil {
|
|
return written, err
|
|
return written, err
|
|
}
|
|
}
|
|
return written, nil
|
|
return written, nil
|
|
}
|
|
}
|
|
|
|
|
|
-// labelPairsToText converts a slice of LabelPair proto messages plus the
|
|
|
|
|
|
+// writeLabelPairs converts a slice of LabelPair proto messages plus the
|
|
// explicitly given additional label pair into text formatted as required by the
|
|
// explicitly given additional label pair into text formatted as required by the
|
|
-// text format and writes it to 'out'. An empty slice in combination with an
|
|
|
|
-// empty string 'additionalLabelName' results in nothing being
|
|
|
|
-// written. Otherwise, the label pairs are written, escaped as required by the
|
|
|
|
-// text format, and enclosed in '{...}'. The function returns the number of
|
|
|
|
-// bytes written and any error encountered.
|
|
|
|
-func labelPairsToText(
|
|
|
|
|
|
+// text format and writes it to 'w'. An empty slice in combination with an empty
|
|
|
|
+// string 'additionalLabelName' results in nothing being written. Otherwise, the
|
|
|
|
+// label pairs are written, escaped as required by the text format, and enclosed
|
|
|
|
+// in '{...}'. The function returns the number of bytes written and any error
|
|
|
|
+// encountered.
|
|
|
|
+func writeLabelPairs(
|
|
|
|
+ w enhancedWriter,
|
|
in []*dto.LabelPair,
|
|
in []*dto.LabelPair,
|
|
- additionalLabelName, additionalLabelValue string,
|
|
|
|
- out io.Writer,
|
|
|
|
|
|
+ additionalLabelName string, additionalLabelValue float64,
|
|
) (int, error) {
|
|
) (int, error) {
|
|
if len(in) == 0 && additionalLabelName == "" {
|
|
if len(in) == 0 && additionalLabelName == "" {
|
|
return 0, nil
|
|
return 0, nil
|
|
}
|
|
}
|
|
- var written int
|
|
|
|
- separator := '{'
|
|
|
|
|
|
+ var (
|
|
|
|
+ written int
|
|
|
|
+ separator byte = '{'
|
|
|
|
+ )
|
|
for _, lp := range in {
|
|
for _, lp := range in {
|
|
- n, err := fmt.Fprintf(
|
|
|
|
- out, `%c%s="%s"`,
|
|
|
|
- separator, lp.GetName(), escapeString(lp.GetValue(), true),
|
|
|
|
- )
|
|
|
|
|
|
+ err := w.WriteByte(separator)
|
|
|
|
+ written++
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ n, err := w.WriteString(lp.GetName())
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ n, err = w.WriteString(`="`)
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ n, err = writeEscapedString(w, lp.GetValue(), true)
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
return written, err
|
|
return written, err
|
|
}
|
|
}
|
|
|
|
+ err = w.WriteByte('"')
|
|
|
|
+ written++
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
separator = ','
|
|
separator = ','
|
|
}
|
|
}
|
|
if additionalLabelName != "" {
|
|
if additionalLabelName != "" {
|
|
- n, err := fmt.Fprintf(
|
|
|
|
- out, `%c%s="%s"`,
|
|
|
|
- separator, additionalLabelName,
|
|
|
|
- escapeString(additionalLabelValue, true),
|
|
|
|
- )
|
|
|
|
|
|
+ err := w.WriteByte(separator)
|
|
|
|
+ written++
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ n, err := w.WriteString(additionalLabelName)
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ n, err = w.WriteString(`="`)
|
|
|
|
+ written += n
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
|
|
+ n, err = writeFloat(w, additionalLabelValue)
|
|
written += n
|
|
written += n
|
|
if err != nil {
|
|
if err != nil {
|
|
return written, err
|
|
return written, err
|
|
}
|
|
}
|
|
|
|
+ err = w.WriteByte('"')
|
|
|
|
+ written++
|
|
|
|
+ if err != nil {
|
|
|
|
+ return written, err
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- n, err := out.Write([]byte{'}'})
|
|
|
|
- written += n
|
|
|
|
|
|
+ err := w.WriteByte('}')
|
|
|
|
+ written++
|
|
if err != nil {
|
|
if err != nil {
|
|
return written, err
|
|
return written, err
|
|
}
|
|
}
|
|
return written, nil
|
|
return written, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// writeEscapedString replaces '\' by '\\', new line character by '\n', and - if
|
|
|
|
+// includeDoubleQuote is true - '"' by '\"'.
|
|
var (
|
|
var (
|
|
- escape = strings.NewReplacer("\\", `\\`, "\n", `\n`)
|
|
|
|
- escapeWithDoubleQuote = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
|
|
|
|
|
|
+ escaper = strings.NewReplacer("\\", `\\`, "\n", `\n`)
|
|
|
|
+ quotedEscaper = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
|
|
)
|
|
)
|
|
|
|
|
|
-// escapeString replaces '\' by '\\', new line character by '\n', and - if
|
|
|
|
-// includeDoubleQuote is true - '"' by '\"'.
|
|
|
|
-func escapeString(v string, includeDoubleQuote bool) string {
|
|
|
|
|
|
+func writeEscapedString(w enhancedWriter, v string, includeDoubleQuote bool) (int, error) {
|
|
if includeDoubleQuote {
|
|
if includeDoubleQuote {
|
|
- return escapeWithDoubleQuote.Replace(v)
|
|
|
|
|
|
+ return quotedEscaper.WriteString(w, v)
|
|
|
|
+ } else {
|
|
|
|
+ return escaper.WriteString(w, v)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// writeFloat is equivalent to fmt.Fprint with a float64 argument but hardcodes
|
|
|
|
+// a few common cases for increased efficiency. For non-hardcoded cases, it uses
|
|
|
|
+// strconv.AppendFloat to avoid allocations, similar to writeInt.
|
|
|
|
+func writeFloat(w enhancedWriter, f float64) (int, error) {
|
|
|
|
+ switch {
|
|
|
|
+ case f == 1:
|
|
|
|
+ return 1, w.WriteByte('1')
|
|
|
|
+ case f == 0:
|
|
|
|
+ return 1, w.WriteByte('0')
|
|
|
|
+ case f == -1:
|
|
|
|
+ return w.WriteString("-1")
|
|
|
|
+ case math.IsNaN(f):
|
|
|
|
+ return w.WriteString("NaN")
|
|
|
|
+ case math.IsInf(f, +1):
|
|
|
|
+ return w.WriteString("+Inf")
|
|
|
|
+ case math.IsInf(f, -1):
|
|
|
|
+ return w.WriteString("-Inf")
|
|
|
|
+ default:
|
|
|
|
+ bp := numBufPool.Get().(*[]byte)
|
|
|
|
+ *bp = strconv.AppendFloat((*bp)[:0], f, 'g', -1, 64)
|
|
|
|
+ written, err := w.Write(*bp)
|
|
|
|
+ numBufPool.Put(bp)
|
|
|
|
+ return written, err
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- return escape.Replace(v)
|
|
|
|
|
|
+// writeInt is equivalent to fmt.Fprint with an int64 argument but uses
|
|
|
|
+// strconv.AppendInt with a byte slice taken from a sync.Pool to avoid
|
|
|
|
+// allocations.
|
|
|
|
+func writeInt(w enhancedWriter, i int64) (int, error) {
|
|
|
|
+ bp := numBufPool.Get().(*[]byte)
|
|
|
|
+ *bp = strconv.AppendInt((*bp)[:0], i, 10)
|
|
|
|
+ written, err := w.Write(*bp)
|
|
|
|
+ numBufPool.Put(bp)
|
|
|
|
+ return written, err
|
|
}
|
|
}
|