mirror of https://github.com/goccy/go-json.git
Move compiler for encoder to internal package
This commit is contained in:
parent
c45f1e8b2c
commit
62b7d3ba0a
File diff suppressed because it is too large
Load Diff
|
@ -1 +0,0 @@
|
||||||
package compiler
|
|
|
@ -1,11 +0,0 @@
|
||||||
// +build !race
|
|
||||||
|
|
||||||
package compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/goccy/go-json/internal/encoder"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CompileToGetCodeSet(typeptr uintptr) (*encoder.OpcodeSet, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
// +build !race
|
||||||
|
|
||||||
|
package encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/goccy/go-json/internal/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
|
||||||
|
if typeptr > typeAddr.MaxTypeAddr {
|
||||||
|
return compileToGetCodeSetSlowPath(typeptr)
|
||||||
|
}
|
||||||
|
index := typeptr - typeAddr.BaseTypeAddr
|
||||||
|
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// noescape trick for header.typ ( reflect.*rtype )
|
||||||
|
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
|
||||||
|
|
||||||
|
code, err := compileHead(&compileContext{
|
||||||
|
typ: copiedType,
|
||||||
|
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
code = copyOpcode(code)
|
||||||
|
codeLength := code.TotalLength()
|
||||||
|
codeSet := &OpcodeSet{
|
||||||
|
Code: code,
|
||||||
|
CodeLength: codeLength,
|
||||||
|
}
|
||||||
|
cachedOpcodeSets[index] = codeSet
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// +build race
|
||||||
|
|
||||||
|
package encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/goccy/go-json/internal/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var setsMu sync.RWMutex
|
||||||
|
|
||||||
|
func CompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
||||||
|
if typeptr > maxTypeAddr {
|
||||||
|
return compileToGetCodeSetSlowPath(typeptr)
|
||||||
|
}
|
||||||
|
index := typeptr - baseTypeAddr
|
||||||
|
setsMu.RLock()
|
||||||
|
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
|
||||||
|
setsMu.RUnlock()
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
||||||
|
setsMu.RUnlock()
|
||||||
|
|
||||||
|
// noescape trick for header.typ ( reflect.*rtype )
|
||||||
|
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
|
||||||
|
|
||||||
|
code, err := compileHead(&compileContext{
|
||||||
|
typ: copiedType,
|
||||||
|
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
code = copyOpcode(code)
|
||||||
|
codeLength := code.TotalLength()
|
||||||
|
codeSet := &opcodeSet{
|
||||||
|
Code: code,
|
||||||
|
CodeLength: codeLength,
|
||||||
|
}
|
||||||
|
setsMu.Lock()
|
||||||
|
cachedOpcodeSets[index] = codeSet
|
||||||
|
setsMu.Unlock()
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/goccy/go-json/internal/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type compileContext struct {
|
||||||
|
typ *runtime.Type
|
||||||
|
opcodeIndex int
|
||||||
|
ptrIndex int
|
||||||
|
indent int
|
||||||
|
structTypeToCompiledCode map[uintptr]*CompiledCode
|
||||||
|
|
||||||
|
parent *compileContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) context() *compileContext {
|
||||||
|
return &compileContext{
|
||||||
|
typ: c.typ,
|
||||||
|
opcodeIndex: c.opcodeIndex,
|
||||||
|
ptrIndex: c.ptrIndex,
|
||||||
|
indent: c.indent,
|
||||||
|
structTypeToCompiledCode: c.structTypeToCompiledCode,
|
||||||
|
parent: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) withType(typ *runtime.Type) *compileContext {
|
||||||
|
ctx := c.context()
|
||||||
|
ctx.typ = typ
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) incIndent() *compileContext {
|
||||||
|
ctx := c.context()
|
||||||
|
ctx.indent++
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) decIndent() *compileContext {
|
||||||
|
ctx := c.context()
|
||||||
|
ctx.indent--
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) incIndex() {
|
||||||
|
c.incOpcodeIndex()
|
||||||
|
c.incPtrIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) decIndex() {
|
||||||
|
c.decOpcodeIndex()
|
||||||
|
c.decPtrIndex()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) incOpcodeIndex() {
|
||||||
|
c.opcodeIndex++
|
||||||
|
if c.parent != nil {
|
||||||
|
c.parent.incOpcodeIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) decOpcodeIndex() {
|
||||||
|
c.opcodeIndex--
|
||||||
|
if c.parent != nil {
|
||||||
|
c.parent.decOpcodeIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) incPtrIndex() {
|
||||||
|
c.ptrIndex++
|
||||||
|
if c.parent != nil {
|
||||||
|
c.parent.incPtrIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compileContext) decPtrIndex() {
|
||||||
|
c.ptrIndex--
|
||||||
|
if c.parent != nil {
|
||||||
|
c.parent.decPtrIndex()
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,41 +24,108 @@ const (
|
||||||
UnorderedMapOption
|
UnorderedMapOption
|
||||||
)
|
)
|
||||||
|
|
||||||
type Opcode struct {
|
func (t OpType) IsMultipleOpHead() bool {
|
||||||
Op OpType // operation type
|
switch t {
|
||||||
Type *runtime.Type // go type
|
case OpStructHead:
|
||||||
DisplayIdx int // opcode index
|
return true
|
||||||
Key []byte // struct field key
|
case OpStructHeadSlice:
|
||||||
EscapedKey []byte // struct field key ( HTML escaped )
|
return true
|
||||||
PtrNum int // pointer number: e.g. double pointer is 2.
|
case OpStructHeadArray:
|
||||||
DisplayKey string // key text to display
|
return true
|
||||||
IsTaggedKey bool // whether tagged key
|
case OpStructHeadMap:
|
||||||
AnonymousKey bool // whether anonymous key
|
return true
|
||||||
AnonymousHead bool // whether anonymous head or not
|
case OpStructHeadStruct:
|
||||||
Indirect bool // whether indirect or not
|
return true
|
||||||
Nilcheck bool // whether needs to nilcheck or not
|
case OpStructHeadOmitEmpty:
|
||||||
AddrForMarshaler bool // whether needs to addr for marshaler or not
|
return true
|
||||||
RshiftNum uint8 // use to take bit for judging whether negative integer or not
|
case OpStructHeadOmitEmptySlice:
|
||||||
Mask uint64 // mask for number
|
return true
|
||||||
Indent int // indent number
|
case OpStructHeadStringTagSlice:
|
||||||
|
return true
|
||||||
|
case OpStructHeadOmitEmptyArray:
|
||||||
|
return true
|
||||||
|
case OpStructHeadStringTagArray:
|
||||||
|
return true
|
||||||
|
case OpStructHeadOmitEmptyMap:
|
||||||
|
return true
|
||||||
|
case OpStructHeadStringTagMap:
|
||||||
|
return true
|
||||||
|
case OpStructHeadOmitEmptyStruct:
|
||||||
|
return true
|
||||||
|
case OpStructHeadStringTag:
|
||||||
|
return true
|
||||||
|
case OpStructHeadSlicePtr:
|
||||||
|
return true
|
||||||
|
case OpStructHeadOmitEmptySlicePtr:
|
||||||
|
return true
|
||||||
|
case OpStructHeadStringTagSlicePtr:
|
||||||
|
return true
|
||||||
|
case OpStructHeadArrayPtr:
|
||||||
|
return true
|
||||||
|
case OpStructHeadOmitEmptyArrayPtr:
|
||||||
|
return true
|
||||||
|
case OpStructHeadStringTagArrayPtr:
|
||||||
|
return true
|
||||||
|
case OpStructHeadMapPtr:
|
||||||
|
return true
|
||||||
|
case OpStructHeadOmitEmptyMapPtr:
|
||||||
|
return true
|
||||||
|
case OpStructHeadStringTagMapPtr:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
Idx uintptr // offset to access ptr
|
func (t OpType) IsMultipleOpField() bool {
|
||||||
HeadIdx uintptr // offset to access slice/struct head
|
switch t {
|
||||||
ElemIdx uintptr // offset to access array/slice/map elem
|
case OpStructField:
|
||||||
Length uintptr // offset to access slice/map length or array length
|
return true
|
||||||
MapIter uintptr // offset to access map iterator
|
case OpStructFieldSlice:
|
||||||
MapPos uintptr // offset to access position list for sorted map
|
return true
|
||||||
Offset uintptr // offset size from struct header
|
case OpStructFieldArray:
|
||||||
Size uintptr // array/slice elem size
|
return true
|
||||||
|
case OpStructFieldMap:
|
||||||
MapKey *Opcode // map key
|
return true
|
||||||
MapValue *Opcode // map value
|
case OpStructFieldStruct:
|
||||||
Elem *Opcode // array/slice elem
|
return true
|
||||||
End *Opcode // array/slice/struct/map end
|
case OpStructFieldOmitEmpty:
|
||||||
PrevField *Opcode // prev struct field
|
return true
|
||||||
NextField *Opcode // next struct field
|
case OpStructFieldOmitEmptySlice:
|
||||||
Next *Opcode // next opcode
|
return true
|
||||||
Jmp *CompiledCode // for recursive call
|
case OpStructFieldStringTagSlice:
|
||||||
|
return true
|
||||||
|
case OpStructFieldOmitEmptyArray:
|
||||||
|
return true
|
||||||
|
case OpStructFieldStringTagArray:
|
||||||
|
return true
|
||||||
|
case OpStructFieldOmitEmptyMap:
|
||||||
|
return true
|
||||||
|
case OpStructFieldStringTagMap:
|
||||||
|
return true
|
||||||
|
case OpStructFieldOmitEmptyStruct:
|
||||||
|
return true
|
||||||
|
case OpStructFieldStringTag:
|
||||||
|
return true
|
||||||
|
case OpStructFieldSlicePtr:
|
||||||
|
return true
|
||||||
|
case OpStructFieldOmitEmptySlicePtr:
|
||||||
|
return true
|
||||||
|
case OpStructFieldStringTagSlicePtr:
|
||||||
|
return true
|
||||||
|
case OpStructFieldArrayPtr:
|
||||||
|
return true
|
||||||
|
case OpStructFieldOmitEmptyArrayPtr:
|
||||||
|
return true
|
||||||
|
case OpStructFieldStringTagArrayPtr:
|
||||||
|
return true
|
||||||
|
case OpStructFieldMapPtr:
|
||||||
|
return true
|
||||||
|
case OpStructFieldOmitEmptyMapPtr:
|
||||||
|
return true
|
||||||
|
case OpStructFieldStringTagMapPtr:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpcodeSet struct {
|
type OpcodeSet struct {
|
||||||
|
|
|
@ -0,0 +1,647 @@
|
||||||
|
package encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/goccy/go-json/internal/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||||
|
|
||||||
|
type Opcode struct {
|
||||||
|
Op OpType // operation type
|
||||||
|
Type *runtime.Type // go type
|
||||||
|
DisplayIdx int // opcode index
|
||||||
|
Key []byte // struct field key
|
||||||
|
EscapedKey []byte // struct field key ( HTML escaped )
|
||||||
|
PtrNum int // pointer number: e.g. double pointer is 2.
|
||||||
|
DisplayKey string // key text to display
|
||||||
|
IsTaggedKey bool // whether tagged key
|
||||||
|
AnonymousKey bool // whether anonymous key
|
||||||
|
AnonymousHead bool // whether anonymous head or not
|
||||||
|
Indirect bool // whether indirect or not
|
||||||
|
Nilcheck bool // whether needs to nilcheck or not
|
||||||
|
AddrForMarshaler bool // whether needs to addr for marshaler or not
|
||||||
|
RshiftNum uint8 // use to take bit for judging whether negative integer or not
|
||||||
|
Mask uint64 // mask for number
|
||||||
|
Indent int // indent number
|
||||||
|
|
||||||
|
Idx uintptr // offset to access ptr
|
||||||
|
HeadIdx uintptr // offset to access slice/struct head
|
||||||
|
ElemIdx uintptr // offset to access array/slice/map elem
|
||||||
|
Length uintptr // offset to access slice/map length or array length
|
||||||
|
MapIter uintptr // offset to access map iterator
|
||||||
|
MapPos uintptr // offset to access position list for sorted map
|
||||||
|
Offset uintptr // offset size from struct header
|
||||||
|
Size uintptr // array/slice elem size
|
||||||
|
|
||||||
|
MapKey *Opcode // map key
|
||||||
|
MapValue *Opcode // map value
|
||||||
|
Elem *Opcode // array/slice elem
|
||||||
|
End *Opcode // array/slice/struct/map end
|
||||||
|
PrevField *Opcode // prev struct field
|
||||||
|
NextField *Opcode // next struct field
|
||||||
|
Next *Opcode // next opcode
|
||||||
|
Jmp *CompiledCode // for recursive call
|
||||||
|
}
|
||||||
|
|
||||||
|
func rshitNum(bitSize uint8) uint8 {
|
||||||
|
return bitSize - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) setMaskAndRshiftNum(bitSize uint8) {
|
||||||
|
switch bitSize {
|
||||||
|
case 8:
|
||||||
|
c.Mask = math.MaxUint8
|
||||||
|
case 16:
|
||||||
|
c.Mask = math.MaxUint16
|
||||||
|
case 32:
|
||||||
|
c.Mask = math.MaxUint32
|
||||||
|
case 64:
|
||||||
|
c.Mask = math.MaxUint64
|
||||||
|
}
|
||||||
|
c.RshiftNum = rshitNum(bitSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) ToHeaderType() OpType {
|
||||||
|
switch c.Op {
|
||||||
|
case OpInt:
|
||||||
|
return OpStructHeadInt
|
||||||
|
case OpIntPtr:
|
||||||
|
return OpStructHeadIntPtr
|
||||||
|
case OpUint:
|
||||||
|
return OpStructHeadUint
|
||||||
|
case OpUintPtr:
|
||||||
|
return OpStructHeadUintPtr
|
||||||
|
case OpFloat32:
|
||||||
|
return OpStructHeadFloat32
|
||||||
|
case OpFloat32Ptr:
|
||||||
|
return OpStructHeadFloat32Ptr
|
||||||
|
case OpFloat64:
|
||||||
|
return OpStructHeadFloat64
|
||||||
|
case OpFloat64Ptr:
|
||||||
|
return OpStructHeadFloat64Ptr
|
||||||
|
case OpString:
|
||||||
|
return OpStructHeadString
|
||||||
|
case OpStringPtr:
|
||||||
|
return OpStructHeadStringPtr
|
||||||
|
case OpNumber:
|
||||||
|
return OpStructHeadNumber
|
||||||
|
case OpNumberPtr:
|
||||||
|
return OpStructHeadNumberPtr
|
||||||
|
case OpBool:
|
||||||
|
return OpStructHeadBool
|
||||||
|
case OpBoolPtr:
|
||||||
|
return OpStructHeadBoolPtr
|
||||||
|
case OpMap:
|
||||||
|
return OpStructHeadMap
|
||||||
|
case OpMapPtr:
|
||||||
|
c.Op = OpMap
|
||||||
|
return OpStructHeadMapPtr
|
||||||
|
case OpArray:
|
||||||
|
return OpStructHeadArray
|
||||||
|
case OpArrayPtr:
|
||||||
|
c.Op = OpArray
|
||||||
|
return OpStructHeadArrayPtr
|
||||||
|
case OpSlice:
|
||||||
|
return OpStructHeadSlice
|
||||||
|
case OpSlicePtr:
|
||||||
|
c.Op = OpSlice
|
||||||
|
return OpStructHeadSlicePtr
|
||||||
|
case OpMarshalJSON:
|
||||||
|
return OpStructHeadMarshalJSON
|
||||||
|
case OpMarshalJSONPtr:
|
||||||
|
return OpStructHeadMarshalJSONPtr
|
||||||
|
case OpMarshalText:
|
||||||
|
return OpStructHeadMarshalText
|
||||||
|
case OpMarshalTextPtr:
|
||||||
|
return OpStructHeadMarshalTextPtr
|
||||||
|
}
|
||||||
|
return OpStructHead
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) ToFieldType() OpType {
|
||||||
|
switch c.Op {
|
||||||
|
case OpInt:
|
||||||
|
return OpStructFieldInt
|
||||||
|
case OpIntPtr:
|
||||||
|
return OpStructFieldIntPtr
|
||||||
|
case OpUint:
|
||||||
|
return OpStructFieldUint
|
||||||
|
case OpUintPtr:
|
||||||
|
return OpStructFieldUintPtr
|
||||||
|
case OpFloat32:
|
||||||
|
return OpStructFieldFloat32
|
||||||
|
case OpFloat32Ptr:
|
||||||
|
return OpStructFieldFloat32Ptr
|
||||||
|
case OpFloat64:
|
||||||
|
return OpStructFieldFloat64
|
||||||
|
case OpFloat64Ptr:
|
||||||
|
return OpStructFieldFloat64Ptr
|
||||||
|
case OpString:
|
||||||
|
return OpStructFieldString
|
||||||
|
case OpStringPtr:
|
||||||
|
return OpStructFieldStringPtr
|
||||||
|
case OpNumber:
|
||||||
|
return OpStructFieldNumber
|
||||||
|
case OpNumberPtr:
|
||||||
|
return OpStructFieldNumberPtr
|
||||||
|
case OpBool:
|
||||||
|
return OpStructFieldBool
|
||||||
|
case OpBoolPtr:
|
||||||
|
return OpStructFieldBoolPtr
|
||||||
|
case OpMap:
|
||||||
|
return OpStructFieldMap
|
||||||
|
case OpMapPtr:
|
||||||
|
c.Op = OpMap
|
||||||
|
return OpStructFieldMapPtr
|
||||||
|
case OpArray:
|
||||||
|
return OpStructFieldArray
|
||||||
|
case OpArrayPtr:
|
||||||
|
c.Op = OpArray
|
||||||
|
return OpStructFieldArrayPtr
|
||||||
|
case OpSlice:
|
||||||
|
return OpStructFieldSlice
|
||||||
|
case OpSlicePtr:
|
||||||
|
c.Op = OpSlice
|
||||||
|
return OpStructFieldSlicePtr
|
||||||
|
case OpMarshalJSON:
|
||||||
|
return OpStructFieldMarshalJSON
|
||||||
|
case OpMarshalJSONPtr:
|
||||||
|
return OpStructFieldMarshalJSONPtr
|
||||||
|
case OpMarshalText:
|
||||||
|
return OpStructFieldMarshalText
|
||||||
|
case OpMarshalTextPtr:
|
||||||
|
return OpStructFieldMarshalTextPtr
|
||||||
|
}
|
||||||
|
return OpStructField
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOpCode(ctx *compileContext, op OpType) *Opcode {
|
||||||
|
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func opcodeOffset(idx int) uintptr {
|
||||||
|
return uintptr(idx) * uintptrSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyOpcode(code *Opcode) *Opcode {
|
||||||
|
codeMap := map[uintptr]*Opcode{}
|
||||||
|
return code.copy(codeMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOpCodeWithNext(ctx *compileContext, op OpType, next *Opcode) *Opcode {
|
||||||
|
return &Opcode{
|
||||||
|
Op: op,
|
||||||
|
Type: ctx.typ,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Indent: ctx.indent,
|
||||||
|
Idx: opcodeOffset(ctx.ptrIndex),
|
||||||
|
Next: next,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEndOp(ctx *compileContext) *Opcode {
|
||||||
|
return newOpCodeWithNext(ctx, OpEnd, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) copy(codeMap map[uintptr]*Opcode) *Opcode {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
addr := uintptr(unsafe.Pointer(c))
|
||||||
|
if code, exists := codeMap[addr]; exists {
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
copied := &Opcode{
|
||||||
|
Op: c.Op,
|
||||||
|
Type: c.Type,
|
||||||
|
DisplayIdx: c.DisplayIdx,
|
||||||
|
Key: c.Key,
|
||||||
|
EscapedKey: c.EscapedKey,
|
||||||
|
DisplayKey: c.DisplayKey,
|
||||||
|
PtrNum: c.PtrNum,
|
||||||
|
Mask: c.Mask,
|
||||||
|
RshiftNum: c.RshiftNum,
|
||||||
|
IsTaggedKey: c.IsTaggedKey,
|
||||||
|
AnonymousKey: c.AnonymousKey,
|
||||||
|
AnonymousHead: c.AnonymousHead,
|
||||||
|
Indirect: c.Indirect,
|
||||||
|
Nilcheck: c.Nilcheck,
|
||||||
|
AddrForMarshaler: c.AddrForMarshaler,
|
||||||
|
Indent: c.Indent,
|
||||||
|
Idx: c.Idx,
|
||||||
|
HeadIdx: c.HeadIdx,
|
||||||
|
ElemIdx: c.ElemIdx,
|
||||||
|
Length: c.Length,
|
||||||
|
MapIter: c.MapIter,
|
||||||
|
MapPos: c.MapPos,
|
||||||
|
Offset: c.Offset,
|
||||||
|
Size: c.Size,
|
||||||
|
}
|
||||||
|
codeMap[addr] = copied
|
||||||
|
copied.MapKey = c.MapKey.copy(codeMap)
|
||||||
|
copied.MapValue = c.MapValue.copy(codeMap)
|
||||||
|
copied.Elem = c.Elem.copy(codeMap)
|
||||||
|
copied.End = c.End.copy(codeMap)
|
||||||
|
copied.PrevField = c.PrevField.copy(codeMap)
|
||||||
|
copied.NextField = c.NextField.copy(codeMap)
|
||||||
|
copied.Next = c.Next.copy(codeMap)
|
||||||
|
copied.Jmp = c.Jmp
|
||||||
|
return copied
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) BeforeLastCode() *Opcode {
|
||||||
|
code := c
|
||||||
|
for {
|
||||||
|
var nextCode *Opcode
|
||||||
|
switch code.Op.CodeType() {
|
||||||
|
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||||
|
nextCode = code.End
|
||||||
|
default:
|
||||||
|
nextCode = code.Next
|
||||||
|
}
|
||||||
|
if nextCode.Op == OpEnd {
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
code = nextCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) TotalLength() int {
|
||||||
|
var idx int
|
||||||
|
for code := c; code.Op != OpEnd; {
|
||||||
|
idx = int(code.Idx / uintptrSize)
|
||||||
|
if code.Op == OpStructFieldRecursiveEnd {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch code.Op.CodeType() {
|
||||||
|
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||||
|
code = code.End
|
||||||
|
default:
|
||||||
|
code = code.Next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idx + 2 // opEnd + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) decOpcodeIndex() {
|
||||||
|
for code := c; code.Op != OpEnd; {
|
||||||
|
code.DisplayIdx--
|
||||||
|
code.Idx -= uintptrSize
|
||||||
|
if code.HeadIdx > 0 {
|
||||||
|
code.HeadIdx -= uintptrSize
|
||||||
|
}
|
||||||
|
if code.ElemIdx > 0 {
|
||||||
|
code.ElemIdx -= uintptrSize
|
||||||
|
}
|
||||||
|
if code.MapIter > 0 {
|
||||||
|
code.MapIter -= uintptrSize
|
||||||
|
}
|
||||||
|
if code.Length > 0 && code.Op.CodeType() != CodeArrayHead && code.Op.CodeType() != CodeArrayElem {
|
||||||
|
code.Length -= uintptrSize
|
||||||
|
}
|
||||||
|
switch code.Op.CodeType() {
|
||||||
|
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||||
|
code = code.End
|
||||||
|
default:
|
||||||
|
code = code.Next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) decIndent() {
|
||||||
|
for code := c; code.Op != OpEnd; {
|
||||||
|
code.Indent--
|
||||||
|
switch code.Op.CodeType() {
|
||||||
|
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||||
|
code = code.End
|
||||||
|
default:
|
||||||
|
code = code.Next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) dumpHead(code *Opcode) string {
|
||||||
|
var length uintptr
|
||||||
|
if code.Op.CodeType() == CodeArrayHead {
|
||||||
|
length = code.Length
|
||||||
|
} else {
|
||||||
|
length = code.Length / uintptrSize
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d])`,
|
||||||
|
code.DisplayIdx,
|
||||||
|
strings.Repeat("-", code.Indent),
|
||||||
|
code.Op,
|
||||||
|
code.Idx/uintptrSize,
|
||||||
|
code.HeadIdx/uintptrSize,
|
||||||
|
code.ElemIdx/uintptrSize,
|
||||||
|
length,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) dumpMapHead(code *Opcode) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||||
|
code.DisplayIdx,
|
||||||
|
strings.Repeat("-", code.Indent),
|
||||||
|
code.Op,
|
||||||
|
code.Idx/uintptrSize,
|
||||||
|
code.HeadIdx/uintptrSize,
|
||||||
|
code.ElemIdx/uintptrSize,
|
||||||
|
code.Length/uintptrSize,
|
||||||
|
code.MapIter/uintptrSize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) dumpMapEnd(code *Opcode) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
|
||||||
|
code.DisplayIdx,
|
||||||
|
strings.Repeat("-", code.Indent),
|
||||||
|
code.Op,
|
||||||
|
code.Idx/uintptrSize,
|
||||||
|
code.MapPos/uintptrSize,
|
||||||
|
code.Length/uintptrSize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) dumpElem(code *Opcode) string {
|
||||||
|
var length uintptr
|
||||||
|
if code.Op.CodeType() == CodeArrayElem {
|
||||||
|
length = code.Length
|
||||||
|
} else {
|
||||||
|
length = code.Length / uintptrSize
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][size:%d])`,
|
||||||
|
code.DisplayIdx,
|
||||||
|
strings.Repeat("-", code.Indent),
|
||||||
|
code.Op,
|
||||||
|
code.Idx/uintptrSize,
|
||||||
|
code.HeadIdx/uintptrSize,
|
||||||
|
code.ElemIdx/uintptrSize,
|
||||||
|
length,
|
||||||
|
code.Size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) dumpField(code *Opcode) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`[%d]%s%s ([idx:%d][key:%s][offset:%d][headIdx:%d])`,
|
||||||
|
code.DisplayIdx,
|
||||||
|
strings.Repeat("-", code.Indent),
|
||||||
|
code.Op,
|
||||||
|
code.Idx/uintptrSize,
|
||||||
|
code.DisplayKey,
|
||||||
|
code.Offset,
|
||||||
|
code.HeadIdx/uintptrSize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) dumpKey(code *Opcode) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||||
|
code.DisplayIdx,
|
||||||
|
strings.Repeat("-", code.Indent),
|
||||||
|
code.Op,
|
||||||
|
code.Idx/uintptrSize,
|
||||||
|
code.ElemIdx/uintptrSize,
|
||||||
|
code.Length/uintptrSize,
|
||||||
|
code.MapIter/uintptrSize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) dumpValue(code *Opcode) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`[%d]%s%s ([idx:%d][mapIter:%d])`,
|
||||||
|
code.DisplayIdx,
|
||||||
|
strings.Repeat("-", code.Indent),
|
||||||
|
code.Op,
|
||||||
|
code.Idx/uintptrSize,
|
||||||
|
code.MapIter/uintptrSize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Opcode) dump() string {
|
||||||
|
codes := []string{}
|
||||||
|
for code := c; code.Op != OpEnd; {
|
||||||
|
switch code.Op.CodeType() {
|
||||||
|
case CodeSliceHead:
|
||||||
|
codes = append(codes, c.dumpHead(code))
|
||||||
|
code = code.Next
|
||||||
|
case CodeMapHead:
|
||||||
|
codes = append(codes, c.dumpMapHead(code))
|
||||||
|
code = code.Next
|
||||||
|
case CodeArrayElem, CodeSliceElem:
|
||||||
|
codes = append(codes, c.dumpElem(code))
|
||||||
|
code = code.End
|
||||||
|
case CodeMapKey:
|
||||||
|
codes = append(codes, c.dumpKey(code))
|
||||||
|
code = code.End
|
||||||
|
case CodeMapValue:
|
||||||
|
codes = append(codes, c.dumpValue(code))
|
||||||
|
code = code.Next
|
||||||
|
case CodeMapEnd:
|
||||||
|
codes = append(codes, c.dumpMapEnd(code))
|
||||||
|
code = code.Next
|
||||||
|
case CodeStructField:
|
||||||
|
codes = append(codes, c.dumpField(code))
|
||||||
|
code = code.Next
|
||||||
|
case CodeStructEnd:
|
||||||
|
codes = append(codes, c.dumpField(code))
|
||||||
|
code = code.Next
|
||||||
|
default:
|
||||||
|
codes = append(codes, fmt.Sprintf(
|
||||||
|
"[%d]%s%s ([idx:%d])",
|
||||||
|
code.DisplayIdx,
|
||||||
|
strings.Repeat("-", code.Indent),
|
||||||
|
code.Op,
|
||||||
|
code.Idx/uintptrSize,
|
||||||
|
))
|
||||||
|
code = code.Next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(codes, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func prevField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
|
||||||
|
if _, exists := removedFields[code]; exists {
|
||||||
|
return prevField(code.PrevField, removedFields)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
|
||||||
|
if _, exists := removedFields[code]; exists {
|
||||||
|
return nextField(code.NextField, removedFields)
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkPrevToNextField(cur *Opcode, removedFields map[*Opcode]struct{}) {
|
||||||
|
prev := prevField(cur.PrevField, removedFields)
|
||||||
|
prev.NextField = nextField(cur.NextField, removedFields)
|
||||||
|
code := prev
|
||||||
|
fcode := cur
|
||||||
|
for {
|
||||||
|
var nextCode *Opcode
|
||||||
|
switch code.Op.CodeType() {
|
||||||
|
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||||
|
nextCode = code.End
|
||||||
|
default:
|
||||||
|
nextCode = code.Next
|
||||||
|
}
|
||||||
|
if nextCode == fcode {
|
||||||
|
code.Next = fcode.Next
|
||||||
|
break
|
||||||
|
} else if nextCode.Op == OpEnd {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
code = nextCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSliceHeaderCode(ctx *compileContext) *Opcode {
|
||||||
|
idx := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
length := opcodeOffset(ctx.ptrIndex)
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpSlice,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: idx,
|
||||||
|
HeadIdx: idx,
|
||||||
|
ElemIdx: elemIdx,
|
||||||
|
Length: length,
|
||||||
|
Indent: ctx.indent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSliceElemCode(ctx *compileContext, head *Opcode, size uintptr) *Opcode {
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpSliceElem,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: opcodeOffset(ctx.ptrIndex),
|
||||||
|
HeadIdx: head.Idx,
|
||||||
|
ElemIdx: head.ElemIdx,
|
||||||
|
Length: head.Length,
|
||||||
|
Indent: ctx.indent,
|
||||||
|
Size: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArrayHeaderCode(ctx *compileContext, alen int) *Opcode {
|
||||||
|
idx := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpArray,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: idx,
|
||||||
|
HeadIdx: idx,
|
||||||
|
ElemIdx: elemIdx,
|
||||||
|
Indent: ctx.indent,
|
||||||
|
Length: uintptr(alen),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArrayElemCode(ctx *compileContext, head *Opcode, length int, size uintptr) *Opcode {
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpArrayElem,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: opcodeOffset(ctx.ptrIndex),
|
||||||
|
ElemIdx: head.ElemIdx,
|
||||||
|
HeadIdx: head.HeadIdx,
|
||||||
|
Length: uintptr(length),
|
||||||
|
Indent: ctx.indent,
|
||||||
|
Size: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapHeaderCode(ctx *compileContext) *Opcode {
|
||||||
|
idx := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
length := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
mapIter := opcodeOffset(ctx.ptrIndex)
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpMap,
|
||||||
|
Type: ctx.typ,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: idx,
|
||||||
|
ElemIdx: elemIdx,
|
||||||
|
Length: length,
|
||||||
|
MapIter: mapIter,
|
||||||
|
Indent: ctx.indent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpMapKey,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: opcodeOffset(ctx.ptrIndex),
|
||||||
|
ElemIdx: head.ElemIdx,
|
||||||
|
Length: head.Length,
|
||||||
|
MapIter: head.MapIter,
|
||||||
|
Indent: ctx.indent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapValueCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpMapValue,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: opcodeOffset(ctx.ptrIndex),
|
||||||
|
ElemIdx: head.ElemIdx,
|
||||||
|
Length: head.Length,
|
||||||
|
MapIter: head.MapIter,
|
||||||
|
Indent: ctx.indent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||||
|
mapPos := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
idx := opcodeOffset(ctx.ptrIndex)
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpMapEnd,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: idx,
|
||||||
|
Length: head.Length,
|
||||||
|
MapPos: mapPos,
|
||||||
|
Indent: ctx.indent,
|
||||||
|
Next: newEndOp(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInterfaceCode(ctx *compileContext) *Opcode {
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpInterface,
|
||||||
|
Type: ctx.typ,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: opcodeOffset(ctx.ptrIndex),
|
||||||
|
Indent: ctx.indent,
|
||||||
|
Next: newEndOp(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRecursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
|
||||||
|
return &Opcode{
|
||||||
|
Op: OpStructFieldRecursive,
|
||||||
|
Type: ctx.typ,
|
||||||
|
DisplayIdx: ctx.opcodeIndex,
|
||||||
|
Idx: opcodeOffset(ctx.ptrIndex),
|
||||||
|
Indent: ctx.indent,
|
||||||
|
Next: newEndOp(ctx),
|
||||||
|
Jmp: jmp,
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,11 +9,10 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/goccy/go-json/internal/encoder"
|
"github.com/goccy/go-json/internal/encoder"
|
||||||
"github.com/goccy/go-json/internal/encoder/compiler"
|
|
||||||
"github.com/goccy/go-json/internal/runtime"
|
"github.com/goccy/go-json/internal/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
const uintptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
|
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
load = encoder.Load
|
load = encoder.Load
|
||||||
|
@ -173,7 +172,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
|
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
|
||||||
ifaceCodeSet, err := compiler.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTag(field reflect.StructField) string {
|
||||||
|
return field.Tag.Get("json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsIgnoredStructField(field reflect.StructField) bool {
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
if field.Anonymous {
|
||||||
|
if !(field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct) && field.Type.Kind() != reflect.Struct {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// private field
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tag := getTag(field)
|
||||||
|
return tag == "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
type StructTag struct {
|
||||||
|
Key string
|
||||||
|
IsTaggedKey bool
|
||||||
|
IsOmitEmpty bool
|
||||||
|
IsString bool
|
||||||
|
Field reflect.StructField
|
||||||
|
}
|
||||||
|
|
||||||
|
type StructTags []*StructTag
|
||||||
|
|
||||||
|
func (t StructTags) ExistsKey(key string) bool {
|
||||||
|
for _, tt := range t {
|
||||||
|
if tt.Key == key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidTag(s string) bool {
|
||||||
|
if s == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range s {
|
||||||
|
switch {
|
||||||
|
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
|
||||||
|
// Backslash and quote chars are reserved, but
|
||||||
|
// otherwise any punctuation chars are allowed
|
||||||
|
// in a tag name.
|
||||||
|
case !unicode.IsLetter(c) && !unicode.IsDigit(c):
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func StructTagFromField(field reflect.StructField) *StructTag {
|
||||||
|
keyName := field.Name
|
||||||
|
tag := getTag(field)
|
||||||
|
st := &StructTag{Field: field}
|
||||||
|
opts := strings.Split(tag, ",")
|
||||||
|
if len(opts) > 0 {
|
||||||
|
if opts[0] != "" && isValidTag(opts[0]) {
|
||||||
|
keyName = opts[0]
|
||||||
|
st.IsTaggedKey = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
st.Key = keyName
|
||||||
|
if len(opts) > 1 {
|
||||||
|
st.IsOmitEmpty = opts[1] == "omitempty"
|
||||||
|
st.IsString = opts[1] == "string"
|
||||||
|
}
|
||||||
|
return st
|
||||||
|
}
|
|
@ -1,9 +1,87 @@
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
type SliceHeader struct {
|
type SliceHeader struct {
|
||||||
Data unsafe.Pointer
|
Data unsafe.Pointer
|
||||||
Len int
|
Len int
|
||||||
Cap int
|
Cap int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
|
||||||
|
)
|
||||||
|
|
||||||
|
type TypeAddr struct {
|
||||||
|
BaseTypeAddr uintptr
|
||||||
|
MaxTypeAddr uintptr
|
||||||
|
AddrRange uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
typeAddr *TypeAddr
|
||||||
|
alreadyAnalyzed bool
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname typelinks reflect.typelinks
|
||||||
|
func typelinks() ([]unsafe.Pointer, [][]int32)
|
||||||
|
|
||||||
|
//go:linkname rtypeOff reflect.rtypeOff
|
||||||
|
func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer
|
||||||
|
|
||||||
|
func AnalyzeTypeAddr() *TypeAddr {
|
||||||
|
defer func() {
|
||||||
|
alreadyAnalyzed = true
|
||||||
|
}()
|
||||||
|
if alreadyAnalyzed {
|
||||||
|
return typeAddr
|
||||||
|
}
|
||||||
|
sections, offsets := typelinks()
|
||||||
|
if len(sections) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(offsets) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
section := sections[0]
|
||||||
|
offset := offsets[0]
|
||||||
|
var (
|
||||||
|
min uintptr = uintptr(^uint(0))
|
||||||
|
max uintptr = 0
|
||||||
|
)
|
||||||
|
for i := 0; i < len(offset); i++ {
|
||||||
|
typ := (*Type)(rtypeOff(section, offset[i]))
|
||||||
|
addr := uintptr(unsafe.Pointer(typ))
|
||||||
|
if min > addr {
|
||||||
|
min = addr
|
||||||
|
}
|
||||||
|
if max < addr {
|
||||||
|
max = addr
|
||||||
|
}
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
addr = uintptr(unsafe.Pointer(typ.Elem()))
|
||||||
|
if min > addr {
|
||||||
|
min = addr
|
||||||
|
}
|
||||||
|
if max < addr {
|
||||||
|
max = addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addrRange := max - min
|
||||||
|
if addrRange == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if addrRange > maxAcceptableTypeAddrRange {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
typeAddr = &TypeAddr{
|
||||||
|
BaseTypeAddr: min,
|
||||||
|
MaxTypeAddr: max,
|
||||||
|
AddrRange: addrRange,
|
||||||
|
}
|
||||||
|
return typeAddr
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue