value.go (3541B)
1 package json 2 3 import ( 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 14 type Value struct { 15 w *bytes.Buffer 16 scratch *[]byte 17 } 18 19 // newValue returns a new Value encoder 20 func newValue(w *bytes.Buffer, scratch *[]byte) Value { 21 return Value{w: w, scratch: scratch} 22 } 23 24 // String encodes v as a JSON string 25 func (jv Value) String(v string) { 26 escapeStringBytes(jv.w, []byte(v)) 27 } 28 29 // Byte encodes v as a JSON number 30 func (jv Value) Byte(v int8) { 31 jv.Long(int64(v)) 32 } 33 34 // Short encodes v as a JSON number 35 func (jv Value) Short(v int16) { 36 jv.Long(int64(v)) 37 } 38 39 // Integer encodes v as a JSON number 40 func (jv Value) Integer(v int32) { 41 jv.Long(int64(v)) 42 } 43 44 // Long encodes v as a JSON number 45 func (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 51 func (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 57 func (jv Value) Float(v float32) { 58 jv.float(float64(v), 32) 59 } 60 61 // Double encodes v as a JSON number 62 func (jv Value) Double(v float64) { 63 jv.float(v, 64) 64 } 65 66 func (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 72 func (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 78 func (jv Value) Base64EncodeBytes(v []byte) { 79 encodeByteSlice(jv.w, (*jv.scratch)[:0], v) 80 } 81 82 // Write writes v directly to the JSON document 83 func (jv Value) Write(v []byte) { 84 jv.w.Write(v) 85 } 86 87 // Array returns a new Array encoder 88 func (jv Value) Array() *Array { 89 return newArray(jv.w, jv.scratch) 90 } 91 92 // Object returns a new Object encoder 93 func (jv Value) Object() *Object { 94 return newObject(jv.w, jv.scratch) 95 } 96 97 // Null encodes a null JSON value 98 func (jv Value) Null() { 99 jv.w.WriteString(null) 100 } 101 102 // BigInteger encodes v as JSON value 103 func (jv Value) BigInteger(v *big.Int) { 104 jv.w.Write([]byte(v.Text(10))) 105 } 106 107 // BigDecimal encodes v as JSON value 108 func (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 119 func 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 }