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
|
||||
)
|
||||
|
||||
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
|
||||
func (t OpType) IsMultipleOpHead() bool {
|
||||
switch t {
|
||||
case OpStructHead:
|
||||
return true
|
||||
case OpStructHeadSlice:
|
||||
return true
|
||||
case OpStructHeadArray:
|
||||
return true
|
||||
case OpStructHeadMap:
|
||||
return true
|
||||
case OpStructHeadStruct:
|
||||
return true
|
||||
case OpStructHeadOmitEmpty:
|
||||
return true
|
||||
case OpStructHeadOmitEmptySlice:
|
||||
return true
|
||||
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
|
||||
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 (t OpType) IsMultipleOpField() bool {
|
||||
switch t {
|
||||
case OpStructField:
|
||||
return true
|
||||
case OpStructFieldSlice:
|
||||
return true
|
||||
case OpStructFieldArray:
|
||||
return true
|
||||
case OpStructFieldMap:
|
||||
return true
|
||||
case OpStructFieldStruct:
|
||||
return true
|
||||
case OpStructFieldOmitEmpty:
|
||||
return true
|
||||
case OpStructFieldOmitEmptySlice:
|
||||
return true
|
||||
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 {
|
||||
|
|
|
@ -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"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/encoder/compiler"
|
||||
"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 (
|
||||
load = encoder.Load
|
||||
|
@ -173,7 +172,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
|
|||
break
|
||||
}
|
||||
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 {
|
||||
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
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len 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