indent.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package encoder
  2. import (
  3. "bytes"
  4. "fmt"
  5. "github.com/goccy/go-json/internal/errors"
  6. )
  7. func takeIndentSrcRuntimeContext(src []byte) (*RuntimeContext, []byte) {
  8. ctx := TakeRuntimeContext()
  9. buf := ctx.Buf[:0]
  10. buf = append(append(buf, src...), nul)
  11. ctx.Buf = buf
  12. return ctx, buf
  13. }
  14. func Indent(buf *bytes.Buffer, src []byte, prefix, indentStr string) error {
  15. if len(src) == 0 {
  16. return errors.ErrUnexpectedEndOfJSON("", 0)
  17. }
  18. srcCtx, srcBuf := takeIndentSrcRuntimeContext(src)
  19. dstCtx := TakeRuntimeContext()
  20. dst := dstCtx.Buf[:0]
  21. dst, err := indentAndWrite(buf, dst, srcBuf, prefix, indentStr)
  22. if err != nil {
  23. ReleaseRuntimeContext(srcCtx)
  24. ReleaseRuntimeContext(dstCtx)
  25. return err
  26. }
  27. dstCtx.Buf = dst
  28. ReleaseRuntimeContext(srcCtx)
  29. ReleaseRuntimeContext(dstCtx)
  30. return nil
  31. }
  32. func indentAndWrite(buf *bytes.Buffer, dst []byte, src []byte, prefix, indentStr string) ([]byte, error) {
  33. dst, err := doIndent(dst, src, prefix, indentStr, false)
  34. if err != nil {
  35. return nil, err
  36. }
  37. if _, err := buf.Write(dst); err != nil {
  38. return nil, err
  39. }
  40. return dst, nil
  41. }
  42. func doIndent(dst, src []byte, prefix, indentStr string, escape bool) ([]byte, error) {
  43. buf, cursor, err := indentValue(dst, src, 0, 0, []byte(prefix), []byte(indentStr), escape)
  44. if err != nil {
  45. return nil, err
  46. }
  47. if err := validateEndBuf(src, cursor); err != nil {
  48. return nil, err
  49. }
  50. return buf, nil
  51. }
  52. func indentValue(
  53. dst []byte,
  54. src []byte,
  55. indentNum int,
  56. cursor int64,
  57. prefix []byte,
  58. indentBytes []byte,
  59. escape bool) ([]byte, int64, error) {
  60. for {
  61. switch src[cursor] {
  62. case ' ', '\t', '\n', '\r':
  63. cursor++
  64. continue
  65. case '{':
  66. return indentObject(dst, src, indentNum, cursor, prefix, indentBytes, escape)
  67. case '}':
  68. return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor)
  69. case '[':
  70. return indentArray(dst, src, indentNum, cursor, prefix, indentBytes, escape)
  71. case ']':
  72. return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor)
  73. case '"':
  74. return compactString(dst, src, cursor, escape)
  75. case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
  76. return compactNumber(dst, src, cursor)
  77. case 't':
  78. return compactTrue(dst, src, cursor)
  79. case 'f':
  80. return compactFalse(dst, src, cursor)
  81. case 'n':
  82. return compactNull(dst, src, cursor)
  83. default:
  84. return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor)
  85. }
  86. }
  87. }
  88. func indentObject(
  89. dst []byte,
  90. src []byte,
  91. indentNum int,
  92. cursor int64,
  93. prefix []byte,
  94. indentBytes []byte,
  95. escape bool) ([]byte, int64, error) {
  96. if src[cursor] == '{' {
  97. dst = append(dst, '{')
  98. } else {
  99. return nil, 0, errors.ErrExpected("expected { character for object value", cursor)
  100. }
  101. cursor = skipWhiteSpace(src, cursor+1)
  102. if src[cursor] == '}' {
  103. dst = append(dst, '}')
  104. return dst, cursor + 1, nil
  105. }
  106. indentNum++
  107. var err error
  108. for {
  109. dst = append(append(dst, '\n'), prefix...)
  110. for i := 0; i < indentNum; i++ {
  111. dst = append(dst, indentBytes...)
  112. }
  113. cursor = skipWhiteSpace(src, cursor)
  114. dst, cursor, err = compactString(dst, src, cursor, escape)
  115. if err != nil {
  116. return nil, 0, err
  117. }
  118. cursor = skipWhiteSpace(src, cursor)
  119. if src[cursor] != ':' {
  120. return nil, 0, errors.ErrSyntax(
  121. fmt.Sprintf("invalid character '%c' after object key", src[cursor]),
  122. cursor+1,
  123. )
  124. }
  125. dst = append(dst, ':', ' ')
  126. dst, cursor, err = indentValue(dst, src, indentNum, cursor+1, prefix, indentBytes, escape)
  127. if err != nil {
  128. return nil, 0, err
  129. }
  130. cursor = skipWhiteSpace(src, cursor)
  131. switch src[cursor] {
  132. case '}':
  133. dst = append(append(dst, '\n'), prefix...)
  134. for i := 0; i < indentNum-1; i++ {
  135. dst = append(dst, indentBytes...)
  136. }
  137. dst = append(dst, '}')
  138. cursor++
  139. return dst, cursor, nil
  140. case ',':
  141. dst = append(dst, ',')
  142. default:
  143. return nil, 0, errors.ErrSyntax(
  144. fmt.Sprintf("invalid character '%c' after object key:value pair", src[cursor]),
  145. cursor+1,
  146. )
  147. }
  148. cursor++
  149. }
  150. }
  151. func indentArray(
  152. dst []byte,
  153. src []byte,
  154. indentNum int,
  155. cursor int64,
  156. prefix []byte,
  157. indentBytes []byte,
  158. escape bool) ([]byte, int64, error) {
  159. if src[cursor] == '[' {
  160. dst = append(dst, '[')
  161. } else {
  162. return nil, 0, errors.ErrExpected("expected [ character for array value", cursor)
  163. }
  164. cursor = skipWhiteSpace(src, cursor+1)
  165. if src[cursor] == ']' {
  166. dst = append(dst, ']')
  167. return dst, cursor + 1, nil
  168. }
  169. indentNum++
  170. var err error
  171. for {
  172. dst = append(append(dst, '\n'), prefix...)
  173. for i := 0; i < indentNum; i++ {
  174. dst = append(dst, indentBytes...)
  175. }
  176. dst, cursor, err = indentValue(dst, src, indentNum, cursor, prefix, indentBytes, escape)
  177. if err != nil {
  178. return nil, 0, err
  179. }
  180. cursor = skipWhiteSpace(src, cursor)
  181. switch src[cursor] {
  182. case ']':
  183. dst = append(append(dst, '\n'), prefix...)
  184. for i := 0; i < indentNum-1; i++ {
  185. dst = append(dst, indentBytes...)
  186. }
  187. dst = append(dst, ']')
  188. cursor++
  189. return dst, cursor, nil
  190. case ',':
  191. dst = append(dst, ',')
  192. default:
  193. return nil, 0, errors.ErrSyntax(
  194. fmt.Sprintf("invalid character '%c' after array value", src[cursor]),
  195. cursor+1,
  196. )
  197. }
  198. cursor++
  199. }
  200. }