Fix encoding engine

This commit is contained in:
Masaaki Goshima 2020-09-01 22:26:26 +09:00
parent 72bc598dd4
commit 3aa921e884
6 changed files with 263 additions and 121 deletions

View File

@ -166,7 +166,7 @@ func (e *Encoder) encode(v interface{}) error {
p := uintptr(header.ptr) p := uintptr(header.ptr)
ctx.init(p) ctx.init(p)
seenPtr := map[uintptr]struct{}{} seenPtr := map[uintptr]struct{}{}
err := e.run(ctx, seenPtr, code) err := e.run(ctx, 0, seenPtr, code)
if e.enabledIndent { if e.enabledIndent {
codeSet.codeIndent.Put(code) codeSet.codeIndent.Put(code)
} else { } else {
@ -228,7 +228,7 @@ func (e *Encoder) encode(v interface{}) error {
} }
seenPtr := map[uintptr]struct{}{} 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) codeSet.ctx.Put(ctx)
return err return err
} }

View File

@ -133,7 +133,7 @@ func (e *Encoder) compileKey(ctx *encodeCompileContext) (*opcode, error) {
func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
ptrOpcodeIndex := ctx.opcodeIndex ptrOpcodeIndex := ctx.opcodeIndex
ctx.incOpcodeIndex() ctx.incIndex()
code, err := e.compile(ctx.withType(ctx.typ.Elem())) code, err := e.compile(ctx.withType(ctx.typ.Elem()))
if err != nil { if err != nil {
return nil, err return nil, err
@ -142,7 +142,7 @@ func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
if code.op != ptrHeadOp { if code.op != ptrHeadOp {
code.op = ptrHeadOp code.op = ptrHeadOp
code.decOpcodeIndex() code.decOpcodeIndex()
ctx.decOpcodeIndex() ctx.decIndex()
return code, nil return code, nil
} }
c := ctx.context() c := ctx.context()
@ -152,121 +152,121 @@ func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
func (e *Encoder) compileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opMarshalJSON) code := newOpCode(ctx, opMarshalJSON)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileMarshalJSONPtr(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileMarshalJSONPtr(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalJSON) code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalJSON)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileMarshalText(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opMarshalText) code := newOpCode(ctx, opMarshalText)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileMarshalTextPtr(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileMarshalTextPtr(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalText) code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalText)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileInt(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileInt(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt) code := newOpCode(ctx, opInt)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileInt8(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileInt8(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt8) code := newOpCode(ctx, opInt8)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileInt16(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileInt16(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt16) code := newOpCode(ctx, opInt16)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileInt32(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileInt32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt32) code := newOpCode(ctx, opInt32)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileInt64(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileInt64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt64) code := newOpCode(ctx, opInt64)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileUint(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileUint(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint) code := newOpCode(ctx, opUint)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileUint8(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileUint8(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint8) code := newOpCode(ctx, opUint8)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileUint16(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileUint16(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint16) code := newOpCode(ctx, opUint16)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileUint32(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileUint32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint32) code := newOpCode(ctx, opUint32)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileUint64(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileUint64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint64) code := newOpCode(ctx, opUint64)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileFloat32(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileFloat32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opFloat32) code := newOpCode(ctx, opFloat32)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileFloat64(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileFloat64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opFloat64) code := newOpCode(ctx, opFloat64)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileString(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileString(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opString) code := newOpCode(ctx, opString)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileBool(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileBool(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opBool) code := newOpCode(ctx, opBool)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileBytes(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileBytes(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opBytes) code := newOpCode(ctx, opBytes)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
func (e *Encoder) compileInterface(ctx *encodeCompileContext) (*opcode, error) { func (e *Encoder) compileInterface(ctx *encodeCompileContext) (*opcode, error) {
code := newInterfaceCode(ctx) code := newInterfaceCode(ctx)
ctx.incOpcodeIndex() ctx.incIndex()
return code, nil return code, nil
} }
@ -276,7 +276,7 @@ func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
size := elem.Size() size := elem.Size()
header := newSliceHeaderCode(ctx) header := newSliceHeaderCode(ctx)
ctx.incOpcodeIndex() ctx.incIndex()
code, err := e.compile(ctx.withType(ctx.typ.Elem()).incIndent()) code, err := e.compile(ctx.withType(ctx.typ.Elem()).incIndent())
if err != nil { if err != nil {
@ -287,11 +287,11 @@ func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
// ^ | // ^ |
// |________| // |________|
elemCode := newSliceElemCode(ctx, size) elemCode := newSliceElemCode(ctx, header, size)
ctx.incOpcodeIndex() ctx.incIndex()
end := newOpCode(ctx, opSliceEnd) end := newOpCode(ctx, opSliceEnd)
ctx.incOpcodeIndex() ctx.incIndex()
if ctx.withIndent { if ctx.withIndent {
if ctx.root { if ctx.root {
header.op = opRootSliceHeadIndent header.op = opRootSliceHeadIndent
@ -320,7 +320,7 @@ func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
size := elem.Size() size := elem.Size()
header := newArrayHeaderCode(ctx, alen) header := newArrayHeaderCode(ctx, alen)
ctx.incOpcodeIndex() ctx.incIndex()
code, err := e.compile(ctx.withType(elem).incIndent()) code, err := e.compile(ctx.withType(elem).incIndent())
if err != nil { if err != nil {
@ -330,11 +330,11 @@ func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
// ^ | // ^ |
// |________| // |________|
elemCode := newArrayElemCode(ctx, alen, size) elemCode := newArrayElemCode(ctx, header, alen, size)
ctx.incOpcodeIndex() ctx.incIndex()
end := newOpCode(ctx, opArrayEnd) end := newOpCode(ctx, opArrayEnd)
ctx.incOpcodeIndex() ctx.incIndex()
if ctx.withIndent { if ctx.withIndent {
header.op = opArrayHeadIndent header.op = opArrayHeadIndent
@ -373,7 +373,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
// |_______________________| // |_______________________|
ctx = ctx.incIndent() ctx = ctx.incIndent()
header := newMapHeaderCode(ctx, withLoad) header := newMapHeaderCode(ctx, withLoad)
ctx.incOpcodeIndex() ctx.incIndex()
typ := ctx.typ typ := ctx.typ
keyType := ctx.typ.Key() keyType := ctx.typ.Key()
@ -382,8 +382,8 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
return nil, err return nil, err
} }
value := newMapValueCode(ctx) value := newMapValueCode(ctx, header)
ctx.incOpcodeIndex() ctx.incIndex()
valueType := typ.Elem() valueType := typ.Elem()
valueCode, err := e.compile(ctx.withType(valueType)) valueCode, err := e.compile(ctx.withType(valueType))
@ -391,15 +391,15 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
return nil, err return nil, err
} }
key := newMapKeyCode(ctx) key := newMapKeyCode(ctx, header)
ctx.incOpcodeIndex() ctx.incIndex()
ctx = ctx.decIndent() ctx = ctx.decIndent()
header.mapKey = key header.mapKey = key
header.mapValue = value header.mapValue = value
end := newOpCode(ctx, opMapEnd) end := newOpCode(ctx, opMapEnd)
ctx.incOpcodeIndex() ctx.incIndex()
if ctx.withIndent { if ctx.withIndent {
if header.op == opMapHead { 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 { func (e *Encoder) recursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
code := newRecursiveCode(ctx, jmp) code := newRecursiveCode(ctx, jmp)
ctx.incOpcodeIndex() ctx.incIndex()
return code return code
} }
@ -667,7 +667,7 @@ func (e *Encoder) structField(ctx *encodeCompileContext, fieldCode *opcode, valu
opStructFieldStringTagIndent: opStructFieldStringTagIndent:
return valueCode.beforeLastCode() return valueCode.beforeLastCode()
} }
ctx.decOpcodeIndex() ctx.decIndex()
return code return code
} }
@ -876,7 +876,8 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
} }
} }
fieldOpcodeIndex := ctx.opcodeIndex fieldOpcodeIndex := ctx.opcodeIndex
ctx.incOpcodeIndex() fieldPtrIndex := ctx.ptrIndex
ctx.incIndex()
valueCode, err := e.compile(ctx.withType(fieldType)) valueCode, err := e.compile(ctx.withType(fieldType))
if err != nil { if err != nil {
return nil, err return nil, err
@ -902,7 +903,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
fieldCode := &opcode{ fieldCode := &opcode{
typ: valueCode.typ, typ: valueCode.typ,
displayIdx: fieldOpcodeIndex, displayIdx: fieldOpcodeIndex,
idx: opcodeOffset(fieldOpcodeIndex), idx: opcodeOffset(fieldPtrIndex),
next: valueCode, next: valueCode,
indent: ctx.indent, indent: ctx.indent,
anonymousKey: field.Anonymous, anonymousKey: field.Anonymous,
@ -912,14 +913,15 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
offset: field.Offset, offset: field.Offset,
} }
if fieldIdx == 0 { if fieldIdx == 0 {
fieldCode.headIdx = fieldCode.idx
code = e.structHeader(ctx, fieldCode, valueCode, tag) code = e.structHeader(ctx, fieldCode, valueCode, tag)
head = fieldCode head = fieldCode
prevField = fieldCode prevField = fieldCode
} else { } else {
fcode := (*opcode)(unsafe.Pointer(fieldCode)) fieldCode.headIdx = head.headIdx
code.next = fcode code.next = fieldCode
code = e.structField(ctx, fieldCode, valueCode, tag) code = e.structField(ctx, fieldCode, valueCode, tag)
prevField.nextField = fcode prevField.nextField = fieldCode
prevField = fieldCode prevField = fieldCode
} }
fieldIdx++ fieldIdx++
@ -939,11 +941,12 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
op: opStructFieldHead, op: opStructFieldHead,
typ: typ, typ: typ,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: opcodeOffset(ctx.ptrIndex),
headIdx: opcodeOffset(ctx.ptrIndex),
indent: ctx.indent, indent: ctx.indent,
nextField: structEndCode, nextField: structEndCode,
} }
ctx.incOpcodeIndex() ctx.incIndex()
if ctx.withIndent { if ctx.withIndent {
head.op = opStructFieldHeadIndent head.op = opStructFieldHeadIndent
} }
@ -952,7 +955,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
structEndCode.displayIdx = ctx.opcodeIndex structEndCode.displayIdx = ctx.opcodeIndex
structEndCode.idx = opcodeOffset(ctx.opcodeIndex) structEndCode.idx = opcodeOffset(ctx.opcodeIndex)
ctx.incOpcodeIndex() ctx.incIndex()
if ctx.withIndent { if ctx.withIndent {
structEndCode.op = opStructEndIndent structEndCode.op = opStructEndIndent

View File

@ -46,9 +46,18 @@ func (c *encodeCompileContext) decIndent() *encodeCompileContext {
return ctx return ctx
} }
func (c *encodeCompileContext) incIndex() {
c.incOpcodeIndex()
c.incPtrIndex()
}
func (c *encodeCompileContext) decIndex() {
c.decOpcodeIndex()
c.decPtrIndex()
}
func (c *encodeCompileContext) incOpcodeIndex() { func (c *encodeCompileContext) incOpcodeIndex() {
c.opcodeIndex++ c.opcodeIndex++
c.ptrIndex++
if c.parent != nil { if c.parent != nil {
c.parent.incOpcodeIndex() c.parent.incOpcodeIndex()
} }
@ -56,12 +65,25 @@ func (c *encodeCompileContext) incOpcodeIndex() {
func (c *encodeCompileContext) decOpcodeIndex() { func (c *encodeCompileContext) decOpcodeIndex() {
c.opcodeIndex-- c.opcodeIndex--
c.ptrIndex--
if c.parent != nil { if c.parent != nil {
c.parent.decOpcodeIndex() 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 { type encodeRuntimeContext struct {
ptrs []uintptr ptrs []uintptr
} }

View File

@ -50,7 +50,7 @@ func newOpCodeWithNext(ctx *encodeCompileContext, op opType, next *opcode) *opco
typ: ctx.typ, typ: ctx.typ,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
indent: ctx.indent, indent: ctx.indent,
idx: opcodeOffset(ctx.opcodeIndex), idx: opcodeOffset(ctx.ptrIndex),
next: next, next: next,
} }
} }
@ -94,7 +94,7 @@ func (c *opcode) totalLength() int {
func (c *opcode) decOpcodeIndex() { func (c *opcode) decOpcodeIndex() {
for code := c; code.op != opEnd; { for code := c; code.op != opEnd; {
code.displayIdx-- code.displayIdx--
code.idx -= 8 code.idx -= uintptrSize
switch code.op.codeType() { switch code.op.codeType() {
case codeArrayElem, codeSliceElem, codeMapKey: case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end 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( 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, code.displayIdx,
strings.Repeat("-", code.indent), strings.Repeat("-", code.indent),
code.op, code.op,
code.headIdx, code.idx/uintptrSize,
code.elemIdx, code.headIdx/uintptrSize,
code.length, 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, code.size,
) )
} }
func (c *opcode) dumpField(code *opcode) string { func (c *opcode) dumpField(code *opcode) string {
return fmt.Sprintf( 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, code.displayIdx,
strings.Repeat("-", code.indent), strings.Repeat("-", code.indent),
code.op, code.op,
code.idx/uintptrSize,
code.displayKey, code.displayKey,
code.offset, code.offset,
code.headIdx, code.headIdx/uintptrSize,
) )
} }
func (c *opcode) dumpKey(code *opcode) string { func (c *opcode) dumpKey(code *opcode) string {
return fmt.Sprintf( 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, code.displayIdx,
strings.Repeat("-", code.indent), strings.Repeat("-", code.indent),
code.op, code.op,
code.elemIdx, code.idx/uintptrSize,
code.length, code.elemIdx/uintptrSize,
code.mapIter, 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{} codes := []string{}
for code := c; code.op != opEnd; { for code := c; code.op != opEnd; {
switch code.op.codeType() { 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: case codeArrayElem, codeSliceElem:
codes = append(codes, c.dumpElem(code)) codes = append(codes, c.dumpElem(code))
code = code.end code = code.end
case codeMapKey: case codeMapKey:
codes = append(codes, c.dumpKey(code)) codes = append(codes, c.dumpKey(code))
code = code.end code = code.end
case codeMapValue:
codes = append(codes, c.dumpValue(code))
code = code.next
case codeStructField: case codeStructField:
codes = append(codes, c.dumpField(code)) codes = append(codes, c.dumpField(code))
code = code.next code = code.next
default: default:
codes = append(codes, fmt.Sprintf( codes = append(codes, fmt.Sprintf(
"[%d]%s%s", "[%d]%s%s ([idx:%d])",
code.displayIdx, code.displayIdx,
strings.Repeat("-", code.indent), strings.Repeat("-", code.indent),
code.op, code.op,
code.idx/uintptrSize,
)) ))
code = code.next code = code.next
} }
@ -190,40 +253,58 @@ func linkPrevToNextField(prev, cur *opcode) {
} }
func newSliceHeaderCode(ctx *encodeCompileContext) *opcode { func newSliceHeaderCode(ctx *encodeCompileContext) *opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
return &opcode{ return &opcode{
op: opSliceHead, op: opSliceHead,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: idx,
headIdx: idx,
elemIdx: elemIdx,
length: length,
indent: ctx.indent, indent: ctx.indent,
} }
} }
func newSliceElemCode(ctx *encodeCompileContext, size uintptr) *opcode { func newSliceElemCode(ctx *encodeCompileContext, head *opcode, size uintptr) *opcode {
return &opcode{ return &opcode{
op: opSliceElem, op: opSliceElem,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: opcodeOffset(ctx.ptrIndex),
headIdx: head.idx,
elemIdx: head.elemIdx,
length: head.length,
indent: ctx.indent, indent: ctx.indent,
size: size, size: size,
} }
} }
func newArrayHeaderCode(ctx *encodeCompileContext, alen int) *opcode { func newArrayHeaderCode(ctx *encodeCompileContext, alen int) *opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
return &opcode{ return &opcode{
op: opArrayHead, op: opArrayHead,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: idx,
headIdx: idx,
elemIdx: elemIdx,
indent: ctx.indent, indent: ctx.indent,
length: uintptr(alen), 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{ return &opcode{
op: opArrayElem, op: opArrayElem,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: opcodeOffset(ctx.ptrIndex),
length: uintptr(alen), elemIdx: head.elemIdx,
headIdx: head.headIdx,
length: uintptr(length),
size: size, size: size,
} }
} }
@ -235,29 +316,45 @@ func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
} else { } else {
op = opMapHead 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{ return &opcode{
op: op, op: op,
typ: ctx.typ, typ: ctx.typ,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: idx,
elemIdx: elemIdx,
length: length,
mapIter: mapIter,
indent: ctx.indent, indent: ctx.indent,
} }
} }
func newMapKeyCode(ctx *encodeCompileContext) *opcode { func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
return &opcode{ return &opcode{
op: opMapKey, op: opMapKey,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx,
length: head.length,
mapIter: head.mapIter,
indent: ctx.indent, indent: ctx.indent,
} }
} }
func newMapValueCode(ctx *encodeCompileContext) *opcode { func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
return &opcode{ return &opcode{
op: opMapValue, op: opMapValue,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: opcodeOffset(ctx.ptrIndex),
elemIdx: head.elemIdx,
length: head.length,
mapIter: head.mapIter,
indent: ctx.indent, indent: ctx.indent,
} }
} }
@ -267,7 +364,7 @@ func newInterfaceCode(ctx *encodeCompileContext) *opcode {
op: opInterface, op: opInterface,
typ: ctx.typ, typ: ctx.typ,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: opcodeOffset(ctx.ptrIndex),
indent: ctx.indent, indent: ctx.indent,
next: newEndOp(ctx), next: newEndOp(ctx),
root: ctx.root, root: ctx.root,
@ -279,7 +376,7 @@ func newRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
op: opStructFieldRecursive, op: opStructFieldRecursive,
typ: ctx.typ, typ: ctx.typ,
displayIdx: ctx.opcodeIndex, displayIdx: ctx.opcodeIndex,
idx: opcodeOffset(ctx.opcodeIndex), idx: opcodeOffset(ctx.ptrIndex),
indent: ctx.indent, indent: ctx.indent,
next: newEndOp(ctx), next: newEndOp(ctx),
jmp: jmp, jmp: jmp,

View File

@ -11,6 +11,8 @@ import (
"unsafe" "unsafe"
) )
const startDetectingCyclesAfter = 1000
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
return *(*uintptr)(unsafe.Pointer(base + idx)) return *(*uintptr)(unsafe.Pointer(base + idx))
} }
@ -19,7 +21,7 @@ func store(base uintptr, idx uintptr, p uintptr) {
*(*uintptr)(unsafe.Pointer(base + idx)) = p *(*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() ctxptr := ctx.ptr()
for { for {
switch code.op { switch code.op {
@ -144,7 +146,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
ptrs: make([]uintptr, c.totalLength()), ptrs: make([]uintptr, c.totalLength()),
} }
ctx.init(uintptr(header.ptr)) 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 return err
} }
code = code.next code = code.next
@ -236,9 +238,10 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
if idx < length { if idx < length {
e.encodeByte(',') e.encodeByte(',')
store(ctxptr, code.elemIdx, idx) store(ctxptr, code.elemIdx, idx)
code = code.next
data := load(ctxptr, code.headIdx) 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 { } else {
e.encodeByte(']') e.encodeByte(']')
code = code.end.next code = code.end.next
@ -295,9 +298,10 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
e.encodeBytes([]byte{',', '\n'}) e.encodeBytes([]byte{',', '\n'})
e.encodeIndent(code.indent + 1) e.encodeIndent(code.indent + 1)
store(ctxptr, code.elemIdx, idx) store(ctxptr, code.elemIdx, idx)
code = code.next
data := load(ctxptr, code.headIdx) 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 { } else {
e.encodeByte('\n') e.encodeByte('\n')
e.encodeIndent(code.indent) e.encodeIndent(code.indent)
@ -329,6 +333,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
} else { } else {
e.encodeByte('[') e.encodeByte('[')
if code.length > 0 { if code.length > 0 {
store(ctxptr, code.elemIdx, 0)
code = code.next code = code.next
store(ctxptr, code.idx, p) store(ctxptr, code.idx, p)
} else { } else {
@ -343,8 +348,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
e.encodeByte(',') e.encodeByte(',')
store(ctxptr, code.elemIdx, idx) store(ctxptr, code.elemIdx, idx)
p := load(ctxptr, code.headIdx) p := load(ctxptr, code.headIdx)
size := code.size
code = code.next code = code.next
store(ctxptr, code.idx, p+idx*code.size) store(ctxptr, code.idx, p+idx*size)
} else { } else {
e.encodeByte(']') e.encodeByte(']')
code = code.end.next code = code.end.next
@ -359,6 +365,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
e.encodeBytes([]byte{'[', '\n'}) e.encodeBytes([]byte{'[', '\n'})
if code.length > 0 { if code.length > 0 {
e.encodeIndent(code.indent + 1) e.encodeIndent(code.indent + 1)
store(ctxptr, code.elemIdx, 0)
code = code.next code = code.next
store(ctxptr, code.idx, p) store(ctxptr, code.idx, p)
} else { } else {
@ -375,8 +382,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
e.encodeIndent(code.indent + 1) e.encodeIndent(code.indent + 1)
store(ctxptr, code.elemIdx, idx) store(ctxptr, code.elemIdx, idx)
p := load(ctxptr, code.headIdx) p := load(ctxptr, code.headIdx)
size := code.size
code = code.next code = code.next
store(ctxptr, code.idx, p+idx*code.size) store(ctxptr, code.idx, p+idx*size)
} else { } else {
e.encodeByte('\n') e.encodeByte('\n')
e.encodeIndent(code.indent) e.encodeIndent(code.indent)
@ -393,9 +401,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
mlen := maplen(unsafe.Pointer(ptr)) mlen := maplen(unsafe.Pointer(ptr))
if mlen > 0 { if mlen > 0 {
iter := mapiterinit(code.typ, unsafe.Pointer(ptr)) iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
store(ctxptr, code.mapKey.length, uintptr(mlen)) store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.mapKey.mapIter, uintptr(iter)) store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapValue.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
code = code.next code = code.next
@ -416,9 +424,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
mlen := maplen(unsafe.Pointer(ptr)) mlen := maplen(unsafe.Pointer(ptr))
if mlen > 0 { if mlen > 0 {
iter := mapiterinit(code.typ, unsafe.Pointer(ptr)) iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
store(ctxptr, code.mapKey.length, uintptr(mlen)) store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.mapKey.mapIter, uintptr(iter)) store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapValue.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
code = code.next code = code.next
@ -460,9 +468,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
if mlen > 0 { if mlen > 0 {
e.encodeBytes([]byte{'{', '\n'}) e.encodeBytes([]byte{'{', '\n'})
iter := mapiterinit(code.typ, unsafe.Pointer(ptr)) iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
store(ctxptr, code.mapKey.length, uintptr(mlen)) store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.mapKey.mapIter, uintptr(iter)) store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapValue.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
code = code.next code = code.next
@ -486,9 +494,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
if mlen > 0 { if mlen > 0 {
e.encodeBytes([]byte{'{', '\n'}) e.encodeBytes([]byte{'{', '\n'})
iter := mapiterinit(code.typ, unsafe.Pointer(ptr)) iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
store(ctxptr, code.mapKey.length, uintptr(mlen)) store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.mapKey.mapIter, uintptr(iter)) store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapValue.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
code = code.next code = code.next
@ -510,9 +518,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
if mlen > 0 { if mlen > 0 {
e.encodeBytes([]byte{'{', '\n'}) e.encodeBytes([]byte{'{', '\n'})
iter := mapiterinit(code.typ, unsafe.Pointer(ptr)) iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
store(ctxptr, code.mapKey.length, uintptr(mlen)) store(ctxptr, code.elemIdx, 0)
store(ctxptr, code.mapKey.mapIter, uintptr(iter)) store(ctxptr, code.length, uintptr(mlen))
store(ctxptr, code.mapValue.mapIter, uintptr(iter)) store(ctxptr, code.mapIter, uintptr(iter))
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.next.idx, uintptr(key)) store(ctxptr, code.next.idx, uintptr(key))
code = code.next code = code.next
@ -569,17 +577,20 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
case opStructFieldRecursive: case opStructFieldRecursive:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)
if ptr != 0 { if ptr != 0 {
if _, exists := seenPtr[ptr]; exists { if recursiveLevel > startDetectingCyclesAfter {
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{ if _, exists := seenPtr[ptr]; exists {
typ: code.typ, v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
ptr: unsafe.Pointer(ptr), typ: code.typ,
})) ptr: unsafe.Pointer(ptr),
return &UnsupportedValueError{ }))
Value: reflect.ValueOf(v), return &UnsupportedValueError{
Str: fmt.Sprintf("encountered a cycle via %s", code.typ), Value: reflect.ValueOf(v),
Str: fmt.Sprintf("encountered a cycle via %s", code.typ),
}
} }
} }
} }
seenPtr[ptr] = struct{}{}
c := *(code.jmp.code) c := *(code.jmp.code)
ctx := &encodeRuntimeContext{ ctx := &encodeRuntimeContext{
ptrs: make([]uintptr, c.totalLength()), ptrs: make([]uintptr, c.totalLength()),
@ -587,7 +598,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
ctx.init(ptr) ctx.init(ptr)
c.end.next = newEndOp(&encodeCompileContext{}) c.end.next = newEndOp(&encodeCompileContext{})
c.op = c.op.ptrHeadToHead() 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 return err
} }
code = code.next code = code.next
@ -611,8 +622,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
if !code.anonymousKey { if !code.anonymousKey {
e.encodeBytes(code.key) e.encodeBytes(code.key)
} }
p := ptr + code.offset
code = code.next code = code.next
store(ctxptr, code.idx, ptr+code.offset) store(ctxptr, code.idx, p)
} }
case opStructFieldAnonymousHead: case opStructFieldAnonymousHead:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)
@ -4063,40 +4075,45 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
} }
e.encodeBytes(code.key) e.encodeBytes(code.key)
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)
p := ptr + code.offset
code = code.next code = code.next
store(ctxptr, code.idx, ptr+code.offset) store(ctxptr, code.idx, p)
case opStructFieldSlice: case opStructFieldSlice:
if e.buf[len(e.buf)-1] != '{' { if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',') e.encodeByte(',')
} }
e.encodeBytes(code.key) e.encodeBytes(code.key)
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)
p := ptr + code.offset
code = code.next code = code.next
store(ctxptr, code.idx, ptr+code.offset) store(ctxptr, code.idx, p)
case opStructFieldMap: case opStructFieldMap:
if e.buf[len(e.buf)-1] != '{' { if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',') e.encodeByte(',')
} }
e.encodeBytes(code.key) e.encodeBytes(code.key)
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)
p := ptr + code.offset
code = code.next code = code.next
store(ctxptr, code.idx, ptr+code.offset) store(ctxptr, code.idx, p)
case opStructFieldMapLoad: case opStructFieldMapLoad:
if e.buf[len(e.buf)-1] != '{' { if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',') e.encodeByte(',')
} }
e.encodeBytes(code.key) e.encodeBytes(code.key)
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)
p := ptr + code.offset
code = code.next code = code.next
store(ctxptr, code.idx, ptr+code.offset) store(ctxptr, code.idx, p)
case opStructFieldStruct: case opStructFieldStruct:
if e.buf[len(e.buf)-1] != '{' { if e.buf[len(e.buf)-1] != '{' {
e.encodeByte(',') e.encodeByte(',')
} }
e.encodeBytes(code.key) e.encodeBytes(code.key)
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)
p := ptr + code.offset
code = code.next code = code.next
store(ctxptr, code.idx, ptr+code.offset) store(ctxptr, code.idx, p)
case opStructFieldIndent: case opStructFieldIndent:
if e.buf[len(e.buf)-2] != '{' || e.buf[len(e.buf)-1] == '}' { if e.buf[len(e.buf)-2] != '{' || e.buf[len(e.buf)-1] == '}' {
e.encodeBytes([]byte{',', '\n'}) e.encodeBytes([]byte{',', '\n'})
@ -4105,8 +4122,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
e.encodeBytes(code.key) e.encodeBytes(code.key)
e.encodeByte(' ') e.encodeByte(' ')
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)
p := ptr + code.offset
code = code.next code = code.next
store(ctxptr, code.idx, ptr+code.offset) store(ctxptr, code.idx, p)
case opStructFieldIntIndent: case opStructFieldIntIndent:
if e.buf[len(e.buf)-2] != '{' || e.buf[len(e.buf)-1] == '}' { if e.buf[len(e.buf)-2] != '{' || e.buf[len(e.buf)-1] == '}' {
e.encodeBytes([]byte{',', '\n'}) e.encodeBytes([]byte{',', '\n'})

View File

@ -121,7 +121,6 @@ func TestIndent(t *testing.T) {
} }
// Tests of a large random structure. // Tests of a large random structure.
func TestCompactBig(t *testing.T) { func TestCompactBig(t *testing.T) {
initBig() initBig()
var buf bytes.Buffer var buf bytes.Buffer
@ -221,7 +220,10 @@ func trim(b []byte) []byte {
// Generate a random JSON object. // Generate a random JSON object.
var jsonBig []byte var (
jsonBig []byte
jsonVal interface{}
)
func initBig() { func initBig() {
if len(jsonBig) > 0 { if len(jsonBig) > 0 {
@ -231,8 +233,8 @@ func initBig() {
if testing.Short() { if testing.Short() {
n = 100 n = 100
} }
v := genValue(n) jsonVal = genValue(n)
b, err := json.Marshal(v) b, err := json.Marshal(jsonVal)
if err != nil { if err != nil {
panic(err) panic(err)
} }