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)
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
}

View File

@ -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

View File

@ -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
}

View File

@ -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,

View File

@ -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,6 +577,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
case opStructFieldRecursive:
ptr := load(ctxptr, code.idx)
if ptr != 0 {
if recursiveLevel > startDetectingCyclesAfter {
if _, exists := seenPtr[ptr]; exists {
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
typ: code.typ,
@ -580,6 +589,8 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, seenPtr map[uintptr]struct{}, c
}
}
}
}
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'})

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,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)
}