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