encoder_compat.go raw

   1  // +build !amd64,!arm64 go1.25 !go1.17 arm64,!go1.20
   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      `github.com/bytedance/sonic/internal/compat`
  29  )
  30  
  31  func init() {
  32      compat.Warn("sonic/encoder")
  33  }
  34  
  35  // EnableFallback indicates if encoder use fallback
  36  const EnableFallback = true
  37  
  38  // Options is a set of encoding options.
  39  type Options uint64
  40  
  41  const (
  42      bitSortMapKeys          = iota
  43      bitEscapeHTML          
  44      bitCompactMarshaler
  45      bitNoQuoteTextMarshaler
  46      bitNoNullSliceOrMap
  47      bitValidateString
  48      bitNoValidateJSONMarshaler
  49      bitNoEncoderNewline
  50  
  51      // used for recursive compile
  52      bitPointerValue = 63
  53  )
  54  
  55  const (
  56      // SortMapKeys indicates that the keys of a map needs to be sorted 
  57      // before serializing into JSON.
  58      // WARNING: This hurts performance A LOT, USE WITH CARE.
  59      SortMapKeys          Options = 1 << bitSortMapKeys
  60  
  61      // EscapeHTML indicates encoder to escape all HTML characters 
  62      // after serializing into JSON (see https://pkg.go.dev/encoding/json#HTMLEscape).
  63      // WARNING: This hurts performance A LOT, USE WITH CARE.
  64      EscapeHTML           Options = 1 << bitEscapeHTML
  65  
  66      // CompactMarshaler indicates that the output JSON from json.Marshaler 
  67      // is always compact and needs no validation 
  68      CompactMarshaler     Options = 1 << bitCompactMarshaler
  69  
  70      // NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler 
  71      // is always escaped string and needs no quoting
  72      NoQuoteTextMarshaler Options = 1 << bitNoQuoteTextMarshaler
  73  
  74      // NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}',
  75      // instead of 'null'
  76      NoNullSliceOrMap     Options = 1 << bitNoNullSliceOrMap
  77  
  78      // ValidateString indicates that encoder should validate the input string
  79      // before encoding it into JSON.
  80      ValidateString       Options = 1 << bitValidateString
  81  
  82      // NoValidateJSONMarshaler indicates that the encoder should not validate the output string
  83      // after encoding the JSONMarshaler to JSON.
  84      NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler
  85  
  86      // NoEncoderNewline indicates that the encoder should not add a newline after every message
  87      NoEncoderNewline Options = 1 << bitNoEncoderNewline
  88    
  89      // CompatibleWithStd is used to be compatible with std encoder.
  90      CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler
  91  )
  92  
  93  // Encoder represents a specific set of encoder configurations.
  94  type Encoder struct {
  95      Opts Options
  96      prefix string
  97      indent string
  98  }
  99  
 100  // Encode returns the JSON encoding of v.
 101  func (self *Encoder) Encode(v interface{}) ([]byte, error) {
 102      if self.indent != "" || self.prefix != "" { 
 103          return EncodeIndented(v, self.prefix, self.indent, self.Opts)
 104      }
 105      return Encode(v, self.Opts)
 106  }
 107  
 108  // SortKeys enables the SortMapKeys option.
 109  func (self *Encoder) SortKeys() *Encoder {
 110      self.Opts |= SortMapKeys
 111      return self
 112  }
 113  
 114  // SetEscapeHTML specifies if option EscapeHTML opens
 115  func (self *Encoder) SetEscapeHTML(f bool) {
 116      if f {
 117          self.Opts |= EscapeHTML
 118      } else {
 119          self.Opts &= ^EscapeHTML
 120      }
 121  }
 122  
 123  // SetValidateString specifies if option ValidateString opens
 124  func (self *Encoder) SetValidateString(f bool) {
 125      if f {
 126          self.Opts |= ValidateString
 127      } else {
 128          self.Opts &= ^ValidateString
 129      }
 130  }
 131  
 132  // SetNoValidateJSONMarshaler specifies if option NoValidateJSONMarshaler opens
 133  func (self *Encoder) SetNoValidateJSONMarshaler(f bool) {
 134      if f {
 135          self.Opts |= NoValidateJSONMarshaler
 136      } else {
 137          self.Opts &= ^NoValidateJSONMarshaler
 138      }
 139  }
 140  
 141  // SetNoEncoderNewline specifies if option NoEncoderNewline opens
 142  func (self *Encoder) SetNoEncoderNewline(f bool) {
 143      if f {
 144          self.Opts |= NoEncoderNewline
 145      } else {
 146          self.Opts &= ^NoEncoderNewline
 147      }
 148  }
 149  
 150  // SetCompactMarshaler specifies if option CompactMarshaler opens
 151  func (self *Encoder) SetCompactMarshaler(f bool) {
 152      if f {
 153          self.Opts |= CompactMarshaler
 154      } else {
 155          self.Opts &= ^CompactMarshaler
 156      }
 157  }
 158  
 159  // SetNoQuoteTextMarshaler specifies if option NoQuoteTextMarshaler opens
 160  func (self *Encoder) SetNoQuoteTextMarshaler(f bool) {
 161      if f {
 162          self.Opts |= NoQuoteTextMarshaler
 163      } else {
 164          self.Opts &= ^NoQuoteTextMarshaler
 165      }
 166  }
 167  
 168  // SetIndent instructs the encoder to format each subsequent encoded
 169  // value as if indented by the package-level function EncodeIndent().
 170  // Calling SetIndent("", "") disables indentation.
 171  func (enc *Encoder) SetIndent(prefix, indent string) {
 172      enc.prefix = prefix
 173      enc.indent = indent
 174  }
 175  
 176  // Quote returns the JSON-quoted version of s.
 177  func Quote(s string) string {
 178      /* check for empty string */
 179      if s == "" {
 180          return `""`
 181      }
 182  
 183      out, _ := json.Marshal(s)
 184      return string(out)
 185  }
 186  
 187  // Encode returns the JSON encoding of val, encoded with opts.
 188  func Encode(val interface{}, opts Options) ([]byte, error) {
 189     return json.Marshal(val)
 190  }
 191  
 192  // EncodeInto is like Encode but uses a user-supplied buffer instead of allocating
 193  // a new one.
 194  func EncodeInto(buf *[]byte, val interface{}, opts Options) error {
 195      if buf == nil {
 196          panic("user-supplied buffer buf is nil")
 197      }
 198      w := bytes.NewBuffer(*buf)
 199      enc := json.NewEncoder(w)
 200      enc.SetEscapeHTML((opts & EscapeHTML) != 0)
 201      err := enc.Encode(val)
 202      *buf = w.Bytes()
 203      l := len(*buf)
 204      if l > 0 && (opts & NoEncoderNewline != 0) && (*buf)[l-1] == '\n' {
 205          *buf = (*buf)[:l-1]
 206      }
 207      return err
 208  }
 209  
 210  // HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
 211  // characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
 212  // so that the JSON will be safe to embed inside HTML <script> tags.
 213  // For historical reasons, web browsers don't honor standard HTML
 214  // escaping within <script> tags, so an alternative JSON encoding must
 215  // be used.
 216  func HTMLEscape(dst []byte, src []byte) []byte {
 217     d := bytes.NewBuffer(dst)
 218     json.HTMLEscape(d, src)
 219     return d.Bytes()
 220  }
 221  
 222  // EncodeIndented is like Encode but applies Indent to format the output.
 223  // Each JSON element in the output will begin on a new line beginning with prefix
 224  // followed by one or more copies of indent according to the indentation nesting.
 225  func EncodeIndented(val interface{}, prefix string, indent string, opts Options) ([]byte, error) {
 226     w := bytes.NewBuffer([]byte{})
 227     enc := json.NewEncoder(w)
 228     enc.SetEscapeHTML((opts & EscapeHTML) != 0)
 229     enc.SetIndent(prefix, indent)
 230     err := enc.Encode(val)
 231     out := w.Bytes()
 232     return out, err
 233  }
 234  
 235  // Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
 236  // order to reduce the first-hit latency.
 237  //
 238  // Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
 239  // a compile option to set the depth of recursive compile for the nested struct type.
 240  func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
 241     return nil
 242  }
 243  
 244  // Valid validates json and returns first non-blank character position,
 245  // if it is only one valid json value.
 246  // Otherwise returns invalid character position using start.
 247  //
 248  // Note: it does not check for the invalid UTF-8 characters.
 249  func Valid(data []byte) (ok bool, start int) {
 250     return json.Valid(data), 0
 251  }
 252  
 253  // StreamEncoder uses io.Writer as 
 254  type StreamEncoder = json.Encoder
 255  
 256  // NewStreamEncoder adapts to encoding/json.NewDecoder API.
 257  //
 258  // NewStreamEncoder returns a new encoder that write to w.
 259  func NewStreamEncoder(w io.Writer) *StreamEncoder {
 260     return json.NewEncoder(w)
 261  }
 262  
 263