1 package json
2
3 import (
4 "context"
5 "io"
6 "os"
7 "unsafe"
8
9 "github.com/goccy/go-json/internal/encoder"
10 "github.com/goccy/go-json/internal/encoder/vm"
11 "github.com/goccy/go-json/internal/encoder/vm_color"
12 "github.com/goccy/go-json/internal/encoder/vm_color_indent"
13 "github.com/goccy/go-json/internal/encoder/vm_indent"
14 )
15
16
17 type Encoder struct {
18 w io.Writer
19 enabledIndent bool
20 enabledHTMLEscape bool
21 prefix string
22 indentStr string
23 }
24
25
26 func NewEncoder(w io.Writer) *Encoder {
27 return &Encoder{w: w, enabledHTMLEscape: true}
28 }
29
30
31
32
33 func (e *Encoder) Encode(v interface{}) error {
34 return e.EncodeWithOption(v)
35 }
36
37
38 func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc) error {
39 ctx := encoder.TakeRuntimeContext()
40 ctx.Option.Flag = 0
41
42 err := e.encodeWithOption(ctx, v, optFuncs...)
43
44 encoder.ReleaseRuntimeContext(ctx)
45 return err
46 }
47
48
49 func (e *Encoder) EncodeContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) error {
50 rctx := encoder.TakeRuntimeContext()
51 rctx.Option.Flag = 0
52 rctx.Option.Flag |= encoder.ContextOption
53 rctx.Option.Context = ctx
54
55 err := e.encodeWithOption(rctx, v, optFuncs...)
56
57 encoder.ReleaseRuntimeContext(rctx)
58 return err
59 }
60
61 func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
62 if e.enabledHTMLEscape {
63 ctx.Option.Flag |= encoder.HTMLEscapeOption
64 }
65 ctx.Option.Flag |= encoder.NormalizeUTF8Option
66 ctx.Option.DebugOut = os.Stdout
67 for _, optFunc := range optFuncs {
68 optFunc(ctx.Option)
69 }
70 var (
71 buf []byte
72 err error
73 )
74 if e.enabledIndent {
75 buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr)
76 } else {
77 buf, err = encode(ctx, v)
78 }
79 if err != nil {
80 return err
81 }
82 if e.enabledIndent {
83 buf = buf[:len(buf)-2]
84 } else {
85 buf = buf[:len(buf)-1]
86 }
87 buf = append(buf, '\n')
88 if _, err := e.w.Write(buf); err != nil {
89 return err
90 }
91 return nil
92 }
93
94
95
96
97
98 func (e *Encoder) SetEscapeHTML(on bool) {
99 e.enabledHTMLEscape = on
100 }
101
102
103
104 func (e *Encoder) SetIndent(prefix, indent string) {
105 if prefix == "" && indent == "" {
106 e.enabledIndent = false
107 return
108 }
109 e.prefix = prefix
110 e.indentStr = indent
111 e.enabledIndent = true
112 }
113
114 func marshalContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
115 rctx := encoder.TakeRuntimeContext()
116 rctx.Option.Flag = 0
117 rctx.Option.Flag = encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option | encoder.ContextOption
118 rctx.Option.Context = ctx
119 for _, optFunc := range optFuncs {
120 optFunc(rctx.Option)
121 }
122
123 buf, err := encode(rctx, v)
124 if err != nil {
125 encoder.ReleaseRuntimeContext(rctx)
126 return nil, err
127 }
128
129
130
131
132
133 buf = buf[:len(buf)-1]
134 copied := make([]byte, len(buf))
135 copy(copied, buf)
136
137 encoder.ReleaseRuntimeContext(rctx)
138 return copied, nil
139 }
140
141 func marshal(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
142 ctx := encoder.TakeRuntimeContext()
143
144 ctx.Option.Flag = 0
145 ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option)
146 for _, optFunc := range optFuncs {
147 optFunc(ctx.Option)
148 }
149
150 buf, err := encode(ctx, v)
151 if err != nil {
152 encoder.ReleaseRuntimeContext(ctx)
153 return nil, err
154 }
155
156
157
158
159
160 buf = buf[:len(buf)-1]
161 copied := make([]byte, len(buf))
162 copy(copied, buf)
163
164 encoder.ReleaseRuntimeContext(ctx)
165 return copied, nil
166 }
167
168 func marshalNoEscape(v interface{}) ([]byte, error) {
169 ctx := encoder.TakeRuntimeContext()
170
171 ctx.Option.Flag = 0
172 ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option)
173
174 buf, err := encodeNoEscape(ctx, v)
175 if err != nil {
176 encoder.ReleaseRuntimeContext(ctx)
177 return nil, err
178 }
179
180
181
182
183
184 buf = buf[:len(buf)-1]
185 copied := make([]byte, len(buf))
186 copy(copied, buf)
187
188 encoder.ReleaseRuntimeContext(ctx)
189 return copied, nil
190 }
191
192 func marshalIndent(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
193 ctx := encoder.TakeRuntimeContext()
194
195 ctx.Option.Flag = 0
196 ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option | encoder.IndentOption)
197 for _, optFunc := range optFuncs {
198 optFunc(ctx.Option)
199 }
200
201 buf, err := encodeIndent(ctx, v, prefix, indent)
202 if err != nil {
203 encoder.ReleaseRuntimeContext(ctx)
204 return nil, err
205 }
206
207 buf = buf[:len(buf)-2]
208 copied := make([]byte, len(buf))
209 copy(copied, buf)
210
211 encoder.ReleaseRuntimeContext(ctx)
212 return copied, nil
213 }
214
215 func encode(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
216 b := ctx.Buf[:0]
217 if v == nil {
218 b = encoder.AppendNull(ctx, b)
219 b = encoder.AppendComma(ctx, b)
220 return b, nil
221 }
222 header := (*emptyInterface)(unsafe.Pointer(&v))
223 typ := header.typ
224
225 typeptr := uintptr(unsafe.Pointer(typ))
226 codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
227 if err != nil {
228 return nil, err
229 }
230
231 p := uintptr(header.ptr)
232 ctx.Init(p, codeSet.CodeLength)
233 ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
234
235 buf, err := encodeRunCode(ctx, b, codeSet)
236 if err != nil {
237 return nil, err
238 }
239 ctx.Buf = buf
240 return buf, nil
241 }
242
243 func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
244 b := ctx.Buf[:0]
245 if v == nil {
246 b = encoder.AppendNull(ctx, b)
247 b = encoder.AppendComma(ctx, b)
248 return b, nil
249 }
250 header := (*emptyInterface)(unsafe.Pointer(&v))
251 typ := header.typ
252
253 typeptr := uintptr(unsafe.Pointer(typ))
254 codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
255 if err != nil {
256 return nil, err
257 }
258
259 p := uintptr(header.ptr)
260 ctx.Init(p, codeSet.CodeLength)
261 buf, err := encodeRunCode(ctx, b, codeSet)
262 if err != nil {
263 return nil, err
264 }
265
266 ctx.Buf = buf
267 return buf, nil
268 }
269
270 func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string) ([]byte, error) {
271 b := ctx.Buf[:0]
272 if v == nil {
273 b = encoder.AppendNull(ctx, b)
274 b = encoder.AppendCommaIndent(ctx, b)
275 return b, nil
276 }
277 header := (*emptyInterface)(unsafe.Pointer(&v))
278 typ := header.typ
279
280 typeptr := uintptr(unsafe.Pointer(typ))
281 codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
282 if err != nil {
283 return nil, err
284 }
285
286 p := uintptr(header.ptr)
287 ctx.Init(p, codeSet.CodeLength)
288 buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent)
289
290 ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
291
292 if err != nil {
293 return nil, err
294 }
295
296 ctx.Buf = buf
297 return buf, nil
298 }
299
300 func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
301 if (ctx.Option.Flag & encoder.DebugOption) != 0 {
302 if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
303 return vm_color.DebugRun(ctx, b, codeSet)
304 }
305 return vm.DebugRun(ctx, b, codeSet)
306 }
307 if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
308 return vm_color.Run(ctx, b, codeSet)
309 }
310 return vm.Run(ctx, b, codeSet)
311 }
312
313 func encodeRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string) ([]byte, error) {
314 ctx.Prefix = []byte(prefix)
315 ctx.IndentStr = []byte(indent)
316 if (ctx.Option.Flag & encoder.DebugOption) != 0 {
317 if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
318 return vm_color_indent.DebugRun(ctx, b, codeSet)
319 }
320 return vm_indent.DebugRun(ctx, b, codeSet)
321 }
322 if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
323 return vm_color_indent.Run(ctx, b, codeSet)
324 }
325 return vm_indent.Run(ctx, b, codeSet)
326 }
327
View as plain text