2
* Copyright 2021 ByteDance Inc.
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
26
`github.com/bytedance/sonic/internal/native`
27
`github.com/bytedance/sonic/internal/native/types`
28
`github.com/bytedance/sonic/internal/rt`
29
`github.com/bytedance/sonic/utf8`
30
`github.com/bytedance/sonic/option`
33
// Options is a set of encoding options.
40
bitNoQuoteTextMarshaler
43
bitNoValidateJSONMarshaler
45
// used for recursive compile
50
// SortMapKeys indicates that the keys of a map needs to be sorted
51
// before serializing into JSON.
52
// WARNING: This hurts performance A LOT, USE WITH CARE.
53
SortMapKeys Options = 1 << bitSortMapKeys
55
// EscapeHTML indicates encoder to escape all HTML characters
56
// after serializing into JSON (see https://pkg.go.dev/encoding/json#HTMLEscape).
57
// WARNING: This hurts performance A LOT, USE WITH CARE.
58
EscapeHTML Options = 1 << bitEscapeHTML
60
// CompactMarshaler indicates that the output JSON from json.Marshaler
61
// is always compact and needs no validation
62
CompactMarshaler Options = 1 << bitCompactMarshaler
64
// NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler
65
// is always escaped string and needs no quoting
66
NoQuoteTextMarshaler Options = 1 << bitNoQuoteTextMarshaler
68
// NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}',
70
NoNullSliceOrMap Options = 1 << bitNoNullSliceOrMap
72
// ValidateString indicates that encoder should validate the input string
73
// before encoding it into JSON.
74
ValidateString Options = 1 << bitValidateString
76
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
77
// after encoding the JSONMarshaler to JSON.
78
NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler
80
// CompatibleWithStd is used to be compatible with std encoder.
81
CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler
84
// Encoder represents a specific set of encoder configurations.
91
// Encode returns the JSON encoding of v.
92
func (self *Encoder) Encode(v interface{}) ([]byte, error) {
93
if self.indent != "" || self.prefix != "" {
94
return EncodeIndented(v, self.prefix, self.indent, self.Opts)
96
return Encode(v, self.Opts)
99
// SortKeys enables the SortMapKeys option.
100
func (self *Encoder) SortKeys() *Encoder {
101
self.Opts |= SortMapKeys
105
// SetEscapeHTML specifies if option EscapeHTML opens
106
func (self *Encoder) SetEscapeHTML(f bool) {
108
self.Opts |= EscapeHTML
110
self.Opts &= ^EscapeHTML
114
// SetValidateString specifies if option ValidateString opens
115
func (self *Encoder) SetValidateString(f bool) {
117
self.Opts |= ValidateString
119
self.Opts &= ^ValidateString
123
// SetNoValidateJSONMarshaler specifies if option NoValidateJSONMarshaler opens
124
func (self *Encoder) SetNoValidateJSONMarshaler(f bool) {
126
self.Opts |= NoValidateJSONMarshaler
128
self.Opts &= ^NoValidateJSONMarshaler
132
// SetCompactMarshaler specifies if option CompactMarshaler opens
133
func (self *Encoder) SetCompactMarshaler(f bool) {
135
self.Opts |= CompactMarshaler
137
self.Opts &= ^CompactMarshaler
141
// SetNoQuoteTextMarshaler specifies if option NoQuoteTextMarshaler opens
142
func (self *Encoder) SetNoQuoteTextMarshaler(f bool) {
144
self.Opts |= NoQuoteTextMarshaler
146
self.Opts &= ^NoQuoteTextMarshaler
150
// SetIndent instructs the encoder to format each subsequent encoded
151
// value as if indented by the package-level function EncodeIndent().
152
// Calling SetIndent("", "") disables indentation.
153
func (enc *Encoder) SetIndent(prefix, indent string) {
158
// Quote returns the JSON-quoted version of s.
159
func Quote(s string) string {
163
/* check for empty string */
168
/* allocate space for result */
170
p = make([]byte, 0, n)
172
/* call the encoder */
173
_ = encodeString(&p, s)
177
// Encode returns the JSON encoding of val, encoded with opts.
178
func Encode(val interface{}, opts Options) ([]byte, error) {
182
err := encodeInto(&buf, val, opts)
184
/* check for errors */
190
/* htmlescape or correct UTF-8 if opts enable */
192
buf = encodeFinish(old, opts)
193
pbuf := ((*rt.GoSlice)(unsafe.Pointer(&buf))).Ptr
194
pold := ((*rt.GoSlice)(unsafe.Pointer(&old))).Ptr
196
/* return when allocated a new buffer */
202
/* make a copy of the result */
203
ret = make([]byte, len(buf))
207
/* return the buffer into pool */
211
// EncodeInto is like Encode but uses a user-supplied buffer instead of allocating
213
func EncodeInto(buf *[]byte, val interface{}, opts Options) error {
214
err := encodeInto(buf, val, opts)
218
*buf = encodeFinish(*buf, opts)
222
func encodeInto(buf *[]byte, val interface{}, opts Options) error {
224
efv := rt.UnpackEface(val)
225
err := encodeTypedPointer(buf, efv.Type, &efv.Value, stk, uint64(opts))
227
/* return the stack into pool */
234
runtime.KeepAlive(buf)
235
runtime.KeepAlive(efv)
239
func encodeFinish(buf []byte, opts Options) []byte {
240
if opts & EscapeHTML != 0 {
241
buf = HTMLEscape(nil, buf)
243
if opts & ValidateString != 0 && !utf8.Validate(buf) {
244
buf = utf8.CorrectWith(nil, buf, `\ufffd`)
249
var typeByte = rt.UnpackType(reflect.TypeOf(byte(0)))
251
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
252
// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
253
// so that the JSON will be safe to embed inside HTML <script> tags.
254
// For historical reasons, web browsers don't honor standard HTML
255
// escaping within <script> tags, so an alternative JSON encoding must
257
func HTMLEscape(dst []byte, src []byte) []byte {
258
return htmlEscape(dst, src)
261
// EncodeIndented is like Encode but applies Indent to format the output.
262
// Each JSON element in the output will begin on a new line beginning with prefix
263
// followed by one or more copies of indent according to the indentation nesting.
264
func EncodeIndented(val interface{}, prefix string, indent string, opts Options) ([]byte, error) {
267
var buf *bytes.Buffer
269
/* encode into the buffer */
271
err = EncodeInto(&out, val, opts)
273
/* check for errors */
279
/* indent the JSON */
281
err = json.Indent(buf, out, prefix, indent)
283
/* check for errors */
290
/* copy to the result buffer */
291
ret := make([]byte, buf.Len())
292
copy(ret, buf.Bytes())
294
/* return the buffers into pool */
300
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
301
// order to reduce the first-hit latency.
303
// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
304
// a compile option to set the depth of recursive compile for the nested struct type.
305
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
306
cfg := option.DefaultCompileOptions()
307
for _, opt := range opts {
310
return pretouchRec(map[reflect.Type]uint8{vt: 0}, cfg)
313
// Valid validates json and returns first non-blank character position,
314
// if it is only one valid json value.
315
// Otherwise returns invalid character position using start.
317
// Note: it does not check for the invalid UTF-8 characters.
318
func Valid(data []byte) (ok bool, start int) {
323
s := rt.Mem2Str(data)
325
m := types.NewStateMachine()
326
ret := native.ValidateOne(&s, &p, m)
327
types.FreeStateMachine(m)
333
/* check for trailing spaces */
335
if (types.SPACE_MASK & (1 << data[p])) == 0 {