encode.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package json
  5. import (
  6. "math"
  7. "math/bits"
  8. "strconv"
  9. "strings"
  10. "unicode/utf8"
  11. "google.golang.org/protobuf/internal/detrand"
  12. "google.golang.org/protobuf/internal/errors"
  13. )
  14. // kind represents an encoding type.
  15. type kind uint8
  16. const (
  17. _ kind = (1 << iota) / 2
  18. name
  19. scalar
  20. objectOpen
  21. objectClose
  22. arrayOpen
  23. arrayClose
  24. )
  25. // Encoder provides methods to write out JSON constructs and values. The user is
  26. // responsible for producing valid sequences of JSON constructs and values.
  27. type Encoder struct {
  28. indent string
  29. lastKind kind
  30. indents []byte
  31. out []byte
  32. }
  33. // NewEncoder returns an Encoder.
  34. //
  35. // If indent is a non-empty string, it causes every entry for an Array or Object
  36. // to be preceded by the indent and trailed by a newline.
  37. func NewEncoder(buf []byte, indent string) (*Encoder, error) {
  38. e := &Encoder{
  39. out: buf,
  40. }
  41. if len(indent) > 0 {
  42. if strings.Trim(indent, " \t") != "" {
  43. return nil, errors.New("indent may only be composed of space or tab characters")
  44. }
  45. e.indent = indent
  46. }
  47. return e, nil
  48. }
  49. // Bytes returns the content of the written bytes.
  50. func (e *Encoder) Bytes() []byte {
  51. return e.out
  52. }
  53. // WriteNull writes out the null value.
  54. func (e *Encoder) WriteNull() {
  55. e.prepareNext(scalar)
  56. e.out = append(e.out, "null"...)
  57. }
  58. // WriteBool writes out the given boolean value.
  59. func (e *Encoder) WriteBool(b bool) {
  60. e.prepareNext(scalar)
  61. if b {
  62. e.out = append(e.out, "true"...)
  63. } else {
  64. e.out = append(e.out, "false"...)
  65. }
  66. }
  67. // WriteString writes out the given string in JSON string value. Returns error
  68. // if input string contains invalid UTF-8.
  69. func (e *Encoder) WriteString(s string) error {
  70. e.prepareNext(scalar)
  71. var err error
  72. if e.out, err = appendString(e.out, s); err != nil {
  73. return err
  74. }
  75. return nil
  76. }
  77. // Sentinel error used for indicating invalid UTF-8.
  78. var errInvalidUTF8 = errors.New("invalid UTF-8")
  79. func appendString(out []byte, in string) ([]byte, error) {
  80. out = append(out, '"')
  81. i := indexNeedEscapeInString(in)
  82. in, out = in[i:], append(out, in[:i]...)
  83. for len(in) > 0 {
  84. switch r, n := utf8.DecodeRuneInString(in); {
  85. case r == utf8.RuneError && n == 1:
  86. return out, errInvalidUTF8
  87. case r < ' ' || r == '"' || r == '\\':
  88. out = append(out, '\\')
  89. switch r {
  90. case '"', '\\':
  91. out = append(out, byte(r))
  92. case '\b':
  93. out = append(out, 'b')
  94. case '\f':
  95. out = append(out, 'f')
  96. case '\n':
  97. out = append(out, 'n')
  98. case '\r':
  99. out = append(out, 'r')
  100. case '\t':
  101. out = append(out, 't')
  102. default:
  103. out = append(out, 'u')
  104. out = append(out, "0000"[1+(bits.Len32(uint32(r))-1)/4:]...)
  105. out = strconv.AppendUint(out, uint64(r), 16)
  106. }
  107. in = in[n:]
  108. default:
  109. i := indexNeedEscapeInString(in[n:])
  110. in, out = in[n+i:], append(out, in[:n+i]...)
  111. }
  112. }
  113. out = append(out, '"')
  114. return out, nil
  115. }
  116. // indexNeedEscapeInString returns the index of the character that needs
  117. // escaping. If no characters need escaping, this returns the input length.
  118. func indexNeedEscapeInString(s string) int {
  119. for i, r := range s {
  120. if r < ' ' || r == '\\' || r == '"' || r == utf8.RuneError {
  121. return i
  122. }
  123. }
  124. return len(s)
  125. }
  126. // WriteFloat writes out the given float and bitSize in JSON number value.
  127. func (e *Encoder) WriteFloat(n float64, bitSize int) {
  128. e.prepareNext(scalar)
  129. e.out = appendFloat(e.out, n, bitSize)
  130. }
  131. // appendFloat formats given float in bitSize, and appends to the given []byte.
  132. func appendFloat(out []byte, n float64, bitSize int) []byte {
  133. switch {
  134. case math.IsNaN(n):
  135. return append(out, `"NaN"`...)
  136. case math.IsInf(n, +1):
  137. return append(out, `"Infinity"`...)
  138. case math.IsInf(n, -1):
  139. return append(out, `"-Infinity"`...)
  140. }
  141. // JSON number formatting logic based on encoding/json.
  142. // See floatEncoder.encode for reference.
  143. fmt := byte('f')
  144. if abs := math.Abs(n); abs != 0 {
  145. if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) ||
  146. bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
  147. fmt = 'e'
  148. }
  149. }
  150. out = strconv.AppendFloat(out, n, fmt, -1, bitSize)
  151. if fmt == 'e' {
  152. n := len(out)
  153. if n >= 4 && out[n-4] == 'e' && out[n-3] == '-' && out[n-2] == '0' {
  154. out[n-2] = out[n-1]
  155. out = out[:n-1]
  156. }
  157. }
  158. return out
  159. }
  160. // WriteInt writes out the given signed integer in JSON number value.
  161. func (e *Encoder) WriteInt(n int64) {
  162. e.prepareNext(scalar)
  163. e.out = strconv.AppendInt(e.out, n, 10)
  164. }
  165. // WriteUint writes out the given unsigned integer in JSON number value.
  166. func (e *Encoder) WriteUint(n uint64) {
  167. e.prepareNext(scalar)
  168. e.out = strconv.AppendUint(e.out, n, 10)
  169. }
  170. // StartObject writes out the '{' symbol.
  171. func (e *Encoder) StartObject() {
  172. e.prepareNext(objectOpen)
  173. e.out = append(e.out, '{')
  174. }
  175. // EndObject writes out the '}' symbol.
  176. func (e *Encoder) EndObject() {
  177. e.prepareNext(objectClose)
  178. e.out = append(e.out, '}')
  179. }
  180. // WriteName writes out the given string in JSON string value and the name
  181. // separator ':'. Returns error if input string contains invalid UTF-8, which
  182. // should not be likely as protobuf field names should be valid.
  183. func (e *Encoder) WriteName(s string) error {
  184. e.prepareNext(name)
  185. var err error
  186. // Append to output regardless of error.
  187. e.out, err = appendString(e.out, s)
  188. e.out = append(e.out, ':')
  189. return err
  190. }
  191. // StartArray writes out the '[' symbol.
  192. func (e *Encoder) StartArray() {
  193. e.prepareNext(arrayOpen)
  194. e.out = append(e.out, '[')
  195. }
  196. // EndArray writes out the ']' symbol.
  197. func (e *Encoder) EndArray() {
  198. e.prepareNext(arrayClose)
  199. e.out = append(e.out, ']')
  200. }
  201. // prepareNext adds possible comma and indentation for the next value based
  202. // on last type and indent option. It also updates lastKind to next.
  203. func (e *Encoder) prepareNext(next kind) {
  204. defer func() {
  205. // Set lastKind to next.
  206. e.lastKind = next
  207. }()
  208. if len(e.indent) == 0 {
  209. // Need to add comma on the following condition.
  210. if e.lastKind&(scalar|objectClose|arrayClose) != 0 &&
  211. next&(name|scalar|objectOpen|arrayOpen) != 0 {
  212. e.out = append(e.out, ',')
  213. // For single-line output, add a random extra space after each
  214. // comma to make output unstable.
  215. if detrand.Bool() {
  216. e.out = append(e.out, ' ')
  217. }
  218. }
  219. return
  220. }
  221. switch {
  222. case e.lastKind&(objectOpen|arrayOpen) != 0:
  223. // If next type is NOT closing, add indent and newline.
  224. if next&(objectClose|arrayClose) == 0 {
  225. e.indents = append(e.indents, e.indent...)
  226. e.out = append(e.out, '\n')
  227. e.out = append(e.out, e.indents...)
  228. }
  229. case e.lastKind&(scalar|objectClose|arrayClose) != 0:
  230. switch {
  231. // If next type is either a value or name, add comma and newline.
  232. case next&(name|scalar|objectOpen|arrayOpen) != 0:
  233. e.out = append(e.out, ',', '\n')
  234. // If next type is a closing object or array, adjust indentation.
  235. case next&(objectClose|arrayClose) != 0:
  236. e.indents = e.indents[:len(e.indents)-len(e.indent)]
  237. e.out = append(e.out, '\n')
  238. }
  239. e.out = append(e.out, e.indents...)
  240. case e.lastKind&name != 0:
  241. e.out = append(e.out, ' ')
  242. // For multi-line output, add a random extra space after key: to make
  243. // output unstable.
  244. if detrand.Bool() {
  245. e.out = append(e.out, ' ')
  246. }
  247. }
  248. }