1
2
3
4
5
6
7 package json
8
9 import "bytes"
10
11
12
13
14
15
16 func HTMLEscape(dst *bytes.Buffer, src []byte) {
17 dst.Grow(len(src))
18 dst.Write(appendHTMLEscape(dst.AvailableBuffer(), src))
19 }
20
21 func appendHTMLEscape(dst, src []byte) []byte {
22
23
24 start := 0
25 for i, c := range src {
26 if c == '<' || c == '>' || c == '&' {
27 dst = append(dst, src[start:i]...)
28 dst = append(dst, '\\', 'u', '0', '0', hex[c>>4], hex[c&0xF])
29 start = i + 1
30 }
31
32 if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
33 dst = append(dst, src[start:i]...)
34 dst = append(dst, '\\', 'u', '2', '0', '2', hex[src[i+2]&0xF])
35 start = i + len("\u2029")
36 }
37 }
38 return append(dst, src[start:]...)
39 }
40
41
42
43 func Compact(dst *bytes.Buffer, src []byte) error {
44 dst.Grow(len(src))
45 b := dst.AvailableBuffer()
46 b, err := appendCompact(b, src, false)
47 dst.Write(b)
48 return err
49 }
50
51 func appendCompact(dst, src []byte, escape bool) ([]byte, error) {
52 origLen := len(dst)
53 scan := newScanner()
54 defer freeScanner(scan)
55 start := 0
56 for i, c := range src {
57 if escape && (c == '<' || c == '>' || c == '&') {
58 if start < i {
59 dst = append(dst, src[start:i]...)
60 }
61 dst = append(dst, '\\', 'u', '0', '0', hex[c>>4], hex[c&0xF])
62 start = i + 1
63 }
64
65 if escape && c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
66 if start < i {
67 dst = append(dst, src[start:i]...)
68 }
69 dst = append(dst, '\\', 'u', '2', '0', '2', hex[src[i+2]&0xF])
70 start = i + 3
71 }
72 v := scan.step(scan, c)
73 if v >= scanSkipSpace {
74 if v == scanError {
75 break
76 }
77 if start < i {
78 dst = append(dst, src[start:i]...)
79 }
80 start = i + 1
81 }
82 }
83 if scan.eof() == scanError {
84 return dst[:origLen], scan.err
85 }
86 if start < len(src) {
87 dst = append(dst, src[start:]...)
88 }
89 return dst, nil
90 }
91
92 func appendNewline(dst []byte, prefix, indent string, depth int) []byte {
93 dst = append(dst, '\n')
94 dst = append(dst, prefix...)
95 for i := 0; i < depth; i++ {
96 dst = append(dst, indent...)
97 }
98 return dst
99 }
100
101
102
103
104
105
106
107 const indentGrowthFactor = 2
108
109
110
111
112
113
114
115
116
117
118
119
120 func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
121 dst.Grow(indentGrowthFactor * len(src))
122 b := dst.AvailableBuffer()
123 b, err := appendIndent(b, src, prefix, indent)
124 dst.Write(b)
125 return err
126 }
127
128 func appendIndent(dst, src []byte, prefix, indent string) ([]byte, error) {
129 origLen := len(dst)
130 scan := newScanner()
131 defer freeScanner(scan)
132 needIndent := false
133 depth := 0
134 for _, c := range src {
135 scan.bytes++
136 v := scan.step(scan, c)
137 if v == scanSkipSpace {
138 continue
139 }
140 if v == scanError {
141 break
142 }
143 if needIndent && v != scanEndObject && v != scanEndArray {
144 needIndent = false
145 depth++
146 dst = appendNewline(dst, prefix, indent, depth)
147 }
148
149
150
151 if v == scanContinue {
152 dst = append(dst, c)
153 continue
154 }
155
156
157 switch c {
158 case '{', '[':
159
160 needIndent = true
161 dst = append(dst, c)
162 case ',':
163 dst = append(dst, c)
164 dst = appendNewline(dst, prefix, indent, depth)
165 case ':':
166 dst = append(dst, c, ' ')
167 case '}', ']':
168 if needIndent {
169
170 needIndent = false
171 } else {
172 depth--
173 dst = appendNewline(dst, prefix, indent, depth)
174 }
175 dst = append(dst, c)
176 default:
177 dst = append(dst, c)
178 }
179 }
180 if scan.eof() == scanError {
181 return dst[:origLen], scan.err
182 }
183 return dst, nil
184 }
185
View as plain text