You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
3.4 KiB

  1. // Copyright 2010 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 fastjson
  5. import "bytes"
  6. // Compact appends to dst the JSON-encoded src with
  7. // insignificant space characters elided.
  8. func Compact(dst *bytes.Buffer, src []byte) error {
  9. return compact(dst, src, false)
  10. }
  11. func compact(dst *bytes.Buffer, src []byte, escape bool) error {
  12. origLen := dst.Len()
  13. var scan scanner
  14. scan.reset()
  15. start := 0
  16. for i, c := range src {
  17. if escape && (c == '<' || c == '>' || c == '&') {
  18. if start < i {
  19. dst.Write(src[start:i])
  20. }
  21. dst.WriteString(`\u00`)
  22. dst.WriteByte(hex[c>>4])
  23. dst.WriteByte(hex[c&0xF])
  24. start = i + 1
  25. }
  26. // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
  27. if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
  28. if start < i {
  29. dst.Write(src[start:i])
  30. }
  31. dst.WriteString(`\u202`)
  32. dst.WriteByte(hex[src[i+2]&0xF])
  33. start = i + 3
  34. }
  35. v := scan.step(&scan, c)
  36. if v >= scanSkipSpace {
  37. if v == scanError {
  38. break
  39. }
  40. if start < i {
  41. dst.Write(src[start:i])
  42. }
  43. start = i + 1
  44. }
  45. }
  46. if scan.eof() == scanError {
  47. dst.Truncate(origLen)
  48. return scan.err
  49. }
  50. if start < len(src) {
  51. dst.Write(src[start:])
  52. }
  53. return nil
  54. }
  55. func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
  56. dst.WriteByte('\n')
  57. dst.WriteString(prefix)
  58. for i := 0; i < depth; i++ {
  59. dst.WriteString(indent)
  60. }
  61. }
  62. // Indent appends to dst an indented form of the JSON-encoded src.
  63. // Each element in a JSON object or array begins on a new,
  64. // indented line beginning with prefix followed by one or more
  65. // copies of indent according to the indentation nesting.
  66. // The data appended to dst does not begin with the prefix nor
  67. // any indentation, to make it easier to embed inside other formatted JSON data.
  68. // Although leading space characters (space, tab, carriage return, newline)
  69. // at the beginning of src are dropped, trailing space characters
  70. // at the end of src are preserved and copied to dst.
  71. // For example, if src has no trailing spaces, neither will dst;
  72. // if src ends in a trailing newline, so will dst.
  73. func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
  74. origLen := dst.Len()
  75. var scan scanner
  76. scan.reset()
  77. needIndent := false
  78. depth := 0
  79. for _, c := range src {
  80. scan.bytes++
  81. v := scan.step(&scan, c)
  82. if v == scanSkipSpace {
  83. continue
  84. }
  85. if v == scanError {
  86. break
  87. }
  88. if needIndent && v != scanEndObject && v != scanEndArray {
  89. needIndent = false
  90. depth++
  91. newline(dst, prefix, indent, depth)
  92. }
  93. // Emit semantically uninteresting bytes
  94. // (in particular, punctuation in strings) unmodified.
  95. if v == scanContinue {
  96. dst.WriteByte(c)
  97. continue
  98. }
  99. // Add spacing around real punctuation.
  100. switch c {
  101. case '{', '[':
  102. // delay indent so that empty object and array are formatted as {} and [].
  103. needIndent = true
  104. dst.WriteByte(c)
  105. case ',':
  106. dst.WriteByte(c)
  107. newline(dst, prefix, indent, depth)
  108. case ':':
  109. dst.WriteByte(c)
  110. dst.WriteByte(' ')
  111. case '}', ']':
  112. if needIndent {
  113. // suppress indent in empty object/array
  114. needIndent = false
  115. } else {
  116. depth--
  117. newline(dst, prefix, indent, depth)
  118. }
  119. dst.WriteByte(c)
  120. default:
  121. dst.WriteByte(c)
  122. }
  123. }
  124. if scan.eof() == scanError {
  125. dst.Truncate(origLen)
  126. return scan.err
  127. }
  128. return nil
  129. }