From 75f34df1c61fc6c0344986ebbe0123b4a2930f62 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Sun, 24 Jan 2021 23:27:23 +0900 Subject: [PATCH] Fix interface operation --- encode_opcode.go | 2 +- encode_vm.go | 63 +++++++++---------------- encode_vm_escaped_indent.go | 91 ++++++++++++------------------------- encode_vm_indent.go | 91 ++++++++++++------------------------- 4 files changed, 81 insertions(+), 166 deletions(-) diff --git a/encode_opcode.go b/encode_opcode.go index e5717e0..cf3d013 100644 --- a/encode_opcode.go +++ b/encode_opcode.go @@ -131,7 +131,7 @@ func (c *opcode) totalLength() int { var idx int for code := c; code.op != opEnd; { idx = int(code.idx / uintptrSize) - if code.op == opInterfaceEnd || code.op == opStructFieldRecursiveEnd { + if code.op == opStructFieldRecursiveEnd { break } switch code.op.codeType() { diff --git a/encode_vm.go b/encode_vm.go index fe05b32..9ee6aba 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -207,65 +207,46 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ( } } ctx.seenPtr = append(ctx.seenPtr, ptr) - v := e.ptrToInterface(code, ptr) - ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v)) - rv := reflect.ValueOf(v) - if rv.IsNil() { + iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr)) + if iface == nil || iface.ptr == nil { b = encodeNull(b) b = encodeComma(b) code = code.next break } - vv := rv.Interface() - header := (*interfaceHeader)(unsafe.Pointer(&vv)) - if header.typ.Kind() == reflect.Ptr { - if rv.Elem().IsNil() { - b = encodeNull(b) - b = encodeComma(b) - code = code.next - break - } - } - c, err := e.compileHead(&encodeCompileContext{ - typ: header.typ, - root: code.root, - indent: code.indent, - structTypeToCompiledCode: map[uintptr]*compiledCode{}, - }) + ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface)) + ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ))) if err != nil { return nil, err } - beforeLastCode := c.beforeLastCode() - lastCode := beforeLastCode.next - lastCode.idx = beforeLastCode.idx + uintptrSize - totalLength := uintptr(code.totalLength()) - nextTotalLength := uintptr(c.totalLength()) + + totalLength := uintptr(codeSet.codeLength) + nextTotalLength := uintptr(ifaceCodeSet.codeLength) + curlen := uintptr(len(ctx.ptrs)) offsetNum := ptrOffset / uintptrSize - oldOffset := ptrOffset - ptrOffset += totalLength * uintptrSize newLen := offsetNum + totalLength + nextTotalLength if curlen < newLen { ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...) } - ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr + oldPtrs := ctx.ptrs - store(ctxptr, 0, uintptr(header.ptr)) - store(ctxptr, lastCode.idx, oldOffset) + newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:] + newPtrs[0] = uintptr(iface.ptr) - // link lastCode ( opInterfaceEnd ) => code.next - lastCode.op = opInterfaceEnd - lastCode.next = code.next + ctx.ptrs = newPtrs - code = c - recursiveLevel++ - case opInterfaceEnd: - recursiveLevel-- - // restore ctxptr - offset := load(ctxptr, code.idx) - ctxptr = ctx.ptr() + offset - ptrOffset = offset + bb, err := e.runEscaped(ctx, b, ifaceCodeSet) + if err != nil { + return nil, err + } + + ctx.ptrs = oldPtrs + ctxptr = ctx.ptr() + ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1] + + b = bb code = code.next case opMarshalJSON: ptr := load(ctxptr, code.idx) diff --git a/encode_vm_escaped_indent.go b/encode_vm_escaped_indent.go index 2cfd745..bc0116c 100644 --- a/encode_vm_escaped_indent.go +++ b/encode_vm_escaped_indent.go @@ -12,8 +12,6 @@ import ( ) func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) { - recursiveLevel := 0 - var seenPtr map[uintptr]struct{} ptrOffset := uintptr(0) ctxptr := ctx.ptr() code := codeSet.code @@ -164,83 +162,52 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, codeSet code = code.next break } - if seenPtr == nil { - seenPtr = map[uintptr]struct{}{} + for _, seen := range ctx.seenPtr { + if ptr == seen { + return nil, errUnsupportedValue(code, ptr) + } } - if _, exists := seenPtr[ptr]; exists { - return nil, errUnsupportedValue(code, ptr) - } - seenPtr[ptr] = struct{}{} - v := e.ptrToInterface(code, ptr) - ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v)) - rv := reflect.ValueOf(v) - if rv.IsNil() { + ctx.seenPtr = append(ctx.seenPtr, ptr) + iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr)) + if iface == nil || iface.ptr == nil { b = encodeNull(b) b = encodeIndentComma(b) code = code.next break } - vv := rv.Interface() - header := (*interfaceHeader)(unsafe.Pointer(&vv)) - typ := header.typ - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() + ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface)) + ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ))) + if err != nil { + return nil, err } - var c *opcode - if typ.Kind() == reflect.Map { - code, err := e.compileMap(&encodeCompileContext{ - typ: typ, - root: code.root, - indent: code.indent, - structTypeToCompiledCode: map[uintptr]*compiledCode{}, - }, false) - if err != nil { - return nil, err - } - c = code - } else { - code, err := e.compile(&encodeCompileContext{ - typ: typ, - root: code.root, - indent: code.indent, - structTypeToCompiledCode: map[uintptr]*compiledCode{}, - }) - if err != nil { - return nil, err - } - c = code - } - beforeLastCode := c.beforeLastCode() - lastCode := beforeLastCode.next - lastCode.idx = beforeLastCode.idx + uintptrSize - totalLength := uintptr(code.totalLength()) - nextTotalLength := uintptr(c.totalLength()) + + totalLength := uintptr(codeSet.codeLength) + nextTotalLength := uintptr(ifaceCodeSet.codeLength) + curlen := uintptr(len(ctx.ptrs)) offsetNum := ptrOffset / uintptrSize - oldOffset := ptrOffset - ptrOffset += totalLength * uintptrSize newLen := offsetNum + totalLength + nextTotalLength if curlen < newLen { ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...) } - ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr + oldPtrs := ctx.ptrs - store(ctxptr, 0, uintptr(header.ptr)) - store(ctxptr, lastCode.idx, oldOffset) + newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:] + newPtrs[0] = uintptr(iface.ptr) - // link lastCode ( opInterfaceEnd ) => code.next - lastCode.op = opInterfaceEnd - lastCode.next = code.next + ctx.ptrs = newPtrs - code = c - recursiveLevel++ - case opInterfaceEnd: - recursiveLevel-- - // restore ctxptr - offset := load(ctxptr, code.idx) - ctxptr = ctx.ptr() + offset - ptrOffset = offset + bb, err := e.runEscaped(ctx, b, ifaceCodeSet) + if err != nil { + return nil, err + } + + ctx.ptrs = oldPtrs + ctxptr = ctx.ptr() + ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1] + + b = bb code = code.next case opMarshalJSON: ptr := load(ctxptr, code.idx) diff --git a/encode_vm_indent.go b/encode_vm_indent.go index 78a6379..9c2f03a 100644 --- a/encode_vm_indent.go +++ b/encode_vm_indent.go @@ -12,8 +12,6 @@ import ( ) func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) { - recursiveLevel := 0 - var seenPtr map[uintptr]struct{} ptrOffset := uintptr(0) ctxptr := ctx.ptr() code := codeSet.code @@ -164,83 +162,52 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcode code = code.next break } - if seenPtr == nil { - seenPtr = map[uintptr]struct{}{} + for _, seen := range ctx.seenPtr { + if ptr == seen { + return nil, errUnsupportedValue(code, ptr) + } } - if _, exists := seenPtr[ptr]; exists { - return nil, errUnsupportedValue(code, ptr) - } - seenPtr[ptr] = struct{}{} - v := e.ptrToInterface(code, ptr) - ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v)) - rv := reflect.ValueOf(v) - if rv.IsNil() { + ctx.seenPtr = append(ctx.seenPtr, ptr) + iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr)) + if iface == nil || iface.ptr == nil { b = encodeNull(b) b = encodeIndentComma(b) code = code.next break } - vv := rv.Interface() - header := (*interfaceHeader)(unsafe.Pointer(&vv)) - typ := header.typ - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() + ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface)) + ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ))) + if err != nil { + return nil, err } - var c *opcode - if typ.Kind() == reflect.Map { - code, err := e.compileMap(&encodeCompileContext{ - typ: typ, - root: code.root, - indent: code.indent, - structTypeToCompiledCode: map[uintptr]*compiledCode{}, - }, false) - if err != nil { - return nil, err - } - c = code - } else { - code, err := e.compile(&encodeCompileContext{ - typ: typ, - root: code.root, - indent: code.indent, - structTypeToCompiledCode: map[uintptr]*compiledCode{}, - }) - if err != nil { - return nil, err - } - c = code - } - beforeLastCode := c.beforeLastCode() - lastCode := beforeLastCode.next - lastCode.idx = beforeLastCode.idx + uintptrSize - totalLength := uintptr(code.totalLength()) - nextTotalLength := uintptr(c.totalLength()) + + totalLength := uintptr(codeSet.codeLength) + nextTotalLength := uintptr(ifaceCodeSet.codeLength) + curlen := uintptr(len(ctx.ptrs)) offsetNum := ptrOffset / uintptrSize - oldOffset := ptrOffset - ptrOffset += totalLength * uintptrSize newLen := offsetNum + totalLength + nextTotalLength if curlen < newLen { ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...) } - ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr + oldPtrs := ctx.ptrs - store(ctxptr, 0, uintptr(header.ptr)) - store(ctxptr, lastCode.idx, oldOffset) + newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:] + newPtrs[0] = uintptr(iface.ptr) - // link lastCode ( opInterfaceEnd ) => code.next - lastCode.op = opInterfaceEnd - lastCode.next = code.next + ctx.ptrs = newPtrs - code = c - recursiveLevel++ - case opInterfaceEnd: - recursiveLevel-- - // restore ctxptr - offset := load(ctxptr, code.idx) - ctxptr = ctx.ptr() + offset - ptrOffset = offset + bb, err := e.runEscaped(ctx, b, ifaceCodeSet) + if err != nil { + return nil, err + } + + ctx.ptrs = oldPtrs + ctxptr = ctx.ptr() + ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1] + + b = bb code = code.next case opMarshalJSON: ptr := load(ctxptr, code.idx)