value.go

  1package json
  2
  3import (
  4	"bytes"
  5	"encoding/base64"
  6	"math/big"
  7	"strconv"
  8
  9	"github.com/aws/smithy-go/encoding"
 10)
 11
 12// Value represents a JSON Value type
 13// JSON Value types: Object, Array, String, Number, Boolean, and Null
 14type Value struct {
 15	w       *bytes.Buffer
 16	scratch *[]byte
 17}
 18
 19// newValue returns a new Value encoder
 20func newValue(w *bytes.Buffer, scratch *[]byte) Value {
 21	return Value{w: w, scratch: scratch}
 22}
 23
 24// String encodes v as a JSON string
 25func (jv Value) String(v string) {
 26	escapeStringBytes(jv.w, []byte(v))
 27}
 28
 29// Byte encodes v as a JSON number
 30func (jv Value) Byte(v int8) {
 31	jv.Long(int64(v))
 32}
 33
 34// Short encodes v as a JSON number
 35func (jv Value) Short(v int16) {
 36	jv.Long(int64(v))
 37}
 38
 39// Integer encodes v as a JSON number
 40func (jv Value) Integer(v int32) {
 41	jv.Long(int64(v))
 42}
 43
 44// Long encodes v as a JSON number
 45func (jv Value) Long(v int64) {
 46	*jv.scratch = strconv.AppendInt((*jv.scratch)[:0], v, 10)
 47	jv.w.Write(*jv.scratch)
 48}
 49
 50// ULong encodes v as a JSON number
 51func (jv Value) ULong(v uint64) {
 52	*jv.scratch = strconv.AppendUint((*jv.scratch)[:0], v, 10)
 53	jv.w.Write(*jv.scratch)
 54}
 55
 56// Float encodes v as a JSON number
 57func (jv Value) Float(v float32) {
 58	jv.float(float64(v), 32)
 59}
 60
 61// Double encodes v as a JSON number
 62func (jv Value) Double(v float64) {
 63	jv.float(v, 64)
 64}
 65
 66func (jv Value) float(v float64, bits int) {
 67	*jv.scratch = encoding.EncodeFloat((*jv.scratch)[:0], v, bits)
 68	jv.w.Write(*jv.scratch)
 69}
 70
 71// Boolean encodes v as a JSON boolean
 72func (jv Value) Boolean(v bool) {
 73	*jv.scratch = strconv.AppendBool((*jv.scratch)[:0], v)
 74	jv.w.Write(*jv.scratch)
 75}
 76
 77// Base64EncodeBytes writes v as a base64 value in JSON string
 78func (jv Value) Base64EncodeBytes(v []byte) {
 79	encodeByteSlice(jv.w, (*jv.scratch)[:0], v)
 80}
 81
 82// Write writes v directly to the JSON document
 83func (jv Value) Write(v []byte) {
 84	jv.w.Write(v)
 85}
 86
 87// Array returns a new Array encoder
 88func (jv Value) Array() *Array {
 89	return newArray(jv.w, jv.scratch)
 90}
 91
 92// Object returns a new Object encoder
 93func (jv Value) Object() *Object {
 94	return newObject(jv.w, jv.scratch)
 95}
 96
 97// Null encodes a null JSON value
 98func (jv Value) Null() {
 99	jv.w.WriteString(null)
100}
101
102// BigInteger encodes v as JSON value
103func (jv Value) BigInteger(v *big.Int) {
104	jv.w.Write([]byte(v.Text(10)))
105}
106
107// BigDecimal encodes v as JSON value
108func (jv Value) BigDecimal(v *big.Float) {
109	if i, accuracy := v.Int64(); accuracy == big.Exact {
110		jv.Long(i)
111		return
112	}
113	// TODO: Should this try to match ES6 ToString similar to stdlib JSON?
114	jv.w.Write([]byte(v.Text('e', -1)))
115}
116
117// Based on encoding/json encodeByteSlice from the Go Standard Library
118// https://golang.org/src/encoding/json/encode.go
119func encodeByteSlice(w *bytes.Buffer, scratch []byte, v []byte) {
120	if v == nil {
121		w.WriteString(null)
122		return
123	}
124
125	w.WriteRune(quote)
126
127	encodedLen := base64.StdEncoding.EncodedLen(len(v))
128	if encodedLen <= len(scratch) {
129		// If the encoded bytes fit in e.scratch, avoid an extra
130		// allocation and use the cheaper Encoding.Encode.
131		dst := scratch[:encodedLen]
132		base64.StdEncoding.Encode(dst, v)
133		w.Write(dst)
134	} else if encodedLen <= 1024 {
135		// The encoded bytes are short enough to allocate for, and
136		// Encoding.Encode is still cheaper.
137		dst := make([]byte, encodedLen)
138		base64.StdEncoding.Encode(dst, v)
139		w.Write(dst)
140	} else {
141		// The encoded bytes are too long to cheaply allocate, and
142		// Encoding.Encode is no longer noticeably cheaper.
143		enc := base64.NewEncoder(base64.StdEncoding, w)
144		enc.Write(v)
145		enc.Close()
146	}
147
148	w.WriteRune(quote)
149}