gotosocial/vendor/github.com/bytedance/sonic/internal/encoder/compiler.go
dependabot[bot] 4d423102c1
[chore]: Bump github.com/gin-contrib/gzip from 1.0.1 to 1.1.0 (#3639)
Bumps [github.com/gin-contrib/gzip](https://github.com/gin-contrib/gzip) from 1.0.1 to 1.1.0.
- [Release notes](https://github.com/gin-contrib/gzip/releases)
- [Changelog](https://github.com/gin-contrib/gzip/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/gin-contrib/gzip/compare/v1.0.1...v1.1.0)

---
updated-dependencies:
- dependency-name: github.com/gin-contrib/gzip
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 13:10:39 +00:00

677 lines
15 KiB
Go

/*
* Copyright 2021 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package encoder
import (
"reflect"
"unsafe"
"github.com/bytedance/sonic/internal/encoder/ir"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/encoder/vm"
"github.com/bytedance/sonic/internal/resolver"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/option"
)
func ForceUseVM() {
vm.SetCompiler(makeEncoderVM)
pretouchType = pretouchTypeVM
encodeTypedPointer = vm.EncodeTypedPointer
vars.UseVM = true
}
var encodeTypedPointer func(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *vars.Stack, fv uint64) error
func makeEncoderVM(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
pp, err := NewCompiler().Compile(vt.Pack(), ex[0].(bool))
if err != nil {
return nil, err
}
return &pp, nil
}
var pretouchType func(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[reflect.Type]uint8, error)
func pretouchTypeVM(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[reflect.Type]uint8, error) {
/* compile function */
compiler := NewCompiler().apply(opts)
/* find or compile */
vt := rt.UnpackType(_vt)
if val := vars.GetProgram(vt); val != nil {
return nil, nil
} else if _, err := vars.ComputeProgram(vt, makeEncoderVM, v == 1); err == nil {
return compiler.rec, nil
} else {
return nil, err
}
}
func pretouchRec(vtm map[reflect.Type]uint8, opts option.CompileOptions) error {
if opts.RecursiveDepth < 0 || len(vtm) == 0 {
return nil
}
next := make(map[reflect.Type]uint8)
for vt, v := range vtm {
sub, err := pretouchType(vt, opts, v)
if err != nil {
return err
}
for svt, v := range sub {
next[svt] = v
}
}
opts.RecursiveDepth -= 1
return pretouchRec(next, opts)
}
type Compiler struct {
opts option.CompileOptions
pv bool
tab map[reflect.Type]bool
rec map[reflect.Type]uint8
}
func NewCompiler() *Compiler {
return &Compiler{
opts: option.DefaultCompileOptions(),
tab: map[reflect.Type]bool{},
rec: map[reflect.Type]uint8{},
}
}
func (self *Compiler) apply(opts option.CompileOptions) *Compiler {
self.opts = opts
if self.opts.RecursiveDepth > 0 {
self.rec = map[reflect.Type]uint8{}
}
return self
}
func (self *Compiler) rescue(ep *error) {
if val := recover(); val != nil {
if err, ok := val.(error); ok {
*ep = err
} else {
panic(val)
}
}
}
func (self *Compiler) Compile(vt reflect.Type, pv bool) (ret ir.Program, err error) {
defer self.rescue(&err)
self.compileOne(&ret, 0, vt, pv)
return
}
func (self *Compiler) compileOne(p *ir.Program, sp int, vt reflect.Type, pv bool) {
if self.tab[vt] {
p.Vp(ir.OP_recurse, vt, pv)
} else {
self.compileRec(p, sp, vt, pv)
}
}
func (self *Compiler) tryCompileMarshaler(p *ir.Program, vt reflect.Type, pv bool) bool {
pt := reflect.PtrTo(vt)
/* check for addressable `json.Marshaler` with pointer receiver */
if pv && pt.Implements(vars.JsonMarshalerType) {
addMarshalerOp(p, ir.OP_marshal_p, pt, vars.JsonMarshalerType)
return true
}
/* check for `json.Marshaler` */
if vt.Implements(vars.JsonMarshalerType) {
self.compileMarshaler(p, ir.OP_marshal, vt, vars.JsonMarshalerType)
return true
}
/* check for addressable `encoding.TextMarshaler` with pointer receiver */
if pv && pt.Implements(vars.EncodingTextMarshalerType) {
addMarshalerOp(p, ir.OP_marshal_text_p, pt, vars.EncodingTextMarshalerType)
return true
}
/* check for `encoding.TextMarshaler` */
if vt.Implements(vars.EncodingTextMarshalerType) {
self.compileMarshaler(p, ir.OP_marshal_text, vt, vars.EncodingTextMarshalerType)
return true
}
return false
}
func (self *Compiler) compileRec(p *ir.Program, sp int, vt reflect.Type, pv bool) {
pr := self.pv
if self.tryCompileMarshaler(p, vt, pv) {
return
}
/* enter the recursion, and compile the type */
self.pv = pv
self.tab[vt] = true
self.compileOps(p, sp, vt)
/* exit the recursion */
self.pv = pr
delete(self.tab, vt)
}
func (self *Compiler) compileOps(p *ir.Program, sp int, vt reflect.Type) {
switch vt.Kind() {
case reflect.Bool:
p.Add(ir.OP_bool)
case reflect.Int:
p.Add(ir.OP_int())
case reflect.Int8:
p.Add(ir.OP_i8)
case reflect.Int16:
p.Add(ir.OP_i16)
case reflect.Int32:
p.Add(ir.OP_i32)
case reflect.Int64:
p.Add(ir.OP_i64)
case reflect.Uint:
p.Add(ir.OP_uint())
case reflect.Uint8:
p.Add(ir.OP_u8)
case reflect.Uint16:
p.Add(ir.OP_u16)
case reflect.Uint32:
p.Add(ir.OP_u32)
case reflect.Uint64:
p.Add(ir.OP_u64)
case reflect.Uintptr:
p.Add(ir.OP_uintptr())
case reflect.Float32:
p.Add(ir.OP_f32)
case reflect.Float64:
p.Add(ir.OP_f64)
case reflect.String:
self.compileString(p, vt)
case reflect.Array:
self.compileArray(p, sp, vt.Elem(), vt.Len())
case reflect.Interface:
self.compileInterface(p, vt)
case reflect.Map:
self.compileMap(p, sp, vt)
case reflect.Ptr:
self.compilePtr(p, sp, vt.Elem())
case reflect.Slice:
self.compileSlice(p, sp, vt.Elem())
case reflect.Struct:
self.compileStruct(p, sp, vt)
default:
panic(vars.Error_type(vt))
}
}
func (self *Compiler) compileNil(p *ir.Program, sp int, vt reflect.Type, nil_op ir.Op, fn func(*ir.Program, int, reflect.Type)) {
x := p.PC()
p.Add(ir.OP_is_nil)
fn(p, sp, vt)
e := p.PC()
p.Add(ir.OP_goto)
p.Pin(x)
p.Add(nil_op)
p.Pin(e)
}
func (self *Compiler) compilePtr(p *ir.Program, sp int, vt reflect.Type) {
self.compileNil(p, sp, vt, ir.OP_null, self.compilePtrBody)
}
func (self *Compiler) compilePtrBody(p *ir.Program, sp int, vt reflect.Type) {
p.Tag(sp)
p.Add(ir.OP_save)
p.Add(ir.OP_deref)
self.compileOne(p, sp+1, vt, true)
p.Add(ir.OP_drop)
}
func (self *Compiler) compileMap(p *ir.Program, sp int, vt reflect.Type) {
self.compileNil(p, sp, vt, ir.OP_empty_obj, self.compileMapBody)
}
func (self *Compiler) compileMapBody(p *ir.Program, sp int, vt reflect.Type) {
p.Tag(sp + 1)
p.Int(ir.OP_byte, '{')
e := p.PC()
p.Add(ir.OP_is_zero_map)
p.Add(ir.OP_save)
p.Rtt(ir.OP_map_iter, vt)
p.Add(ir.OP_save)
i := p.PC()
p.Add(ir.OP_map_check_key)
u := p.PC()
p.Add(ir.OP_map_write_key)
self.compileMapBodyKey(p, vt.Key())
p.Pin(u)
p.Int(ir.OP_byte, ':')
p.Add(ir.OP_map_value_next)
self.compileOne(p, sp+2, vt.Elem(), false)
j := p.PC()
p.Add(ir.OP_map_check_key)
p.Int(ir.OP_byte, ',')
v := p.PC()
p.Add(ir.OP_map_write_key)
self.compileMapBodyKey(p, vt.Key())
p.Pin(v)
p.Int(ir.OP_byte, ':')
p.Add(ir.OP_map_value_next)
self.compileOne(p, sp+2, vt.Elem(), false)
p.Int(ir.OP_goto, j)
p.Pin(i)
p.Pin(j)
p.Add(ir.OP_map_stop)
p.Add(ir.OP_drop_2)
p.Pin(e)
p.Int(ir.OP_byte, '}')
}
func (self *Compiler) compileMapBodyKey(p *ir.Program, vk reflect.Type) {
if !vk.Implements(vars.EncodingTextMarshalerType) {
self.compileMapBodyTextKey(p, vk)
} else {
self.compileMapBodyUtextKey(p, vk)
}
}
func (self *Compiler) compileMapBodyTextKey(p *ir.Program, vk reflect.Type) {
switch vk.Kind() {
case reflect.Invalid:
panic("map key is nil")
case reflect.Bool:
p.Key(ir.OP_bool)
case reflect.Int:
p.Key(ir.OP_int())
case reflect.Int8:
p.Key(ir.OP_i8)
case reflect.Int16:
p.Key(ir.OP_i16)
case reflect.Int32:
p.Key(ir.OP_i32)
case reflect.Int64:
p.Key(ir.OP_i64)
case reflect.Uint:
p.Key(ir.OP_uint())
case reflect.Uint8:
p.Key(ir.OP_u8)
case reflect.Uint16:
p.Key(ir.OP_u16)
case reflect.Uint32:
p.Key(ir.OP_u32)
case reflect.Uint64:
p.Key(ir.OP_u64)
case reflect.Uintptr:
p.Key(ir.OP_uintptr())
case reflect.Float32:
p.Key(ir.OP_f32)
case reflect.Float64:
p.Key(ir.OP_f64)
case reflect.String:
self.compileString(p, vk)
default:
panic(vars.Error_type(vk))
}
}
func (self *Compiler) compileMapBodyUtextKey(p *ir.Program, vk reflect.Type) {
if vk.Kind() != reflect.Ptr {
addMarshalerOp(p, ir.OP_marshal_text, vk, vars.EncodingTextMarshalerType)
} else {
self.compileMapBodyUtextPtr(p, vk)
}
}
func (self *Compiler) compileMapBodyUtextPtr(p *ir.Program, vk reflect.Type) {
i := p.PC()
p.Add(ir.OP_is_nil)
addMarshalerOp(p, ir.OP_marshal_text, vk, vars.EncodingTextMarshalerType)
j := p.PC()
p.Add(ir.OP_goto)
p.Pin(i)
p.Str(ir.OP_text, "\"\"")
p.Pin(j)
}
func (self *Compiler) compileSlice(p *ir.Program, sp int, vt reflect.Type) {
self.compileNil(p, sp, vt, ir.OP_empty_arr, self.compileSliceBody)
}
func (self *Compiler) compileSliceBody(p *ir.Program, sp int, vt reflect.Type) {
if vars.IsSimpleByte(vt) {
p.Add(ir.OP_bin)
} else {
self.compileSliceArray(p, sp, vt)
}
}
func (self *Compiler) compileSliceArray(p *ir.Program, sp int, vt reflect.Type) {
p.Tag(sp)
p.Int(ir.OP_byte, '[')
e := p.PC()
p.Add(ir.OP_is_nil)
p.Add(ir.OP_save)
p.Add(ir.OP_slice_len)
i := p.PC()
p.Rtt(ir.OP_slice_next, vt)
self.compileOne(p, sp+1, vt, true)
j := p.PC()
p.Rtt(ir.OP_slice_next, vt)
p.Int(ir.OP_byte, ',')
self.compileOne(p, sp+1, vt, true)
p.Int(ir.OP_goto, j)
p.Pin(i)
p.Pin(j)
p.Add(ir.OP_drop)
p.Pin(e)
p.Int(ir.OP_byte, ']')
}
func (self *Compiler) compileArray(p *ir.Program, sp int, vt reflect.Type, nb int) {
p.Tag(sp)
p.Int(ir.OP_byte, '[')
p.Add(ir.OP_save)
/* first item */
if nb != 0 {
self.compileOne(p, sp+1, vt, self.pv)
p.Add(ir.OP_load)
}
/* remaining items */
for i := 1; i < nb; i++ {
p.Int(ir.OP_byte, ',')
p.Int(ir.OP_index, i*int(vt.Size()))
self.compileOne(p, sp+1, vt, self.pv)
p.Add(ir.OP_load)
}
/* end of array */
p.Add(ir.OP_drop)
p.Int(ir.OP_byte, ']')
}
func (self *Compiler) compileString(p *ir.Program, vt reflect.Type) {
if vt != vars.JsonNumberType {
p.Add(ir.OP_str)
} else {
p.Add(ir.OP_number)
}
}
func (self *Compiler) compileStruct(p *ir.Program, sp int, vt reflect.Type) {
if sp >= self.opts.MaxInlineDepth || p.PC() >= vars.MAX_ILBUF || (sp > 0 && vt.NumField() >= vars.MAX_FIELDS) {
p.Vp(ir.OP_recurse, vt, self.pv)
if self.opts.RecursiveDepth > 0 {
if self.pv {
self.rec[vt] = 1
} else {
self.rec[vt] = 0
}
}
} else {
self.compileStructBody(p, sp, vt)
}
}
func (self *Compiler) compileStructBody(p *ir.Program, sp int, vt reflect.Type) {
p.Tag(sp)
p.Int(ir.OP_byte, '{')
p.Add(ir.OP_save)
p.Add(ir.OP_cond_set)
/* compile each field */
for _, fv := range resolver.ResolveStruct(vt) {
var s []int
var o resolver.Offset
/* "omitempty" for arrays */
if fv.Type.Kind() == reflect.Array {
if fv.Type.Len() == 0 && (fv.Opts&resolver.F_omitempty) != 0 {
continue
}
}
/* index to the field */
for _, o = range fv.Path {
if p.Int(ir.OP_index, int(o.Size)); o.Kind == resolver.F_deref {
s = append(s, p.PC())
p.Add(ir.OP_is_nil)
p.Add(ir.OP_deref)
}
}
/* check for "omitempty" option */
if fv.Type.Kind() != reflect.Struct && fv.Type.Kind() != reflect.Array && (fv.Opts&resolver.F_omitempty) != 0 {
s = append(s, p.PC())
self.compileStructFieldZero(p, fv.Type)
}
/* add the comma if not the first element */
i := p.PC()
p.Add(ir.OP_cond_testc)
p.Int(ir.OP_byte, ',')
p.Pin(i)
/* compile the key and value */
ft := fv.Type
p.Str(ir.OP_text, Quote(fv.Name)+":")
/* check for "stringnize" option */
if (fv.Opts & resolver.F_stringize) == 0 {
self.compileOne(p, sp+1, ft, self.pv)
} else {
self.compileStructFieldStr(p, sp+1, ft)
}
/* patch the skipping jumps and reload the struct pointer */
p.Rel(s)
p.Add(ir.OP_load)
}
/* end of object */
p.Add(ir.OP_drop)
p.Int(ir.OP_byte, '}')
}
func (self *Compiler) compileStructFieldStr(p *ir.Program, sp int, vt reflect.Type) {
// NOTICE: according to encoding/json, Marshaler type has higher priority than string option
// see issue:
if self.tryCompileMarshaler(p, vt, self.pv) {
return
}
pc := -1
ft := vt
sv := false
/* dereference the pointer if needed */
if ft.Kind() == reflect.Ptr {
ft = ft.Elem()
}
/* check if it can be stringized */
switch ft.Kind() {
case reflect.Bool:
sv = true
case reflect.Int:
sv = true
case reflect.Int8:
sv = true
case reflect.Int16:
sv = true
case reflect.Int32:
sv = true
case reflect.Int64:
sv = true
case reflect.Uint:
sv = true
case reflect.Uint8:
sv = true
case reflect.Uint16:
sv = true
case reflect.Uint32:
sv = true
case reflect.Uint64:
sv = true
case reflect.Uintptr:
sv = true
case reflect.Float32:
sv = true
case reflect.Float64:
sv = true
case reflect.String:
sv = true
}
/* if it's not, ignore the "string" and follow the regular path */
if !sv {
self.compileOne(p, sp, vt, self.pv)
return
}
/* dereference the pointer */
if vt.Kind() == reflect.Ptr {
pc = p.PC()
vt = vt.Elem()
p.Add(ir.OP_is_nil)
p.Add(ir.OP_deref)
}
/* special case of a double-quoted string */
if ft != vars.JsonNumberType && ft.Kind() == reflect.String {
p.Add(ir.OP_quote)
} else {
self.compileStructFieldQuoted(p, sp, vt)
}
/* the "null" case of the pointer */
if pc != -1 {
e := p.PC()
p.Add(ir.OP_goto)
p.Pin(pc)
p.Add(ir.OP_null)
p.Pin(e)
}
}
func (self *Compiler) compileStructFieldZero(p *ir.Program, vt reflect.Type) {
switch vt.Kind() {
case reflect.Bool:
p.Add(ir.OP_is_zero_1)
case reflect.Int:
p.Add(ir.OP_is_zero_ints())
case reflect.Int8:
p.Add(ir.OP_is_zero_1)
case reflect.Int16:
p.Add(ir.OP_is_zero_2)
case reflect.Int32:
p.Add(ir.OP_is_zero_4)
case reflect.Int64:
p.Add(ir.OP_is_zero_8)
case reflect.Uint:
p.Add(ir.OP_is_zero_ints())
case reflect.Uint8:
p.Add(ir.OP_is_zero_1)
case reflect.Uint16:
p.Add(ir.OP_is_zero_2)
case reflect.Uint32:
p.Add(ir.OP_is_zero_4)
case reflect.Uint64:
p.Add(ir.OP_is_zero_8)
case reflect.Uintptr:
p.Add(ir.OP_is_nil)
case reflect.Float32:
p.Add(ir.OP_is_zero_4)
case reflect.Float64:
p.Add(ir.OP_is_zero_8)
case reflect.String:
p.Add(ir.OP_is_nil_p1)
case reflect.Interface:
p.Add(ir.OP_is_nil)
case reflect.Map:
p.Add(ir.OP_is_zero_map)
case reflect.Ptr:
p.Add(ir.OP_is_nil)
case reflect.Slice:
p.Add(ir.OP_is_nil_p1)
default:
panic(vars.Error_type(vt))
}
}
func (self *Compiler) compileStructFieldQuoted(p *ir.Program, sp int, vt reflect.Type) {
p.Int(ir.OP_byte, '"')
self.compileOne(p, sp, vt, self.pv)
p.Int(ir.OP_byte, '"')
}
func (self *Compiler) compileInterface(p *ir.Program, vt reflect.Type) {
x := p.PC()
p.Add(ir.OP_is_nil_p1)
/* iface and efaces are different */
if vt.NumMethod() == 0 {
p.Add(ir.OP_eface)
} else {
p.Add(ir.OP_iface)
}
/* the "null" value */
e := p.PC()
p.Add(ir.OP_goto)
p.Pin(x)
p.Add(ir.OP_null)
p.Pin(e)
}
func (self *Compiler) compileMarshaler(p *ir.Program, op ir.Op, vt reflect.Type, mt reflect.Type) {
pc := p.PC()
vk := vt.Kind()
/* direct receiver */
if vk != reflect.Ptr {
addMarshalerOp(p, op, vt, mt)
return
}
/* value receiver with a pointer type, check for nil before calling the marshaler */
p.Add(ir.OP_is_nil)
addMarshalerOp(p, op, vt, mt)
i := p.PC()
p.Add(ir.OP_goto)
p.Pin(pc)
p.Add(ir.OP_null)
p.Pin(i)
}
func addMarshalerOp(p *ir.Program, op ir.Op, vt reflect.Type, mt reflect.Type) {
if vars.UseVM {
itab := rt.GetItab(rt.IfaceType(rt.UnpackType(mt)), rt.UnpackType(vt), true)
p.Vtab(op, vt, itab)
} else {
// OPT: get itab here
p.Rtt(op, vt)
}
}