terraform/vendor/github.com/newrelic/go-agent/internal/jsonx/encode.go

175 lines
4.1 KiB
Go

// Copyright 2010 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 jsonx extends the encoding/json package to encode JSON
// incrementally and without requiring reflection.
package jsonx
import (
"bytes"
"encoding/json"
"math"
"reflect"
"strconv"
"unicode/utf8"
)
var hex = "0123456789abcdef"
// AppendString escapes s appends it to buf.
func AppendString(buf *bytes.Buffer, s string) {
buf.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
i++
continue
}
if start < i {
buf.WriteString(s[start:i])
}
switch b {
case '\\', '"':
buf.WriteByte('\\')
buf.WriteByte(b)
case '\n':
buf.WriteByte('\\')
buf.WriteByte('n')
case '\r':
buf.WriteByte('\\')
buf.WriteByte('r')
case '\t':
buf.WriteByte('\\')
buf.WriteByte('t')
default:
// This encodes bytes < 0x20 except for \n and \r,
// as well as <, > and &. The latter are escaped because they
// can lead to security holes when user-controlled strings
// are rendered into JSON and served to some browsers.
buf.WriteString(`\u00`)
buf.WriteByte(hex[b>>4])
buf.WriteByte(hex[b&0xF])
}
i++
start = i
continue
}
c, size := utf8.DecodeRuneInString(s[i:])
if c == utf8.RuneError && size == 1 {
if start < i {
buf.WriteString(s[start:i])
}
buf.WriteString(`\ufffd`)
i += size
start = i
continue
}
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
if c == '\u2028' || c == '\u2029' {
if start < i {
buf.WriteString(s[start:i])
}
buf.WriteString(`\u202`)
buf.WriteByte(hex[c&0xF])
i += size
start = i
continue
}
i += size
}
if start < len(s) {
buf.WriteString(s[start:])
}
buf.WriteByte('"')
}
// AppendStringArray appends an array of string literals to buf.
func AppendStringArray(buf *bytes.Buffer, a ...string) {
buf.WriteByte('[')
for i, s := range a {
if i > 0 {
buf.WriteByte(',')
}
AppendString(buf, s)
}
buf.WriteByte(']')
}
// AppendFloat appends a numeric literal representing the value to buf.
func AppendFloat(buf *bytes.Buffer, x float64) error {
var scratch [64]byte
if math.IsInf(x, 0) || math.IsNaN(x) {
return &json.UnsupportedValueError{
Value: reflect.ValueOf(x),
Str: strconv.FormatFloat(x, 'g', -1, 64),
}
}
buf.Write(strconv.AppendFloat(scratch[:0], x, 'g', -1, 64))
return nil
}
// AppendFloatArray appends an array of numeric literals to buf.
func AppendFloatArray(buf *bytes.Buffer, a ...float64) error {
buf.WriteByte('[')
for i, x := range a {
if i > 0 {
buf.WriteByte(',')
}
if err := AppendFloat(buf, x); err != nil {
return err
}
}
buf.WriteByte(']')
return nil
}
// AppendInt appends a numeric literal representing the value to buf.
func AppendInt(buf *bytes.Buffer, x int64) {
var scratch [64]byte
buf.Write(strconv.AppendInt(scratch[:0], x, 10))
}
// AppendIntArray appends an array of numeric literals to buf.
func AppendIntArray(buf *bytes.Buffer, a ...int64) {
var scratch [64]byte
buf.WriteByte('[')
for i, x := range a {
if i > 0 {
buf.WriteByte(',')
}
buf.Write(strconv.AppendInt(scratch[:0], x, 10))
}
buf.WriteByte(']')
}
// AppendUint appends a numeric literal representing the value to buf.
func AppendUint(buf *bytes.Buffer, x uint64) {
var scratch [64]byte
buf.Write(strconv.AppendUint(scratch[:0], x, 10))
}
// AppendUintArray appends an array of numeric literals to buf.
func AppendUintArray(buf *bytes.Buffer, a ...uint64) {
var scratch [64]byte
buf.WriteByte('[')
for i, x := range a {
if i > 0 {
buf.WriteByte(',')
}
buf.Write(strconv.AppendUint(scratch[:0], x, 10))
}
buf.WriteByte(']')
}