code.dwrz.net

Go monorepo.
Log | Files | Refs

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 }