gotosocial/vendor/github.com/bytedance/sonic/internal/decoder/optdec/compiler.go

450 lines
9 KiB
Go
Raw Normal View History

package optdec
import (
"encoding/json"
"fmt"
"reflect"
"github.com/bytedance/sonic/option"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/internal/caching"
)
var (
programCache = caching.CreateProgramCache()
)
func findOrCompile(vt *rt.GoType) (decFunc, error) {
makeDecoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
ret, err := newCompiler().compileType(vt.Pack())
return ret, err
}
if val := programCache.Get(vt); val != nil {
return val.(decFunc), nil
} else if ret, err := programCache.Compute(vt, makeDecoder); err == nil {
return ret.(decFunc), nil
} else {
return nil, err
}
}
type compiler struct {
visited map[reflect.Type]bool
depth int
counts int
opts option.CompileOptions
namedPtr bool
}
func newCompiler() *compiler {
return &compiler{
visited: make(map[reflect.Type]bool),
opts: option.DefaultCompileOptions(),
}
}
func (self *compiler) apply(opts option.CompileOptions) *compiler {
self.opts = opts
return self
}
const _CompileMaxDepth = 4096
func (c *compiler) enter(vt reflect.Type) {
c.visited[vt] = true
c.depth += 1
if c.depth > _CompileMaxDepth {
panic(*stackOverflow)
}
}
func (c *compiler) exit(vt reflect.Type) {
c.visited[vt] = false
c.depth -= 1
}
func (c *compiler) compileInt(vt reflect.Type) decFunc {
switch vt.Size() {
case 4:
switch vt.Kind() {
case reflect.Uint:
fallthrough
case reflect.Uintptr:
return &u32Decoder{}
case reflect.Int:
return &i32Decoder{}
}
case 8:
switch vt.Kind() {
case reflect.Uint:
fallthrough
case reflect.Uintptr:
return &u64Decoder{}
case reflect.Int:
return &i64Decoder{}
}
default:
panic("not supported pointer size: " + fmt.Sprint(vt.Size()))
}
panic("unreachable")
}
func (c *compiler) rescue(ep *error) {
if val := recover(); val != nil {
if err, ok := val.(error); ok {
*ep = err
} else {
panic(val)
}
}
}
func (c *compiler) compileType(vt reflect.Type) (rt decFunc, err error) {
defer c.rescue(&err)
rt = c.compile(vt)
return rt, err
}
func (c *compiler) compile(vt reflect.Type) decFunc {
if c.visited[vt] {
return &recuriveDecoder{
typ: rt.UnpackType(vt),
}
}
dec := c.tryCompilePtrUnmarshaler(vt, false)
if dec != nil {
return dec
}
return c.compileBasic(vt)
}
func (c *compiler) compileBasic(vt reflect.Type) decFunc {
defer func() {
c.counts += 1
}()
switch vt.Kind() {
case reflect.Bool:
return &boolDecoder{}
case reflect.Int8:
return &i8Decoder{}
case reflect.Int16:
return &i16Decoder{}
case reflect.Int32:
return &i32Decoder{}
case reflect.Int64:
return &i64Decoder{}
case reflect.Uint8:
return &u8Decoder{}
case reflect.Uint16:
return &u16Decoder{}
case reflect.Uint32:
return &u32Decoder{}
case reflect.Uint64:
return &u64Decoder{}
case reflect.Float32:
return &f32Decoder{}
case reflect.Float64:
return &f64Decoder{}
case reflect.Uint:
fallthrough
case reflect.Uintptr:
fallthrough
case reflect.Int:
return c.compileInt(vt)
case reflect.String:
return c.compileString(vt)
case reflect.Array:
return c.compileArray(vt)
case reflect.Interface:
return c.compileInterface(vt)
case reflect.Map:
return c.compileMap(vt)
case reflect.Ptr:
return c.compilePtr(vt)
case reflect.Slice:
return c.compileSlice(vt)
case reflect.Struct:
return c.compileStruct(vt)
default:
panic(&json.UnmarshalTypeError{Type: vt})
}
}
func (c *compiler) compilePtr(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
// specail logic for Named Ptr, issue 379
if reflect.PtrTo(vt.Elem()) != vt {
c.namedPtr = true
return &ptrDecoder{
typ: rt.UnpackType(vt.Elem()),
deref: c.compileBasic(vt.Elem()),
}
}
return &ptrDecoder{
typ: rt.UnpackType(vt.Elem()),
deref: c.compile(vt.Elem()),
}
}
func (c *compiler) compileArray(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
return &arrayDecoder{
len: vt.Len(),
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
func (c *compiler) compileString(vt reflect.Type) decFunc {
if vt == jsonNumberType {
return &numberDecoder{}
}
return &stringDecoder{}
}
func (c *compiler) tryCompileSliceUnmarshaler(vt reflect.Type) decFunc {
pt := reflect.PtrTo(vt.Elem())
if pt.Implements(jsonUnmarshalerType) {
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
if pt.Implements(encodingTextUnmarshalerType) {
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
return nil
}
func (c *compiler) compileSlice(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
// Some common slice, use a decoder, to avoid function calls
et := rt.UnpackType(vt.Elem())
/* first checking `[]byte` */
if et.Kind() == reflect.Uint8 /* []byte */ {
return c.compileSliceBytes(vt)
}
dec := c.tryCompileSliceUnmarshaler(vt)
if dec != nil {
return dec
}
if vt == reflect.TypeOf([]interface{}{}) {
return &sliceEfaceDecoder{}
}
if et.IsInt32() {
return &sliceI32Decoder{}
}
if et.IsInt64() {
return &sliceI64Decoder{}
}
if et.IsUint32() {
return &sliceU32Decoder{}
}
if et.IsUint64() {
return &sliceU64Decoder{}
}
if et.Kind() == reflect.String {
return &sliceStringDecoder{}
}
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
func (c *compiler) compileSliceBytes(vt reflect.Type) decFunc {
ep := reflect.PtrTo(vt.Elem())
if ep.Implements(jsonUnmarshalerType) {
return &sliceBytesUnmarshalerDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
if ep.Implements(encodingTextUnmarshalerType) {
return &sliceBytesUnmarshalerDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
return &sliceBytesDecoder{}
}
func (c *compiler) compileInterface(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
if vt.NumMethod() == 0 {
return &efaceDecoder{}
}
if vt.Implements(jsonUnmarshalerType) {
return &unmarshalJSONDecoder{
typ: rt.UnpackType(vt),
}
}
if vt.Implements(encodingTextUnmarshalerType) {
return &unmarshalTextDecoder{
typ: rt.UnpackType(vt),
}
}
return &ifaceDecoder{
typ: rt.UnpackType(vt),
}
}
func (c *compiler) compileMap(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
// check the key unmarshaler at first
decKey := tryCompileKeyUnmarshaler(vt)
if decKey != nil {
return &mapDecoder{
mapType: rt.MapType(rt.UnpackType(vt)),
keyDec: decKey,
elemDec: c.compile(vt.Elem()),
}
}
// Most common map, use a decoder, to avoid function calls
if vt == reflect.TypeOf(map[string]interface{}{}) {
return &mapEfaceDecoder{}
} else if vt == reflect.TypeOf(map[string]string{}) {
return &mapStringDecoder{}
}
// Some common integer map later
mt := rt.MapType(rt.UnpackType(vt))
if mt.Key.Kind() == reflect.String {
return &mapStrKeyDecoder{
mapType: mt,
assign: rt.GetMapStrAssign(vt),
elemDec: c.compile(vt.Elem()),
}
}
if mt.Key.IsInt64() {
return &mapI64KeyDecoder{
mapType: mt,
elemDec: c.compile(vt.Elem()),
assign: rt.GetMap64Assign(vt),
}
}
if mt.Key.IsInt32() {
return &mapI32KeyDecoder{
mapType: mt,
elemDec: c.compile(vt.Elem()),
assign: rt.GetMap32Assign(vt),
}
}
if mt.Key.IsUint64() {
return &mapU64KeyDecoder{
mapType: mt,
elemDec: c.compile(vt.Elem()),
assign: rt.GetMap64Assign(vt),
}
}
if mt.Key.IsUint32() {
return &mapU32KeyDecoder{
mapType: mt,
elemDec: c.compile(vt.Elem()),
assign: rt.GetMap32Assign(vt),
}
}
// Generic map
return &mapDecoder{
mapType: mt,
keyDec: c.compileMapKey(vt),
elemDec: c.compile(vt.Elem()),
}
}
func tryCompileKeyUnmarshaler(vt reflect.Type) decKey {
pt := reflect.PtrTo(vt.Key())
/* check for `encoding.TextUnmarshaler` with pointer receiver */
if pt.Implements(encodingTextUnmarshalerType) {
return decodeKeyTextUnmarshaler
}
/* not support map key with `json.Unmarshaler` */
return nil
}
func (c *compiler) compileMapKey(vt reflect.Type) decKey {
switch vt.Key().Kind() {
case reflect.Int8:
return decodeKeyI8
case reflect.Int16:
return decodeKeyI16
case reflect.Uint8:
return decodeKeyU8
case reflect.Uint16:
return decodeKeyU16
default:
panic(&json.UnmarshalTypeError{Type: vt})
}
}
// maybe vt is a named type, and not a pointer receiver, see issue 379
func (c *compiler) tryCompilePtrUnmarshaler(vt reflect.Type, strOpt bool) decFunc {
pt := reflect.PtrTo(vt)
/* check for `json.Unmarshaler` with pointer receiver */
if pt.Implements(jsonUnmarshalerType) {
return &unmarshalJSONDecoder{
typ: rt.UnpackType(pt),
strOpt: strOpt,
}
}
/* check for `encoding.TextMarshaler` with pointer receiver */
if pt.Implements(encodingTextUnmarshalerType) {
/* TextUnmarshal not support ,strig tag */
if strOpt {
panicForInvalidStrType(vt)
}
return &unmarshalTextDecoder{
typ: rt.UnpackType(pt),
}
}
return nil
}
func panicForInvalidStrType(vt reflect.Type) {
panic(error_type(rt.UnpackType(vt)))
}