mirror of https://github.com/goccy/go-json.git
Fix encoding engine
This commit is contained in:
parent
72bc598dd4
commit
3aa921e884
|
@ -166,7 +166,7 @@ func (e *Encoder) encode(v interface{}) error {
|
|||
p := uintptr(header.ptr)
|
||||
ctx.init(p)
|
||||
seenPtr := map[uintptr]struct{}{}
|
||||
err := e.run(ctx, seenPtr, code)
|
||||
err := e.run(ctx, 0, seenPtr, code)
|
||||
if e.enabledIndent {
|
||||
codeSet.codeIndent.Put(code)
|
||||
} else {
|
||||
|
@ -228,7 +228,7 @@ func (e *Encoder) encode(v interface{}) error {
|
|||
}
|
||||
|
||||
seenPtr := map[uintptr]struct{}{}
|
||||
if err := e.run(ctx, seenPtr, c); err != nil {
|
||||
if err := e.run(ctx, 0, seenPtr, c); err != nil {
|
||||
codeSet.ctx.Put(ctx)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ func (e *Encoder) compileKey(ctx *encodeCompileContext) (*opcode, error) {
|
|||
|
||||
func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||
ptrOpcodeIndex := ctx.opcodeIndex
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
code, err := e.compile(ctx.withType(ctx.typ.Elem()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -142,7 +142,7 @@ func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
|
|||
if code.op != ptrHeadOp {
|
||||
code.op = ptrHeadOp
|
||||
code.decOpcodeIndex()
|
||||
ctx.decOpcodeIndex()
|
||||
ctx.decIndex()
|
||||
return code, nil
|
||||
}
|
||||
c := ctx.context()
|
||||
|
@ -152,121 +152,121 @@ func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
|
|||
|
||||
func (e *Encoder) compileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opMarshalJSON)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileMarshalJSONPtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalJSON)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opMarshalText)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileMarshalTextPtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalText)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileInt(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opInt)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileInt8(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opInt8)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileInt16(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opInt16)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileInt32(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opInt32)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileInt64(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opInt64)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileUint(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opUint)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileUint8(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opUint8)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileUint16(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opUint16)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileUint32(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opUint32)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileUint64(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opUint64)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileFloat32(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opFloat32)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileFloat64(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opFloat64)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileString(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opString)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileBool(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opBool)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileBytes(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newOpCode(ctx, opBytes)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileInterface(ctx *encodeCompileContext) (*opcode, error) {
|
||||
code := newInterfaceCode(ctx)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code, nil
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
|
|||
size := elem.Size()
|
||||
|
||||
header := newSliceHeaderCode(ctx)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
|
||||
code, err := e.compile(ctx.withType(ctx.typ.Elem()).incIndent())
|
||||
if err != nil {
|
||||
|
@ -287,11 +287,11 @@ func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
|
|||
// ^ |
|
||||
// |________|
|
||||
|
||||
elemCode := newSliceElemCode(ctx, size)
|
||||
ctx.incOpcodeIndex()
|
||||
elemCode := newSliceElemCode(ctx, header, size)
|
||||
ctx.incIndex()
|
||||
|
||||
end := newOpCode(ctx, opSliceEnd)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
if ctx.withIndent {
|
||||
if ctx.root {
|
||||
header.op = opRootSliceHeadIndent
|
||||
|
@ -320,7 +320,7 @@ func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
|
|||
size := elem.Size()
|
||||
|
||||
header := newArrayHeaderCode(ctx, alen)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
|
||||
code, err := e.compile(ctx.withType(elem).incIndent())
|
||||
if err != nil {
|
||||
|
@ -330,11 +330,11 @@ func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
|
|||
// ^ |
|
||||
// |________|
|
||||
|
||||
elemCode := newArrayElemCode(ctx, alen, size)
|
||||
ctx.incOpcodeIndex()
|
||||
elemCode := newArrayElemCode(ctx, header, alen, size)
|
||||
ctx.incIndex()
|
||||
|
||||
end := newOpCode(ctx, opArrayEnd)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
|
||||
if ctx.withIndent {
|
||||
header.op = opArrayHeadIndent
|
||||
|
@ -373,7 +373,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
|
|||
// |_______________________|
|
||||
ctx = ctx.incIndent()
|
||||
header := newMapHeaderCode(ctx, withLoad)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
|
||||
typ := ctx.typ
|
||||
keyType := ctx.typ.Key()
|
||||
|
@ -382,8 +382,8 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
value := newMapValueCode(ctx)
|
||||
ctx.incOpcodeIndex()
|
||||
value := newMapValueCode(ctx, header)
|
||||
ctx.incIndex()
|
||||
|
||||
valueType := typ.Elem()
|
||||
valueCode, err := e.compile(ctx.withType(valueType))
|
||||
|
@ -391,15 +391,15 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
key := newMapKeyCode(ctx)
|
||||
ctx.incOpcodeIndex()
|
||||
key := newMapKeyCode(ctx, header)
|
||||
ctx.incIndex()
|
||||
|
||||
ctx = ctx.decIndent()
|
||||
|
||||
header.mapKey = key
|
||||
header.mapValue = value
|
||||
end := newOpCode(ctx, opMapEnd)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
|
||||
if ctx.withIndent {
|
||||
if header.op == opMapHead {
|
||||
|
@ -578,7 +578,7 @@ func (e *Encoder) optimizeStructField(op opType, tag *structTag, withIndent bool
|
|||
|
||||
func (e *Encoder) recursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
|
||||
code := newRecursiveCode(ctx, jmp)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
return code
|
||||
}
|
||||
|
||||
|
@ -667,7 +667,7 @@ func (e *Encoder) structField(ctx *encodeCompileContext, fieldCode *opcode, valu
|
|||
opStructFieldStringTagIndent:
|
||||
return valueCode.beforeLastCode()
|
||||
}
|
||||
ctx.decOpcodeIndex()
|
||||
ctx.decIndex()
|
||||
return code
|
||||
}
|
||||
|
||||
|
@ -876,7 +876,8 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
}
|
||||
}
|
||||
fieldOpcodeIndex := ctx.opcodeIndex
|
||||
ctx.incOpcodeIndex()
|
||||
fieldPtrIndex := ctx.ptrIndex
|
||||
ctx.incIndex()
|
||||
valueCode, err := e.compile(ctx.withType(fieldType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -902,7 +903,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
fieldCode := &opcode{
|
||||
typ: valueCode.typ,
|
||||
displayIdx: fieldOpcodeIndex,
|
||||
idx: opcodeOffset(fieldOpcodeIndex),
|
||||
idx: opcodeOffset(fieldPtrIndex),
|
||||
next: valueCode,
|
||||
indent: ctx.indent,
|
||||
anonymousKey: field.Anonymous,
|
||||
|
@ -912,14 +913,15 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
offset: field.Offset,
|
||||
}
|
||||
if fieldIdx == 0 {
|
||||
fieldCode.headIdx = fieldCode.idx
|
||||
code = e.structHeader(ctx, fieldCode, valueCode, tag)
|
||||
head = fieldCode
|
||||
prevField = fieldCode
|
||||
} else {
|
||||
fcode := (*opcode)(unsafe.Pointer(fieldCode))
|
||||
code.next = fcode
|
||||
fieldCode.headIdx = head.headIdx
|
||||
code.next = fieldCode
|
||||
code = e.structField(ctx, fieldCode, valueCode, tag)
|
||||
prevField.nextField = fcode
|
||||
prevField.nextField = fieldCode
|
||||
prevField = fieldCode
|
||||
}
|
||||
fieldIdx++
|
||||
|
@ -939,11 +941,12 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
op: opStructFieldHead,
|
||||
typ: typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
headIdx: opcodeOffset(ctx.ptrIndex),
|
||||
indent: ctx.indent,
|
||||
nextField: structEndCode,
|
||||
}
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
if ctx.withIndent {
|
||||
head.op = opStructFieldHeadIndent
|
||||
}
|
||||
|
@ -952,7 +955,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
|
||||
structEndCode.displayIdx = ctx.opcodeIndex
|
||||
structEndCode.idx = opcodeOffset(ctx.opcodeIndex)
|
||||
ctx.incOpcodeIndex()
|
||||
ctx.incIndex()
|
||||
|
||||
if ctx.withIndent {
|
||||
structEndCode.op = opStructEndIndent
|
||||
|
|
|
@ -46,9 +46,18 @@ func (c *encodeCompileContext) decIndent() *encodeCompileContext {
|
|||
return ctx
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) incIndex() {
|
||||
c.incOpcodeIndex()
|
||||
c.incPtrIndex()
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) decIndex() {
|
||||
c.decOpcodeIndex()
|
||||
c.decPtrIndex()
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) incOpcodeIndex() {
|
||||
c.opcodeIndex++
|
||||
c.ptrIndex++
|
||||
if c.parent != nil {
|
||||
c.parent.incOpcodeIndex()
|
||||
}
|
||||
|
@ -56,12 +65,25 @@ func (c *encodeCompileContext) incOpcodeIndex() {
|
|||
|
||||
func (c *encodeCompileContext) decOpcodeIndex() {
|
||||
c.opcodeIndex--
|
||||
c.ptrIndex--
|
||||
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
|
||||
}
|
||||
|
|
153
encode_opcode.go
153
encode_opcode.go
|
@ -50,7 +50,7 @@ func newOpCodeWithNext(ctx *encodeCompileContext, op opType, next *opcode) *opco
|
|||
typ: ctx.typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
indent: ctx.indent,
|
||||
idx: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func (c *opcode) totalLength() int {
|
|||
func (c *opcode) decOpcodeIndex() {
|
||||
for code := c; code.op != opEnd; {
|
||||
code.displayIdx--
|
||||
code.idx -= 8
|
||||
code.idx -= uintptrSize
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem, codeSliceElem, codeMapKey:
|
||||
code = code.end
|
||||
|
@ -104,40 +104,93 @@ func (c *opcode) decOpcodeIndex() {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *opcode) dumpElem(code *opcode) string {
|
||||
func (c *opcode) dumpHead(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 ([headIdx:%d][elemIdx:%d][length:%d][size:%d])`,
|
||||
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.headIdx,
|
||||
code.elemIdx,
|
||||
code.length,
|
||||
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 ([key:%s][offset:%d][headIdx:%d])`,
|
||||
`[%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,
|
||||
code.headIdx/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *opcode) dumpKey(code *opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([elemIdx:%d][length:%d][mapIter:%d])`,
|
||||
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.elemIdx,
|
||||
code.length,
|
||||
code.mapIter,
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -145,21 +198,31 @@ 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 codeStructField:
|
||||
codes = append(codes, c.dumpField(code))
|
||||
code = code.next
|
||||
default:
|
||||
codes = append(codes, fmt.Sprintf(
|
||||
"[%d]%s%s",
|
||||
"[%d]%s%s ([idx:%d])",
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
))
|
||||
code = code.next
|
||||
}
|
||||
|
@ -190,40 +253,58 @@ func linkPrevToNextField(prev, cur *opcode) {
|
|||
}
|
||||
|
||||
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: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: idx,
|
||||
headIdx: idx,
|
||||
elemIdx: elemIdx,
|
||||
length: length,
|
||||
indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newSliceElemCode(ctx *encodeCompileContext, size uintptr) *opcode {
|
||||
func newSliceElemCode(ctx *encodeCompileContext, head *opcode, size uintptr) *opcode {
|
||||
return &opcode{
|
||||
op: opSliceElem,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
headIdx: head.idx,
|
||||
elemIdx: head.elemIdx,
|
||||
length: head.length,
|
||||
indent: ctx.indent,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
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: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: idx,
|
||||
headIdx: idx,
|
||||
elemIdx: elemIdx,
|
||||
indent: ctx.indent,
|
||||
length: uintptr(alen),
|
||||
}
|
||||
}
|
||||
|
||||
func newArrayElemCode(ctx *encodeCompileContext, alen int, size uintptr) *opcode {
|
||||
func newArrayElemCode(ctx *encodeCompileContext, head *opcode, length int, size uintptr) *opcode {
|
||||
return &opcode{
|
||||
op: opArrayElem,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.opcodeIndex),
|
||||
length: uintptr(alen),
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
elemIdx: head.elemIdx,
|
||||
headIdx: head.headIdx,
|
||||
length: uintptr(length),
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
@ -235,29 +316,45 @@ func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
|
|||
} else {
|
||||
op = opMapHead
|
||||
}
|
||||
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: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: idx,
|
||||
elemIdx: elemIdx,
|
||||
length: length,
|
||||
mapIter: mapIter,
|
||||
indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapKeyCode(ctx *encodeCompileContext) *opcode {
|
||||
func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||
return &opcode{
|
||||
op: opMapKey,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
elemIdx: head.elemIdx,
|
||||
length: head.length,
|
||||
mapIter: head.mapIter,
|
||||
indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapValueCode(ctx *encodeCompileContext) *opcode {
|
||||
func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||
return &opcode{
|
||||
op: opMapValue,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
elemIdx: head.elemIdx,
|
||||
length: head.length,
|
||||
mapIter: head.mapIter,
|
||||
indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
@ -267,7 +364,7 @@ func newInterfaceCode(ctx *encodeCompileContext) *opcode {
|
|||
op: opInterface,
|
||||
typ: ctx.typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
indent: ctx.indent,
|
||||
next: newEndOp(ctx),
|
||||
root: ctx.root,
|
||||
|
@ -279,7 +376,7 @@ func newRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
|
|||
op: opStructFieldRecursive,
|
||||
typ: ctx.typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.opcodeIndex),
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
indent: ctx.indent,
|
||||
next: newEndOp(ctx),
|
||||
jmp: jmp,
|
||||
|
|
96
encode_vm.go
96
encode_vm.go
|
@ -11,6 +11,8 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
const startDetectingCyclesAfter = 1000
|
||||
|
||||
func load(base uintptr, idx uintptr) uintptr {
|
||||
return *(*uintptr)(unsafe.Pointer(base + idx))
|
||||
}
|
||||
|
@ -19,7 +21,7 @@ func store(base uintptr, idx uintptr, p uintptr) {
|
|||
*(*uintptr)(unsafe.Pointer(base + idx)) = p
|
||||
}
|
||||
|
||||
func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, code *opcode) error {
|
||||
func (e *Encoder) run(ctx *encodeRuntimeContext, recursiveLevel int, seenPtr map[uintptr]struct{}, code *opcode) error {
|
||||
ctxptr := ctx.ptr()
|
||||
for {
|
||||
switch code.op {
|
||||
|
@ -144,7 +146,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
ptrs: make([]uintptr, c.totalLength()),
|
||||
}
|
||||
ctx.init(uintptr(header.ptr))
|
||||
if err := e.run(ctx, seenPtr, c); err != nil {
|
||||
if err := e.run(ctx, recursiveLevel+1, seenPtr, c); err != nil {
|
||||
return err
|
||||
}
|
||||
code = code.next
|
||||
|
@ -236,9 +238,10 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
if idx < length {
|
||||
e.encodeByte(',')
|
||||
store(ctxptr, code.elemIdx, idx)
|
||||
code = code.next
|
||||
data := load(ctxptr, code.headIdx)
|
||||
store(ctxptr, code.idx, data+idx*code.size)
|
||||
size := code.size
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, data+idx*size)
|
||||
} else {
|
||||
e.encodeByte(']')
|
||||
code = code.end.next
|
||||
|
@ -295,9 +298,10 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
e.encodeBytes([]byte{',', '\n'})
|
||||
e.encodeIndent(code.indent + 1)
|
||||
store(ctxptr, code.elemIdx, idx)
|
||||
code = code.next
|
||||
data := load(ctxptr, code.headIdx)
|
||||
store(ctxptr, code.idx, data+idx*code.size)
|
||||
size := code.size
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, data+idx*size)
|
||||
} else {
|
||||
e.encodeByte('\n')
|
||||
e.encodeIndent(code.indent)
|
||||
|
@ -329,6 +333,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
} else {
|
||||
e.encodeByte('[')
|
||||
if code.length > 0 {
|
||||
store(ctxptr, code.elemIdx, 0)
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, p)
|
||||
} else {
|
||||
|
@ -343,8 +348,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
e.encodeByte(',')
|
||||
store(ctxptr, code.elemIdx, idx)
|
||||
p := load(ctxptr, code.headIdx)
|
||||
size := code.size
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, p+idx*code.size)
|
||||
store(ctxptr, code.idx, p+idx*size)
|
||||
} else {
|
||||
e.encodeByte(']')
|
||||
code = code.end.next
|
||||
|
@ -359,6 +365,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
e.encodeBytes([]byte{'[', '\n'})
|
||||
if code.length > 0 {
|
||||
e.encodeIndent(code.indent + 1)
|
||||
store(ctxptr, code.elemIdx, 0)
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, p)
|
||||
} else {
|
||||
|
@ -375,8 +382,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
e.encodeIndent(code.indent + 1)
|
||||
store(ctxptr, code.elemIdx, idx)
|
||||
p := load(ctxptr, code.headIdx)
|
||||
size := code.size
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, p+idx*code.size)
|
||||
store(ctxptr, code.idx, p+idx*size)
|
||||
} else {
|
||||
e.encodeByte('\n')
|
||||
e.encodeIndent(code.indent)
|
||||
|
@ -393,9 +401,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
mlen := maplen(unsafe.Pointer(ptr))
|
||||
if mlen > 0 {
|
||||
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||
store(ctxptr, code.mapKey.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapKey.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.mapValue.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.elemIdx, 0)
|
||||
store(ctxptr, code.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapIter, uintptr(iter))
|
||||
key := mapiterkey(iter)
|
||||
store(ctxptr, code.next.idx, uintptr(key))
|
||||
code = code.next
|
||||
|
@ -416,9 +424,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
mlen := maplen(unsafe.Pointer(ptr))
|
||||
if mlen > 0 {
|
||||
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||
store(ctxptr, code.mapKey.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapKey.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.mapValue.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.elemIdx, 0)
|
||||
store(ctxptr, code.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapIter, uintptr(iter))
|
||||
key := mapiterkey(iter)
|
||||
store(ctxptr, code.next.idx, uintptr(key))
|
||||
code = code.next
|
||||
|
@ -460,9 +468,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
if mlen > 0 {
|
||||
e.encodeBytes([]byte{'{', '\n'})
|
||||
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||
store(ctxptr, code.mapKey.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapKey.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.mapValue.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.elemIdx, 0)
|
||||
store(ctxptr, code.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapIter, uintptr(iter))
|
||||
key := mapiterkey(iter)
|
||||
store(ctxptr, code.next.idx, uintptr(key))
|
||||
code = code.next
|
||||
|
@ -486,9 +494,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
if mlen > 0 {
|
||||
e.encodeBytes([]byte{'{', '\n'})
|
||||
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||
store(ctxptr, code.mapKey.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapKey.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.mapValue.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.elemIdx, 0)
|
||||
store(ctxptr, code.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapIter, uintptr(iter))
|
||||
key := mapiterkey(iter)
|
||||
store(ctxptr, code.next.idx, uintptr(key))
|
||||
code = code.next
|
||||
|
@ -510,9 +518,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
if mlen > 0 {
|
||||
e.encodeBytes([]byte{'{', '\n'})
|
||||
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||
store(ctxptr, code.mapKey.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapKey.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.mapValue.mapIter, uintptr(iter))
|
||||
store(ctxptr, code.elemIdx, 0)
|
||||
store(ctxptr, code.length, uintptr(mlen))
|
||||
store(ctxptr, code.mapIter, uintptr(iter))
|
||||
key := mapiterkey(iter)
|
||||
store(ctxptr, code.next.idx, uintptr(key))
|
||||
code = code.next
|
||||
|
@ -569,17 +577,20 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
case opStructFieldRecursive:
|
||||
ptr := load(ctxptr, code.idx)
|
||||
if ptr != 0 {
|
||||
if _, exists := seenPtr[ptr]; exists {
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: fmt.Sprintf("encountered a cycle via %s", code.typ),
|
||||
if recursiveLevel > startDetectingCyclesAfter {
|
||||
if _, exists := seenPtr[ptr]; exists {
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
return &UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: fmt.Sprintf("encountered a cycle via %s", code.typ),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
seenPtr[ptr] = struct{}{}
|
||||
c := *(code.jmp.code)
|
||||
ctx := &encodeRuntimeContext{
|
||||
ptrs: make([]uintptr, c.totalLength()),
|
||||
|
@ -587,7 +598,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
ctx.init(ptr)
|
||||
c.end.next = newEndOp(&encodeCompileContext{})
|
||||
c.op = c.op.ptrHeadToHead()
|
||||
if err := e.run(ctx, seenPtr, &c); err != nil {
|
||||
if err := e.run(ctx, recursiveLevel+1, seenPtr, &c); err != nil {
|
||||
return err
|
||||
}
|
||||
code = code.next
|
||||
|
@ -611,8 +622,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
if !code.anonymousKey {
|
||||
e.encodeBytes(code.key)
|
||||
}
|
||||
p := ptr + code.offset
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, ptr+code.offset)
|
||||
store(ctxptr, code.idx, p)
|
||||
}
|
||||
case opStructFieldAnonymousHead:
|
||||
ptr := load(ctxptr, code.idx)
|
||||
|
@ -4063,40 +4075,45 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
}
|
||||
e.encodeBytes(code.key)
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
p := ptr + code.offset
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, ptr+code.offset)
|
||||
store(ctxptr, code.idx, p)
|
||||
case opStructFieldSlice:
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(code.key)
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
p := ptr + code.offset
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, ptr+code.offset)
|
||||
store(ctxptr, code.idx, p)
|
||||
case opStructFieldMap:
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(code.key)
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
p := ptr + code.offset
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, ptr+code.offset)
|
||||
store(ctxptr, code.idx, p)
|
||||
case opStructFieldMapLoad:
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(code.key)
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
p := ptr + code.offset
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, ptr+code.offset)
|
||||
store(ctxptr, code.idx, p)
|
||||
case opStructFieldStruct:
|
||||
if e.buf[len(e.buf)-1] != '{' {
|
||||
e.encodeByte(',')
|
||||
}
|
||||
e.encodeBytes(code.key)
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
p := ptr + code.offset
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, ptr+code.offset)
|
||||
store(ctxptr, code.idx, p)
|
||||
case opStructFieldIndent:
|
||||
if e.buf[len(e.buf)-2] != '{' || e.buf[len(e.buf)-1] == '}' {
|
||||
e.encodeBytes([]byte{',', '\n'})
|
||||
|
@ -4105,8 +4122,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
|
|||
e.encodeBytes(code.key)
|
||||
e.encodeByte(' ')
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
p := ptr + code.offset
|
||||
code = code.next
|
||||
store(ctxptr, code.idx, ptr+code.offset)
|
||||
store(ctxptr, code.idx, p)
|
||||
case opStructFieldIntIndent:
|
||||
if e.buf[len(e.buf)-2] != '{' || e.buf[len(e.buf)-1] == '}' {
|
||||
e.encodeBytes([]byte{',', '\n'})
|
||||
|
|
10
json_test.go
10
json_test.go
|
@ -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,10 @@ func trim(b []byte) []byte {
|
|||
|
||||
// Generate a random JSON object.
|
||||
|
||||
var jsonBig []byte
|
||||
var (
|
||||
jsonBig []byte
|
||||
jsonVal interface{}
|
||||
)
|
||||
|
||||
func initBig() {
|
||||
if len(jsonBig) > 0 {
|
||||
|
@ -231,8 +233,8 @@ func initBig() {
|
|||
if testing.Short() {
|
||||
n = 100
|
||||
}
|
||||
v := genValue(n)
|
||||
b, err := json.Marshal(v)
|
||||
jsonVal = genValue(n)
|
||||
b, err := json.Marshal(jsonVal)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue