From 3aa921e884337420ac422fafb9ad387083e6f00a Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Tue, 1 Sep 2020 22:26:26 +0900 Subject: [PATCH] Fix encoding engine --- encode.go | 4 +- encode_compile.go | 95 ++++++++++++++-------------- encode_context.go | 26 +++++++- encode_opcode.go | 153 +++++++++++++++++++++++++++++++++++++--------- encode_vm.go | 96 +++++++++++++++++------------ json_test.go | 10 +-- 6 files changed, 263 insertions(+), 121 deletions(-) diff --git a/encode.go b/encode.go index e9f2c48..4ca7216 100644 --- a/encode.go +++ b/encode.go @@ -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 } diff --git a/encode_compile.go b/encode_compile.go index ff1124e..54819df 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -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 diff --git a/encode_context.go b/encode_context.go index a36ddec..0b30e45 100644 --- a/encode_context.go +++ b/encode_context.go @@ -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 } diff --git a/encode_opcode.go b/encode_opcode.go index bc1fe9f..9ff35ba 100644 --- a/encode_opcode.go +++ b/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, diff --git a/encode_vm.go b/encode_vm.go index 409a60c..81b9750 100644 --- a/encode_vm.go +++ b/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'}) diff --git a/json_test.go b/json_test.go index 3831777..bb25fce 100644 --- a/json_test.go +++ b/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) }