value.go 3.5 KB

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