Merge pull request #45 from goccy/feature/update-encode-engine

Upgrade encoding engine to the 2nd generation
This commit is contained in:
Masaaki Goshima 2020-09-04 21:53:15 +09:00 committed by GitHub
commit eb088b8b76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 3552 additions and 3710 deletions

View File

@ -220,6 +220,7 @@ func (t opType) fieldToStringTagField() opType {
opTypes := []opType{
{"End", "EndIndent", "Op"},
{"Interface", "InterfaceIndent", "Op"},
{"InterfaceEnd", "InterfaceEndIndent", "Op"},
{"Ptr", "PtrIndent", "Op"},
{"SliceHead", "SliceHeadIndent", "SliceHead"},
{"RootSliceHead", "RootSliceHeadIndent", "SliceHead"},
@ -252,6 +253,7 @@ func (t opType) fieldToStringTagField() opType {
{"StructFieldOmitEmpty", "StructFieldOmitEmptyIndent", "StructField"},
{"StructFieldStringTag", "StructFieldStringTagIndent", "StructField"},
{"StructFieldRecursive", "StructFieldRecursiveIndent", "StructFieldRecursive"},
{"StructFieldRecursiveEnd", "StructFieldRecursiveEndIndent", "Op"},
{"StructEnd", "StructEndIndent", "StructField"},
{"StructAnonymousEnd", "StructAnonymousEndIndent", "StructField"},
}

View File

@ -19,7 +19,6 @@ type Encoder struct {
enabledHTMLEscape bool
prefix []byte
indentStr []byte
indent int
structTypeToCompiledCode map[uintptr]*compiledCode
structTypeToCompiledIndentCode map[uintptr]*compiledCode
}
@ -37,8 +36,9 @@ type opcodeMap struct {
}
type opcodeSet struct {
codeIndent sync.Pool
code sync.Pool
codeIndent *opcode
code *opcode
ctx sync.Pool
}
func (m *opcodeMap) get(k uintptr) *opcodeSet {
@ -123,7 +123,6 @@ func (e *Encoder) release() {
func (e *Encoder) reset() {
e.buf = e.buf[:0]
e.indent = 0
e.enabledHTMLEscape = true
e.enabledIndent = false
}
@ -159,54 +158,68 @@ func (e *Encoder) encode(v interface{}) error {
if codeSet := cachedOpcode.get(typeptr); codeSet != nil {
var code *opcode
if e.enabledIndent {
code = codeSet.codeIndent.Get().(*opcode)
code = codeSet.codeIndent
} else {
code = codeSet.code.Get().(*opcode)
code = codeSet.code
}
ctx := codeSet.ctx.Get().(*encodeRuntimeContext)
p := uintptr(header.ptr)
code.ptr = p
if err := e.run(code); err != nil {
return err
}
if e.enabledIndent {
codeSet.codeIndent.Put(code)
} else {
codeSet.code.Put(code)
}
return nil
ctx.init(p)
err := e.run(ctx, code)
codeSet.ctx.Put(ctx)
return err
}
// noescape trick for header.typ ( reflect.*rtype )
copiedType := (*rtype)(unsafe.Pointer(typeptr))
codeIndent, err := e.compileHead(copiedType, true)
codeIndent, err := e.compileHead(&encodeCompileContext{
typ: copiedType,
root: true,
withIndent: true,
})
if err != nil {
return err
}
code, err := e.compileHead(copiedType, false)
code, err := e.compileHead(&encodeCompileContext{
typ: copiedType,
root: true,
withIndent: false,
})
if err != nil {
return err
}
codeLength := code.totalLength()
codeSet := &opcodeSet{
codeIndent: sync.Pool{
codeIndent: codeIndent,
code: code,
ctx: sync.Pool{
New: func() interface{} {
return copyOpcode(codeIndent)
},
},
code: sync.Pool{
New: func() interface{} {
return copyOpcode(code)
return &encodeRuntimeContext{
ptrs: make([]uintptr, codeLength),
keepRefs: make([]unsafe.Pointer, 8),
}
},
},
}
cachedOpcode.set(typeptr, codeSet)
p := uintptr(header.ptr)
ctx := codeSet.ctx.Get().(*encodeRuntimeContext)
ctx.init(p)
var c *opcode
if e.enabledIndent {
codeIndent.ptr = p
return e.run(codeIndent)
c = codeIndent
} else {
c = code
}
code.ptr = p
return e.run(code)
if err := e.run(ctx, c); err != nil {
codeSet.ctx.Put(ctx)
return err
}
codeSet.ctx.Put(ctx)
return nil
}
func (e *Encoder) encodeInt(v int) {

View File

@ -6,17 +6,17 @@ import (
"unsafe"
)
func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) {
root := true
func (e *Encoder) compileHead(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
switch {
case typ.Implements(marshalJSONType):
return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil
return e.compileMarshalJSON(ctx)
case rtype_ptrTo(typ).Implements(marshalJSONType):
return newOpCode(opMarshalJSON, rtype_ptrTo(typ), e.indent, newEndOp(e.indent)), nil
return e.compileMarshalJSONPtr(ctx)
case typ.Implements(marshalTextType):
return newOpCode(opMarshalText, typ, e.indent, newEndOp(e.indent)), nil
return e.compileMarshalText(ctx)
case rtype_ptrTo(typ).Implements(marshalTextType):
return newOpCode(opMarshalText, rtype_ptrTo(typ), e.indent, newEndOp(e.indent)), nil
return e.compileMarshalTextPtr(ctx)
}
isPtr := false
if typ.Kind() == reflect.Ptr {
@ -24,11 +24,11 @@ func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) {
isPtr = true
}
if typ.Kind() == reflect.Map {
return e.compileMap(typ, isPtr, root, withIndent)
return e.compileMap(ctx.withType(typ), isPtr)
} else if typ.Kind() == reflect.Struct {
return e.compileStruct(typ, isPtr, root, withIndent)
return e.compileStruct(ctx.withType(typ), isPtr)
}
return e.compile(typ, root, withIndent)
return e.compile(ctx.withType(typ))
}
func (e *Encoder) implementsMarshaler(typ *rtype) bool {
@ -45,215 +45,240 @@ func (e *Encoder) implementsMarshaler(typ *rtype) bool {
return false
}
func (e *Encoder) compile(typ *rtype, root, withIndent bool) (*opcode, error) {
func (e *Encoder) compile(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
switch {
case typ.Implements(marshalJSONType):
return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil
return e.compileMarshalJSON(ctx)
case rtype_ptrTo(typ).Implements(marshalJSONType):
return newOpCode(opMarshalJSON, rtype_ptrTo(typ), e.indent, newEndOp(e.indent)), nil
return e.compileMarshalJSONPtr(ctx)
case typ.Implements(marshalTextType):
return newOpCode(opMarshalText, typ, e.indent, newEndOp(e.indent)), nil
return e.compileMarshalText(ctx)
case rtype_ptrTo(typ).Implements(marshalTextType):
return newOpCode(opMarshalText, rtype_ptrTo(typ), e.indent, newEndOp(e.indent)), nil
return e.compileMarshalTextPtr(ctx)
}
switch typ.Kind() {
case reflect.Ptr:
return e.compilePtr(typ, root, withIndent)
return e.compilePtr(ctx)
case reflect.Slice:
elem := typ.Elem()
if !e.implementsMarshaler(elem) && elem.Kind() == reflect.Uint8 {
return e.compileBytes(typ)
return e.compileBytes(ctx)
}
return e.compileSlice(typ, root, withIndent)
return e.compileSlice(ctx)
case reflect.Array:
return e.compileArray(typ, root, withIndent)
return e.compileArray(ctx)
case reflect.Map:
return e.compileMap(typ, true, root, withIndent)
return e.compileMap(ctx, true)
case reflect.Struct:
return e.compileStruct(typ, false, root, withIndent)
return e.compileStruct(ctx, false)
case reflect.Interface:
return e.compileInterface(typ, root)
return e.compileInterface(ctx)
case reflect.Int:
return e.compileInt(typ)
return e.compileInt(ctx)
case reflect.Int8:
return e.compileInt8(typ)
return e.compileInt8(ctx)
case reflect.Int16:
return e.compileInt16(typ)
return e.compileInt16(ctx)
case reflect.Int32:
return e.compileInt32(typ)
return e.compileInt32(ctx)
case reflect.Int64:
return e.compileInt64(typ)
return e.compileInt64(ctx)
case reflect.Uint:
return e.compileUint(typ)
return e.compileUint(ctx)
case reflect.Uint8:
return e.compileUint8(typ)
return e.compileUint8(ctx)
case reflect.Uint16:
return e.compileUint16(typ)
return e.compileUint16(ctx)
case reflect.Uint32:
return e.compileUint32(typ)
return e.compileUint32(ctx)
case reflect.Uint64:
return e.compileUint64(typ)
return e.compileUint64(ctx)
case reflect.Uintptr:
return e.compileUint(typ)
return e.compileUint(ctx)
case reflect.Float32:
return e.compileFloat32(typ)
return e.compileFloat32(ctx)
case reflect.Float64:
return e.compileFloat64(typ)
return e.compileFloat64(ctx)
case reflect.String:
return e.compileString(typ)
return e.compileString(ctx)
case reflect.Bool:
return e.compileBool(typ)
return e.compileBool(ctx)
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
}
func (e *Encoder) compileKey(typ *rtype, root, withIndent bool) (*opcode, error) {
func (e *Encoder) compileKey(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
switch {
case typ.Implements(marshalJSONType):
return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil
return e.compileMarshalJSON(ctx)
case rtype_ptrTo(typ).Implements(marshalJSONType):
return newOpCode(opMarshalJSON, rtype_ptrTo(typ), e.indent, newEndOp(e.indent)), nil
return e.compileMarshalJSONPtr(ctx)
case typ.Implements(marshalTextType):
return newOpCode(opMarshalText, typ, e.indent, newEndOp(e.indent)), nil
return e.compileMarshalText(ctx)
case rtype_ptrTo(typ).Implements(marshalTextType):
return newOpCode(opMarshalText, rtype_ptrTo(typ), e.indent, newEndOp(e.indent)), nil
return e.compileMarshalTextPtr(ctx)
}
switch typ.Kind() {
case reflect.Ptr:
return e.compilePtr(typ, root, withIndent)
return e.compilePtr(ctx)
case reflect.Interface:
return e.compileInterface(typ, root)
case reflect.Int:
return e.compileInt(typ)
case reflect.Int8:
return e.compileInt8(typ)
case reflect.Int16:
return e.compileInt16(typ)
case reflect.Int32:
return e.compileInt32(typ)
case reflect.Int64:
return e.compileInt64(typ)
case reflect.Uint:
return e.compileUint(typ)
case reflect.Uint8:
return e.compileUint8(typ)
case reflect.Uint16:
return e.compileUint16(typ)
case reflect.Uint32:
return e.compileUint32(typ)
case reflect.Uint64:
return e.compileUint64(typ)
case reflect.Uintptr:
return e.compileUint(typ)
case reflect.Float32:
return e.compileFloat32(typ)
case reflect.Float64:
return e.compileFloat64(typ)
return e.compileInterface(ctx)
case reflect.String:
return e.compileString(typ)
case reflect.Bool:
return e.compileBool(typ)
return e.compileString(ctx)
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
}
func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
ptrHeadOp := code.op.headToPtrHead()
if code.op != ptrHeadOp {
code.op = ptrHeadOp
return code
}
return newOpCode(opPtr, typ, e.indent, code)
}
func (e *Encoder) compilePtr(typ *rtype, root, withIndent bool) (*opcode, error) {
code, err := e.compile(typ.Elem(), root, withIndent)
func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
ptrOpcodeIndex := ctx.opcodeIndex
ctx.incIndex()
code, err := e.compile(ctx.withType(ctx.typ.Elem()))
if err != nil {
return nil, err
}
return e.optimizeStructFieldPtrHead(typ, code), nil
ptrHeadOp := code.op.headToPtrHead()
if code.op != ptrHeadOp {
code.op = ptrHeadOp
code.decOpcodeIndex()
ctx.decIndex()
return code, nil
}
c := ctx.context()
c.opcodeIndex = ptrOpcodeIndex
return newOpCodeWithNext(c, opPtr, code), nil
}
func (e *Encoder) compileInt(typ *rtype) (*opcode, error) {
return newOpCode(opInt, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opMarshalJSON)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt8(typ *rtype) (*opcode, error) {
return newOpCode(opInt8, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileMarshalJSONPtr(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalJSON)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt16(typ *rtype) (*opcode, error) {
return newOpCode(opInt16, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opMarshalText)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt32(typ *rtype) (*opcode, error) {
return newOpCode(opInt32, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileMarshalTextPtr(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalText)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt64(typ *rtype) (*opcode, error) {
return newOpCode(opInt64, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileInt(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint(typ *rtype) (*opcode, error) {
return newOpCode(opUint, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileInt8(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt8)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint8(typ *rtype) (*opcode, error) {
return newOpCode(opUint8, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileInt16(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt16)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint16(typ *rtype) (*opcode, error) {
return newOpCode(opUint16, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileInt32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt32)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint32(typ *rtype) (*opcode, error) {
return newOpCode(opUint32, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileInt64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt64)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint64(typ *rtype) (*opcode, error) {
return newOpCode(opUint64, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileUint(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileFloat32(typ *rtype) (*opcode, error) {
return newOpCode(opFloat32, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileUint8(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint8)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileFloat64(typ *rtype) (*opcode, error) {
return newOpCode(opFloat64, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileUint16(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint16)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileString(typ *rtype) (*opcode, error) {
return newOpCode(opString, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileUint32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint32)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileBool(typ *rtype) (*opcode, error) {
return newOpCode(opBool, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileUint64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint64)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileBytes(typ *rtype) (*opcode, error) {
return newOpCode(opBytes, typ, e.indent, newEndOp(e.indent)), nil
func (e *Encoder) compileFloat32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opFloat32)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInterface(typ *rtype, root bool) (*opcode, error) {
return (*opcode)(unsafe.Pointer(&interfaceCode{
opcodeHeader: &opcodeHeader{
op: opInterface,
typ: typ,
indent: e.indent,
next: newEndOp(e.indent),
},
root: root,
})), nil
func (e *Encoder) compileFloat64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opFloat64)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileSlice(typ *rtype, root, withIndent bool) (*opcode, error) {
elem := typ.Elem()
func (e *Encoder) compileString(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opString)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileBool(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opBool)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileBytes(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opBytes)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInterface(ctx *encodeCompileContext) (*opcode, error) {
code := newInterfaceCode(ctx)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
ctx.root = false
elem := ctx.typ.Elem()
size := elem.Size()
e.indent++
code, err := e.compile(elem, false, withIndent)
e.indent--
header := newSliceHeaderCode(ctx)
ctx.incIndex()
code, err := e.compile(ctx.withType(ctx.typ.Elem()).incIndent())
if err != nil {
return nil, err
}
@ -262,17 +287,13 @@ func (e *Encoder) compileSlice(typ *rtype, root, withIndent bool) (*opcode, erro
// ^ |
// |________|
header := newSliceHeaderCode(e.indent)
elemCode := &sliceElemCode{
opcodeHeader: &opcodeHeader{
op: opSliceElem,
indent: e.indent,
},
size: size,
}
end := newOpCode(opSliceEnd, nil, e.indent, newEndOp(e.indent))
if withIndent {
if root {
elemCode := newSliceElemCode(ctx, header, size)
ctx.incIndex()
end := newOpCode(ctx, opSliceEnd)
ctx.incIndex()
if ctx.withIndent {
if ctx.root {
header.op = opRootSliceHeadIndent
elemCode.op = opRootSliceElemIndent
} else {
@ -291,15 +312,17 @@ func (e *Encoder) compileSlice(typ *rtype, root, withIndent bool) (*opcode, erro
return (*opcode)(unsafe.Pointer(header)), nil
}
func (e *Encoder) compileArray(typ *rtype, root, withIndent bool) (*opcode, error) {
func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
ctx.root = false
typ := ctx.typ
elem := typ.Elem()
alen := typ.Len()
size := elem.Size()
e.indent++
code, err := e.compile(elem, false, withIndent)
e.indent--
header := newArrayHeaderCode(ctx, alen)
ctx.incIndex()
code, err := e.compile(ctx.withType(elem).incIndent())
if err != nil {
return nil, err
}
@ -307,17 +330,13 @@ func (e *Encoder) compileArray(typ *rtype, root, withIndent bool) (*opcode, erro
// ^ |
// |________|
header := newArrayHeaderCode(e.indent, alen)
elemCode := &arrayElemCode{
opcodeHeader: &opcodeHeader{
op: opArrayElem,
},
len: uintptr(alen),
size: size,
}
end := newOpCode(opArrayEnd, nil, e.indent, newEndOp(e.indent))
elemCode := newArrayElemCode(ctx, header, alen, size)
ctx.incIndex()
if withIndent {
end := newOpCode(ctx, opArrayEnd)
ctx.incIndex()
if ctx.withIndent {
header.op = opArrayHeadIndent
elemCode.op = opArrayElemIndent
end.op = opArrayEndIndent
@ -348,35 +367,43 @@ func mapiternext(it unsafe.Pointer)
//go:noescape
func maplen(m unsafe.Pointer) int
func (e *Encoder) compileMap(typ *rtype, withLoad, root, withIndent bool) (*opcode, error) {
func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode, error) {
// header => code => value => code => key => code => value => code => end
// ^ |
// |_______________________|
e.indent++
keyType := typ.Key()
keyCode, err := e.compileKey(keyType, false, withIndent)
ctx = ctx.incIndent()
header := newMapHeaderCode(ctx, withLoad)
ctx.incIndex()
typ := ctx.typ
keyType := ctx.typ.Key()
keyCode, err := e.compileKey(ctx.withType(keyType))
if err != nil {
return nil, err
}
value := newMapValueCode(ctx, header)
ctx.incIndex()
valueType := typ.Elem()
valueCode, err := e.compile(valueType, false, withIndent)
valueCode, err := e.compile(ctx.withType(valueType))
if err != nil {
return nil, err
}
key := newMapKeyCode(e.indent)
value := newMapValueCode(e.indent)
key := newMapKeyCode(ctx, header)
ctx.incIndex()
e.indent--
ctx = ctx.decIndent()
header := newMapHeaderCode(typ, withLoad, e.indent)
header.key = key
header.value = value
end := newOpCode(opMapEnd, nil, e.indent, newEndOp(e.indent))
header.mapKey = key
header.mapValue = value
end := newOpCode(ctx, opMapEnd)
ctx.incIndex()
if withIndent {
if ctx.withIndent {
if header.op == opMapHead {
if root {
if ctx.root {
header.op = opRootMapHeadIndent
} else {
header.op = opMapHeadIndent
@ -384,7 +411,7 @@ func (e *Encoder) compileMap(typ *rtype, withLoad, root, withIndent bool) (*opco
} else {
header.op = opMapHeadLoadIndent
}
if root {
if ctx.root {
key.op = opRootMapKeyIndent
} else {
key.op = opMapKeyIndent
@ -549,35 +576,30 @@ func (e *Encoder) optimizeStructField(op opType, tag *structTag, withIndent bool
return fieldType
}
func (e *Encoder) recursiveCode(typ *rtype, code *compiledCode) *opcode {
return (*opcode)(unsafe.Pointer(&recursiveCode{
opcodeHeader: &opcodeHeader{
op: opStructFieldRecursive,
typ: typ,
indent: e.indent,
next: newEndOp(e.indent),
},
jmp: code,
}))
func (e *Encoder) recursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
code := newRecursiveCode(ctx, jmp)
ctx.incIndex()
return code
}
func (e *Encoder) compiledCode(typ *rtype, withIndent bool) *opcode {
func (e *Encoder) compiledCode(ctx *encodeCompileContext) *opcode {
typ := ctx.typ
typeptr := uintptr(unsafe.Pointer(typ))
if withIndent {
if ctx.withIndent {
if compiledCode, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
return e.recursiveCode(typ, compiledCode)
return e.recursiveCode(ctx, compiledCode)
}
} else {
if compiledCode, exists := e.structTypeToCompiledCode[typeptr]; exists {
return e.recursiveCode(typ, compiledCode)
return e.recursiveCode(ctx, compiledCode)
}
}
return nil
}
func (e *Encoder) structHeader(fieldCode *structFieldCode, valueCode *opcode, tag *structTag, withIndent bool) *opcode {
func (e *Encoder) structHeader(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
fieldCode.indent--
op := e.optimizeStructHeader(valueCode.op, tag, withIndent)
op := e.optimizeStructHeader(valueCode.op, tag, ctx.withIndent)
fieldCode.op = op
switch op {
case opStructFieldHead,
@ -608,12 +630,13 @@ func (e *Encoder) structHeader(fieldCode *structFieldCode, valueCode *opcode, ta
opStructFieldHeadStringTagIndent:
return valueCode.beforeLastCode()
}
ctx.decOpcodeIndex()
return (*opcode)(unsafe.Pointer(fieldCode))
}
func (e *Encoder) structField(fieldCode *structFieldCode, valueCode *opcode, tag *structTag, withIndent bool) *opcode {
func (e *Encoder) structField(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
code := (*opcode)(unsafe.Pointer(fieldCode))
op := e.optimizeStructField(valueCode.op, tag, withIndent)
op := e.optimizeStructField(valueCode.op, tag, ctx.withIndent)
fieldCode.op = op
switch op {
case opStructField,
@ -644,10 +667,11 @@ func (e *Encoder) structField(fieldCode *structFieldCode, valueCode *opcode, tag
opStructFieldStringTagIndent:
return valueCode.beforeLastCode()
}
ctx.decIndex()
return code
}
func (e *Encoder) isNotExistsField(head *structFieldCode) bool {
func (e *Encoder) isNotExistsField(head *opcode) bool {
if head == nil {
return false
}
@ -669,12 +693,12 @@ func (e *Encoder) isNotExistsField(head *structFieldCode) bool {
if head.next.op.codeType() != codeStructField {
return false
}
return e.isNotExistsField(head.next.toStructFieldCode())
return e.isNotExistsField(head.next)
}
func (e *Encoder) optimizeAnonymousFields(head *structFieldCode) {
func (e *Encoder) optimizeAnonymousFields(head *opcode) {
code := head
var prev *structFieldCode
var prev *opcode
for {
if code.op == opStructEnd || code.op == opStructEndIndent {
break
@ -682,29 +706,33 @@ func (e *Encoder) optimizeAnonymousFields(head *structFieldCode) {
if code.op == opStructField || code.op == opStructFieldIndent {
codeType := code.next.op.codeType()
if codeType == codeStructField {
if e.isNotExistsField(code.next.toStructFieldCode()) {
if e.isNotExistsField(code.next) {
code.next = code.nextField
diff := code.next.displayIdx - code.displayIdx
for i := 0; i < diff; i++ {
code.next.decOpcodeIndex()
}
linkPrevToNextField(prev, code)
code = prev
}
}
}
prev = code
code = code.nextField.toStructFieldCode()
code = code.nextField
}
}
type structFieldPair struct {
prevField *structFieldCode
curField *structFieldCode
prevField *opcode
curField *opcode
isTaggedKey bool
linked bool
}
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *structFieldCode) map[string][]structFieldPair {
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *opcode) map[string][]structFieldPair {
anonymousFields := map[string][]structFieldPair{}
f := valueCode
var prevAnonymousField *structFieldCode
var prevAnonymousField *opcode
for {
existsKey := tags.existsKey(f.displayKey)
op := f.op.headToAnonymousHead()
@ -717,6 +745,10 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, value
} else if f.op == opStructEnd {
f.op = opStructAnonymousEnd
} else if existsKey {
diff := f.nextField.displayIdx - f.displayIdx
for i := 0; i < diff; i++ {
f.nextField.decOpcodeIndex()
}
linkPrevToNextField(prevAnonymousField, f)
}
@ -725,7 +757,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, value
break
}
prevAnonymousField = f
f = f.nextField.toStructFieldCode()
f = f.nextField
continue
}
@ -735,7 +767,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, value
isTaggedKey: f.isTaggedKey,
})
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
for k, v := range e.anonymousStructFieldPairMap(typ, tags, f.next.toStructFieldCode()) {
for k, v := range e.anonymousStructFieldPairMap(typ, tags, f.next) {
anonymousFields[k] = append(anonymousFields[k], v...)
}
}
@ -743,7 +775,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, value
break
}
prevAnonymousField = f
f = f.nextField.toStructFieldCode()
f = f.nextField
}
return anonymousFields
}
@ -764,6 +796,10 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
// head operation
fieldPair.curField.op = opStructFieldAnonymousHead
} else {
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
for i := 0; i < diff; i++ {
fieldPair.curField.nextField.decOpcodeIndex()
}
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
}
fieldPair.linked = true
@ -777,6 +813,10 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
// head operation
fieldPair.curField.op = opStructFieldAnonymousHead
} else {
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
for i := 0; i < diff; i++ {
fieldPair.curField.nextField.decOpcodeIndex()
}
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
}
fieldPair.linked = true
@ -790,13 +830,15 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
}
}
func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opcode, error) {
if code := e.compiledCode(typ, withIndent); code != nil {
func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error) {
ctx.root = false
if code := e.compiledCode(ctx); code != nil {
return code, nil
}
typ := ctx.typ
typeptr := uintptr(unsafe.Pointer(typ))
compiled := &compiledCode{}
if withIndent {
if ctx.withIndent {
e.structTypeToCompiledIndentCode[typeptr] = compiled
} else {
e.structTypeToCompiledCode[typeptr] = compiled
@ -807,11 +849,11 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
fieldNum := typ.NumField()
fieldIdx := 0
var (
head *structFieldCode
head *opcode
code *opcode
prevField *structFieldCode
prevField *opcode
)
e.indent++
ctx = ctx.incIndent()
tags := structTags{}
anonymousFields := map[string][]structFieldPair{}
for i := 0; i < fieldNum; i++ {
@ -833,12 +875,16 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
fieldType = rtype_ptrTo(fieldType)
}
}
valueCode, err := e.compile(fieldType, false, withIndent)
fieldOpcodeIndex := ctx.opcodeIndex
fieldPtrIndex := ctx.ptrIndex
ctx.incIndex()
valueCode, err := e.compile(ctx.withType(fieldType))
if err != nil {
return nil, err
}
if field.Anonymous {
for k, v := range e.anonymousStructFieldPairMap(typ, tags, valueCode.toStructFieldCode()) {
for k, v := range e.anonymousStructFieldPairMap(typ, tags, valueCode) {
anonymousFields[k] = append(anonymousFields[k], v...)
}
}
@ -850,15 +896,16 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
opUint, opUint8, opUint16, opUint32, opUint64,
opFloat32, opFloat64, opBool, opString, opBytes:
valueCode = valueCode.next
ctx.decOpcodeIndex()
}
}
key := fmt.Sprintf(`"%s":`, tag.key)
fieldCode := &structFieldCode{
opcodeHeader: &opcodeHeader{
typ: valueCode.typ,
next: valueCode,
indent: e.indent,
},
fieldCode := &opcode{
typ: valueCode.typ,
displayIdx: fieldOpcodeIndex,
idx: opcodeOffset(fieldPtrIndex),
next: valueCode,
indent: ctx.indent,
anonymousKey: field.Anonymous,
key: []byte(key),
isTaggedKey: tag.isTaggedKey,
@ -866,29 +913,51 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
offset: field.Offset,
}
if fieldIdx == 0 {
code = e.structHeader(fieldCode, valueCode, tag, withIndent)
fieldCode.headIdx = fieldCode.idx
code = e.structHeader(ctx, fieldCode, valueCode, tag)
head = fieldCode
prevField = fieldCode
} else {
fcode := (*opcode)(unsafe.Pointer(fieldCode))
code.next = fcode
code = e.structField(fieldCode, valueCode, tag, withIndent)
prevField.nextField = fcode
fieldCode.headIdx = head.headIdx
code.next = fieldCode
code = e.structField(ctx, fieldCode, valueCode, tag)
prevField.nextField = fieldCode
prevField = fieldCode
}
fieldIdx++
}
e.indent--
ctx = ctx.decIndent()
structEndCode := (*opcode)(unsafe.Pointer(&structFieldCode{
opcodeHeader: &opcodeHeader{
op: opStructEnd,
typ: nil,
indent: e.indent,
},
}))
structEndCode.next = newEndOp(e.indent)
if withIndent {
structEndCode := &opcode{
op: opStructEnd,
typ: nil,
indent: ctx.indent,
next: newEndOp(ctx),
}
// no struct field
if head == nil {
head = &opcode{
op: opStructFieldHead,
typ: typ,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
headIdx: opcodeOffset(ctx.ptrIndex),
indent: ctx.indent,
nextField: structEndCode,
}
ctx.incIndex()
if ctx.withIndent {
head.op = opStructFieldHeadIndent
}
code = head
}
structEndCode.displayIdx = ctx.opcodeIndex
structEndCode.idx = opcodeOffset(ctx.ptrIndex)
ctx.incIndex()
if ctx.withIndent {
structEndCode.op = opStructEndIndent
}
@ -896,21 +965,6 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
prevField.nextField = structEndCode
}
// no struct field
if head == nil {
head = &structFieldCode{
opcodeHeader: &opcodeHeader{
op: opStructFieldHead,
typ: typ,
indent: e.indent,
},
nextField: structEndCode,
}
if withIndent {
head.op = opStructFieldHeadIndent
}
code = (*opcode)(unsafe.Pointer(head))
}
head.end = structEndCode
code.next = structEndCode
@ -920,7 +974,7 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
ret := (*opcode)(unsafe.Pointer(head))
compiled.code = ret
if withIndent {
if ctx.withIndent {
delete(e.structTypeToCompiledIndentCode, typeptr)
} else {
delete(e.structTypeToCompiledCode, typeptr)

100
encode_context.go Normal file
View File

@ -0,0 +1,100 @@
package json
import (
"reflect"
"unsafe"
)
type encodeCompileContext struct {
typ *rtype
withIndent bool
root bool
opcodeIndex int
ptrIndex int
indent int
parent *encodeCompileContext
}
func (c *encodeCompileContext) context() *encodeCompileContext {
return &encodeCompileContext{
typ: c.typ,
withIndent: c.withIndent,
root: c.root,
opcodeIndex: c.opcodeIndex,
ptrIndex: c.ptrIndex,
indent: c.indent,
parent: c,
}
}
func (c *encodeCompileContext) withType(typ *rtype) *encodeCompileContext {
ctx := c.context()
ctx.typ = typ
return ctx
}
func (c *encodeCompileContext) incIndent() *encodeCompileContext {
ctx := c.context()
ctx.indent++
return ctx
}
func (c *encodeCompileContext) decIndent() *encodeCompileContext {
ctx := c.context()
ctx.indent--
return ctx
}
func (c *encodeCompileContext) incIndex() {
c.incOpcodeIndex()
c.incPtrIndex()
}
func (c *encodeCompileContext) decIndex() {
c.decOpcodeIndex()
c.decPtrIndex()
}
func (c *encodeCompileContext) incOpcodeIndex() {
c.opcodeIndex++
if c.parent != nil {
c.parent.incOpcodeIndex()
}
}
func (c *encodeCompileContext) decOpcodeIndex() {
c.opcodeIndex--
if c.parent != nil {
c.parent.decOpcodeIndex()
}
}
func (c *encodeCompileContext) incPtrIndex() {
c.ptrIndex++
if c.parent != nil {
c.parent.incPtrIndex()
}
}
func (c *encodeCompileContext) decPtrIndex() {
c.ptrIndex--
if c.parent != nil {
c.parent.decPtrIndex()
}
}
type encodeRuntimeContext struct {
ptrs []uintptr
keepRefs []unsafe.Pointer
}
func (c *encodeRuntimeContext) init(p uintptr) {
c.ptrs[0] = p
c.keepRefs = c.keepRefs[:0]
}
func (c *encodeRuntimeContext) ptr() uintptr {
header := (*reflect.SliceHeader)(unsafe.Pointer(&c.ptrs))
return header.Data
}

View File

@ -2,51 +2,61 @@ package json
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
func copyOpcode(code *opcode) *opcode {
codeMap := map[uintptr]*opcode{}
return code.copy(codeMap)
}
type opcodeHeader struct {
op opType
typ *rtype
ptr uintptr
indent int
next *opcode
}
func (h *opcodeHeader) copy(codeMap map[uintptr]*opcode) *opcodeHeader {
return &opcodeHeader{
op: h.op,
typ: h.typ,
ptr: h.ptr,
indent: h.indent,
next: h.next.copy(codeMap),
}
}
var uintptrSize = unsafe.Sizeof(uintptr(0))
type opcode struct {
*opcodeHeader
op opType // operation type
typ *rtype // go type
displayIdx int // opcode index
key []byte // struct field key
displayKey string // key text to display
isTaggedKey bool // whether tagged key
anonymousKey bool // whether anonymous key
root bool // whether root
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
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
nextField *opcode // next struct field
next *opcode // next opcode
jmp *compiledCode // for recursive call
}
func newOpCode(op opType, typ *rtype, indent int, next *opcode) *opcode {
func newOpCode(ctx *encodeCompileContext, op opType) *opcode {
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
}
func opcodeOffset(idx int) uintptr {
return uintptr(idx) * uintptrSize
}
func newOpCodeWithNext(ctx *encodeCompileContext, op opType, next *opcode) *opcode {
return &opcode{
opcodeHeader: &opcodeHeader{
op: op,
typ: typ,
indent: indent,
next: next,
},
op: op,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
indent: ctx.indent,
idx: opcodeOffset(ctx.ptrIndex),
next: next,
}
}
func newEndOp(indent int) *opcode {
return newOpCode(opEnd, nil, indent, nil)
func newEndOp(ctx *encodeCompileContext) *opcode {
return newOpCodeWithNext(ctx, opEnd, nil)
}
func (c *opcode) beforeLastCode() *opcode {
@ -54,12 +64,8 @@ func (c *opcode) beforeLastCode() *opcode {
for {
var nextCode *opcode
switch code.op.codeType() {
case codeArrayElem:
nextCode = code.toArrayElemCode().end
case codeSliceElem:
nextCode = code.toSliceElemCode().end
case codeMapKey:
nextCode = code.toMapKeyCode().end
case codeArrayElem, codeSliceElem, codeMapKey:
nextCode = code.end
default:
nextCode = code.next
}
@ -71,270 +77,183 @@ func (c *opcode) beforeLastCode() *opcode {
return nil
}
func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
func (c *opcode) totalLength() int {
var idx int
for code := c; code.op != opEnd; {
idx = int(code.idx / uintptrSize)
if code.op == opInterfaceEnd || code.op == opStructFieldRecursiveEnd {
break
}
switch code.op.codeType() {
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
default:
code = code.next
}
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
var code *opcode
switch c.op.codeType() {
case codeArrayHead:
code = c.toArrayHeaderCode().copy(codeMap)
case codeArrayElem:
code = c.toArrayElemCode().copy(codeMap)
case codeSliceHead:
code = c.toSliceHeaderCode().copy(codeMap)
case codeSliceElem:
code = c.toSliceElemCode().copy(codeMap)
case codeMapHead:
code = c.toMapHeadCode().copy(codeMap)
case codeMapKey:
code = c.toMapKeyCode().copy(codeMap)
case codeMapValue:
code = c.toMapValueCode().copy(codeMap)
case codeStructFieldRecursive:
code = c.toRecursiveCode().copy(codeMap)
case codeStructField:
code = c.toStructFieldCode().copy(codeMap)
default:
code = &opcode{}
codeMap[addr] = code
return idx + 2 // opEnd + 1
}
code.opcodeHeader = c.opcodeHeader.copy(codeMap)
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
}
}
return code
}
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) 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; {
indent := strings.Repeat(" ", code.indent)
switch code.op.codeType() {
case codeArrayElem:
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
code = code.toArrayElemCode().end
case codeSliceElem:
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
code = code.toSliceElemCode().end
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, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
code = code.toMapKeyCode().end
codes = append(codes, c.dumpKey(code))
code = code.end
case codeMapValue:
codes = append(codes, c.dumpValue(code))
code = code.next
case codeStructField:
sf := code.toStructFieldCode()
key := sf.displayKey
offset := sf.offset
codes = append(codes, fmt.Sprintf("%s%s [%s:%d] ( %p )", indent, code.op, key, offset, unsafe.Pointer(code)))
codes = append(codes, c.dumpField(code))
code = code.next
default:
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
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 (c *opcode) toSliceHeaderCode() *sliceHeaderCode {
return (*sliceHeaderCode)(unsafe.Pointer(c))
}
func (c *opcode) toSliceElemCode() *sliceElemCode {
return (*sliceElemCode)(unsafe.Pointer(c))
}
func (c *opcode) toArrayHeaderCode() *arrayHeaderCode {
return (*arrayHeaderCode)(unsafe.Pointer(c))
}
func (c *opcode) toArrayElemCode() *arrayElemCode {
return (*arrayElemCode)(unsafe.Pointer(c))
}
func (c *opcode) toStructFieldCode() *structFieldCode {
return (*structFieldCode)(unsafe.Pointer(c))
}
func (c *opcode) toMapHeadCode() *mapHeaderCode {
return (*mapHeaderCode)(unsafe.Pointer(c))
}
func (c *opcode) toMapKeyCode() *mapKeyCode {
return (*mapKeyCode)(unsafe.Pointer(c))
}
func (c *opcode) toMapValueCode() *mapValueCode {
return (*mapValueCode)(unsafe.Pointer(c))
}
func (c *opcode) toInterfaceCode() *interfaceCode {
return (*interfaceCode)(unsafe.Pointer(c))
}
func (c *opcode) toRecursiveCode() *recursiveCode {
return (*recursiveCode)(unsafe.Pointer(c))
}
type sliceHeaderCode struct {
*opcodeHeader
elem *sliceElemCode
end *opcode
}
func newSliceHeaderCode(indent int) *sliceHeaderCode {
return &sliceHeaderCode{
opcodeHeader: &opcodeHeader{
op: opSliceHead,
indent: indent,
},
}
}
func (c *sliceHeaderCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
header := &sliceHeaderCode{}
code := (*opcode)(unsafe.Pointer(header))
codeMap[addr] = code
header.opcodeHeader = c.opcodeHeader.copy(codeMap)
header.elem = (*sliceElemCode)(unsafe.Pointer(c.elem.copy(codeMap)))
header.end = c.end.copy(codeMap)
return code
}
type sliceElemCode struct {
*opcodeHeader
idx uintptr
len uintptr
size uintptr
data uintptr
end *opcode
}
func (c *sliceElemCode) set(header *reflect.SliceHeader) {
c.idx = uintptr(0)
c.len = uintptr(header.Len)
c.data = header.Data
}
func (c *sliceElemCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
elem := &sliceElemCode{
idx: c.idx,
len: c.len,
size: c.size,
data: c.data,
}
code := (*opcode)(unsafe.Pointer(elem))
codeMap[addr] = code
elem.opcodeHeader = c.opcodeHeader.copy(codeMap)
elem.end = c.end.copy(codeMap)
return code
}
type arrayHeaderCode struct {
*opcodeHeader
len uintptr
elem *arrayElemCode
end *opcode
}
func newArrayHeaderCode(indent, alen int) *arrayHeaderCode {
return &arrayHeaderCode{
opcodeHeader: &opcodeHeader{
op: opArrayHead,
indent: indent,
},
len: uintptr(alen),
}
}
func (c *arrayHeaderCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
header := &arrayHeaderCode{}
code := (*opcode)(unsafe.Pointer(header))
codeMap[addr] = code
header.opcodeHeader = c.opcodeHeader.copy(codeMap)
header.len = c.len
header.elem = (*arrayElemCode)(unsafe.Pointer(c.elem.copy(codeMap)))
header.end = c.end.copy(codeMap)
return code
}
type arrayElemCode struct {
*opcodeHeader
idx uintptr
len uintptr
size uintptr
end *opcode
}
func (c *arrayElemCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
elem := &arrayElemCode{
idx: c.idx,
len: c.len,
size: c.size,
}
code := (*opcode)(unsafe.Pointer(elem))
codeMap[addr] = code
elem.opcodeHeader = c.opcodeHeader.copy(codeMap)
elem.end = c.end.copy(codeMap)
return code
}
type structFieldCode struct {
*opcodeHeader
key []byte
displayKey string
isTaggedKey bool
offset uintptr
anonymousKey bool
nextField *opcode
end *opcode
}
func linkPrevToNextField(prev, cur *structFieldCode) {
func linkPrevToNextField(prev, cur *opcode) {
prev.nextField = cur.nextField
code := prev.toOpcode()
fcode := cur.toOpcode()
code := prev
fcode := cur
for {
var nextCode *opcode
switch code.op.codeType() {
case codeArrayElem:
nextCode = code.toArrayElemCode().end
case codeSliceElem:
nextCode = code.toSliceElemCode().end
case codeMapKey:
nextCode = code.toMapKeyCode().end
case codeArrayElem, codeSliceElem, codeMapKey:
nextCode = code.end
default:
nextCode = code.next
}
@ -348,208 +267,133 @@ func linkPrevToNextField(prev, cur *structFieldCode) {
}
}
func (c *structFieldCode) toOpcode() *opcode {
return (*opcode)(unsafe.Pointer(c))
func newSliceHeaderCode(ctx *encodeCompileContext) *opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
return &opcode{
op: opSliceHead,
displayIdx: ctx.opcodeIndex,
idx: idx,
headIdx: idx,
elemIdx: elemIdx,
length: length,
indent: ctx.indent,
}
}
func (c *structFieldCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
func newSliceElemCode(ctx *encodeCompileContext, 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,
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
field := &structFieldCode{
key: c.key,
isTaggedKey: c.isTaggedKey,
displayKey: c.displayKey,
anonymousKey: c.anonymousKey,
offset: c.offset,
}
code := (*opcode)(unsafe.Pointer(field))
codeMap[addr] = code
field.opcodeHeader = c.opcodeHeader.copy(codeMap)
field.nextField = c.nextField.copy(codeMap)
field.end = c.end.copy(codeMap)
return code
}
type mapHeaderCode struct {
*opcodeHeader
key *mapKeyCode
value *mapValueCode
end *opcode
func newArrayHeaderCode(ctx *encodeCompileContext, alen int) *opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
return &opcode{
op: opArrayHead,
displayIdx: ctx.opcodeIndex,
idx: idx,
headIdx: idx,
elemIdx: elemIdx,
indent: ctx.indent,
length: uintptr(alen),
}
}
func (c *mapHeaderCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
func newArrayElemCode(ctx *encodeCompileContext, 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),
size: size,
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
header := &mapHeaderCode{}
code := (*opcode)(unsafe.Pointer(header))
codeMap[addr] = code
header.opcodeHeader = c.opcodeHeader.copy(codeMap)
header.key = (*mapKeyCode)(unsafe.Pointer(c.key.copy(codeMap)))
header.value = (*mapValueCode)(unsafe.Pointer(c.value.copy(codeMap)))
header.end = c.end.copy(codeMap)
return code
}
type mapKeyCode struct {
*opcodeHeader
idx int
len int
iter unsafe.Pointer
end *opcode
}
func (c *mapKeyCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
key := &mapKeyCode{
idx: c.idx,
len: c.len,
iter: c.iter,
}
code := (*opcode)(unsafe.Pointer(key))
codeMap[addr] = code
key.opcodeHeader = c.opcodeHeader.copy(codeMap)
key.end = c.end.copy(codeMap)
return code
}
func (c *mapKeyCode) set(len int, iter unsafe.Pointer) {
c.idx = 0
c.len = len
c.iter = iter
}
type mapValueCode struct {
*opcodeHeader
iter unsafe.Pointer
}
func (c *mapValueCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
value := &mapValueCode{
iter: c.iter,
}
code := (*opcode)(unsafe.Pointer(value))
codeMap[addr] = code
value.opcodeHeader = c.opcodeHeader.copy(codeMap)
return code
}
func (c *mapValueCode) set(iter unsafe.Pointer) {
c.iter = iter
}
func newMapHeaderCode(typ *rtype, withLoad bool, indent int) *mapHeaderCode {
func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
var op opType
if withLoad {
op = opMapHeadLoad
} else {
op = opMapHead
}
return &mapHeaderCode{
opcodeHeader: &opcodeHeader{
op: op,
typ: typ,
indent: indent,
},
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: op,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
idx: idx,
elemIdx: elemIdx,
length: length,
mapIter: mapIter,
indent: ctx.indent,
}
}
func newMapKeyCode(indent int) *mapKeyCode {
return &mapKeyCode{
opcodeHeader: &opcodeHeader{
op: opMapKey,
indent: indent,
},
func newMapKeyCode(ctx *encodeCompileContext, 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(indent int) *mapValueCode {
return &mapValueCode{
opcodeHeader: &opcodeHeader{
op: opMapValue,
indent: indent,
},
func newMapValueCode(ctx *encodeCompileContext, 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,
}
}
type interfaceCode struct {
*opcodeHeader
root bool
func newInterfaceCode(ctx *encodeCompileContext) *opcode {
return &opcode{
op: opInterface,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
indent: ctx.indent,
root: ctx.root,
next: newEndOp(ctx),
}
}
func (c *interfaceCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
func newRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
return &opcode{
op: opStructFieldRecursive,
typ: ctx.typ,
displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.ptrIndex),
indent: ctx.indent,
next: newEndOp(ctx),
jmp: jmp,
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
iface := &interfaceCode{}
code := (*opcode)(unsafe.Pointer(iface))
codeMap[addr] = code
iface.opcodeHeader = c.opcodeHeader.copy(codeMap)
return code
}
type recursiveCode struct {
*opcodeHeader
jmp *compiledCode
seenPtr uintptr
}
func (c *recursiveCode) copy(codeMap map[uintptr]*opcode) *opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
recur := &recursiveCode{seenPtr: c.seenPtr}
code := (*opcode)(unsafe.Pointer(recur))
codeMap[addr] = code
recur.opcodeHeader = c.opcodeHeader.copy(codeMap)
recur.jmp = &compiledCode{
code: c.jmp.code.copy(codeMap),
}
return code
}
func newRecursiveCode(recursive *recursiveCode) *opcode {
code := copyOpcode(recursive.jmp.code)
head := (*structFieldCode)(unsafe.Pointer(code))
head.end.next = newEndOp(0)
code.ptr = recursive.ptr
code.op = code.op.ptrHeadToHead()
return code
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -121,7 +121,6 @@ func TestIndent(t *testing.T) {
}
// Tests of a large random structure.
func TestCompactBig(t *testing.T) {
initBig()
var buf bytes.Buffer
@ -221,7 +220,9 @@ func trim(b []byte) []byte {
// Generate a random JSON object.
var jsonBig []byte
var (
jsonBig []byte
)
func initBig() {
if len(jsonBig) > 0 {