encoder_compat.go (7261B)
1 // +build !amd64 go1.21 2 3 /* 4 * Copyright 2023 ByteDance Inc. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package encoder 20 21 import ( 22 `io` 23 `bytes` 24 `encoding/json` 25 `reflect` 26 27 `github.com/bytedance/sonic/option` 28 ) 29 30 // Options is a set of encoding options. 31 type Options uint64 32 33 const ( 34 bitSortMapKeys = iota 35 bitEscapeHTML 36 bitCompactMarshaler 37 bitNoQuoteTextMarshaler 38 bitNoNullSliceOrMap 39 bitValidateString 40 41 // used for recursive compile 42 bitPointerValue = 63 43 ) 44 45 const ( 46 // SortMapKeys indicates that the keys of a map needs to be sorted 47 // before serializing into JSON. 48 // WARNING: This hurts performance A LOT, USE WITH CARE. 49 SortMapKeys Options = 1 << bitSortMapKeys 50 51 // EscapeHTML indicates encoder to escape all HTML characters 52 // after serializing into JSON (see https://pkg.go.dev/encoding/json#HTMLEscape). 53 // WARNING: This hurts performance A LOT, USE WITH CARE. 54 EscapeHTML Options = 1 << bitEscapeHTML 55 56 // CompactMarshaler indicates that the output JSON from json.Marshaler 57 // is always compact and needs no validation 58 CompactMarshaler Options = 1 << bitCompactMarshaler 59 60 // NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler 61 // is always escaped string and needs no quoting 62 NoQuoteTextMarshaler Options = 1 << bitNoQuoteTextMarshaler 63 64 // NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}', 65 // instead of 'null' 66 NoNullSliceOrMap Options = 1 << bitNoNullSliceOrMap 67 68 // ValidateString indicates that encoder should validate the input string 69 // before encoding it into JSON. 70 ValidateString Options = 1 << bitValidateString 71 72 // CompatibleWithStd is used to be compatible with std encoder. 73 CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler 74 ) 75 76 // Encoder represents a specific set of encoder configurations. 77 type Encoder struct { 78 Opts Options 79 prefix string 80 indent string 81 } 82 83 // Encode returns the JSON encoding of v. 84 func (self *Encoder) Encode(v interface{}) ([]byte, error) { 85 if self.indent != "" || self.prefix != "" { 86 return EncodeIndented(v, self.prefix, self.indent, self.Opts) 87 } 88 return Encode(v, self.Opts) 89 } 90 91 // SortKeys enables the SortMapKeys option. 92 func (self *Encoder) SortKeys() *Encoder { 93 self.Opts |= SortMapKeys 94 return self 95 } 96 97 // SetEscapeHTML specifies if option EscapeHTML opens 98 func (self *Encoder) SetEscapeHTML(f bool) { 99 if f { 100 self.Opts |= EscapeHTML 101 } else { 102 self.Opts &= ^EscapeHTML 103 } 104 } 105 106 // SetValidateString specifies if option ValidateString opens 107 func (self *Encoder) SetValidateString(f bool) { 108 if f { 109 self.Opts |= ValidateString 110 } else { 111 self.Opts &= ^ValidateString 112 } 113 } 114 115 // SetCompactMarshaler specifies if option CompactMarshaler opens 116 func (self *Encoder) SetCompactMarshaler(f bool) { 117 if f { 118 self.Opts |= CompactMarshaler 119 } else { 120 self.Opts &= ^CompactMarshaler 121 } 122 } 123 124 // SetNoQuoteTextMarshaler specifies if option NoQuoteTextMarshaler opens 125 func (self *Encoder) SetNoQuoteTextMarshaler(f bool) { 126 if f { 127 self.Opts |= NoQuoteTextMarshaler 128 } else { 129 self.Opts &= ^NoQuoteTextMarshaler 130 } 131 } 132 133 // SetIndent instructs the encoder to format each subsequent encoded 134 // value as if indented by the package-level function EncodeIndent(). 135 // Calling SetIndent("", "") disables indentation. 136 func (enc *Encoder) SetIndent(prefix, indent string) { 137 enc.prefix = prefix 138 enc.indent = indent 139 } 140 141 // Quote returns the JSON-quoted version of s. 142 func Quote(s string) string { 143 /* check for empty string */ 144 if s == "" { 145 return `""` 146 } 147 148 out, _ := json.Marshal(s) 149 return string(out) 150 } 151 152 // Encode returns the JSON encoding of val, encoded with opts. 153 func Encode(val interface{}, opts Options) ([]byte, error) { 154 return json.Marshal(val) 155 } 156 157 // EncodeInto is like Encode but uses a user-supplied buffer instead of allocating 158 // a new one. 159 func EncodeInto(buf *[]byte, val interface{}, opts Options) error { 160 if buf == nil { 161 panic("user-supplied buffer buf is nil") 162 } 163 w := bytes.NewBuffer(*buf) 164 enc := json.NewEncoder(w) 165 enc.SetEscapeHTML((opts & EscapeHTML) != 0) 166 err := enc.Encode(val) 167 *buf = w.Bytes() 168 return err 169 } 170 171 // HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 172 // characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 173 // so that the JSON will be safe to embed inside HTML <script> tags. 174 // For historical reasons, web browsers don't honor standard HTML 175 // escaping within <script> tags, so an alternative JSON encoding must 176 // be used. 177 func HTMLEscape(dst []byte, src []byte) []byte { 178 d := bytes.NewBuffer(dst) 179 json.HTMLEscape(d, src) 180 return d.Bytes() 181 } 182 183 // EncodeIndented is like Encode but applies Indent to format the output. 184 // Each JSON element in the output will begin on a new line beginning with prefix 185 // followed by one or more copies of indent according to the indentation nesting. 186 func EncodeIndented(val interface{}, prefix string, indent string, opts Options) ([]byte, error) { 187 w := bytes.NewBuffer([]byte{}) 188 enc := json.NewEncoder(w) 189 enc.SetEscapeHTML((opts & EscapeHTML) != 0) 190 enc.SetIndent(prefix, indent) 191 err := enc.Encode(val) 192 out := w.Bytes() 193 return out, err 194 } 195 196 // Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in 197 // order to reduce the first-hit latency. 198 // 199 // Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is 200 // a compile option to set the depth of recursive compile for the nested struct type. 201 func Pretouch(vt reflect.Type, opts ...option.CompileOption) error { 202 return nil 203 } 204 205 // Valid validates json and returns first non-blank character position, 206 // if it is only one valid json value. 207 // Otherwise returns invalid character position using start. 208 // 209 // Note: it does not check for the invalid UTF-8 characters. 210 func Valid(data []byte) (ok bool, start int) { 211 return json.Valid(data), 0 212 } 213 214 // StreamEncoder uses io.Writer as 215 type StreamEncoder struct { 216 w io.Writer 217 Encoder 218 } 219 220 // NewStreamEncoder adapts to encoding/json.NewDecoder API. 221 // 222 // NewStreamEncoder returns a new encoder that write to w. 223 func NewStreamEncoder(w io.Writer) *StreamEncoder { 224 return &StreamEncoder{w: w} 225 } 226 227 // Encode encodes interface{} as JSON to io.Writer 228 func (enc *StreamEncoder) Encode(val interface{}) (err error) { 229 jenc := json.NewEncoder(enc.w) 230 jenc.SetEscapeHTML((enc.Opts & EscapeHTML) != 0) 231 jenc.SetIndent(enc.prefix, enc.indent) 232 err = jenc.Encode(val) 233 return err 234 }