Refactor vm sources for encoding ( use same source )

This commit is contained in:
Masaaki Goshima 2021-05-18 13:51:57 +09:00
parent fc968c75ee
commit f696453c1b
15 changed files with 927 additions and 651 deletions

View File

@ -1450,7 +1450,6 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
} }
fieldIdx++ fieldIdx++
} }
ctx = ctx.decIndent()
structEndCode := &Opcode{ structEndCode := &Opcode{
Op: OpStructEnd, Op: OpStructEnd,
@ -1459,6 +1458,8 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
Next: newEndOp(ctx), Next: newEndOp(ctx),
} }
ctx = ctx.decIndent()
// no struct field // no struct field
if head == nil { if head == nil {
head = &Opcode{ head = &Opcode{

View File

@ -0,0 +1,9 @@
package vm
import (
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent`, `vm_debug` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent => vm_debug
_ "github.com/goccy/go-json/internal/encoder/vm_escaped"
)

View File

@ -2,12 +2,41 @@ package vm
import ( import (
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
addr := base + idx addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr)) return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +105,57 @@ func appendComma(b []byte) []byte {
return append(b, ',') return append(b, ',')
} }
func appendColon(b []byte) []byte {
last := len(b) - 1
b[last] = ':'
return b
}
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
b = append(b, key...)
b[len(b)-1] = ':'
return append(b, value...)
}
func appendMapEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b[len(b)-1] = '}'
b = append(b, ',')
return b
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalJSON(ctx, code, b, v, false) return encoder.AppendMarshalJSON(ctx, code, b, v, false)
} }
@ -84,14 +164,38 @@ func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, e
return encoder.AppendMarshalText(code, b, v, false) return encoder.AppendMarshalText(code, b, v, false)
} }
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, code.Key...) return append(b, '[')
}
func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = ']'
return append(b, ',')
}
func appendEmptyArray(b []byte) []byte {
return append(b, '[', ']', ',')
}
func appendEmptyObject(b []byte) []byte {
return append(b, '{', '}', ',')
}
func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = '}'
return append(b, ',')
} }
func appendStructHead(b []byte) []byte { func appendStructHead(b []byte) []byte {
return append(b, '{') return append(b, '{')
} }
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return append(b, code.Key...)
}
func appendStructEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { func appendStructEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, '}', ',') return append(b, '}', ',')
} }
@ -104,3 +208,8 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
} }
return appendStructEnd(ctx, code, b) return appendStructEnd(ctx, code, b)
} }
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {}
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {}
func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }

View File

@ -1,45 +1,13 @@
package vm package vm
import ( import (
"fmt"
"math" "math"
"sort" "sort"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent`, `vm_debug` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent => vm_debug
_ "github.com/goccy/go-json/internal/encoder/vm_escaped"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
@ -49,7 +17,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for { for {
switch code.Op { switch code.Op {
default: default:
return nil, fmt.Errorf("encoder: opcode %s has not been implemented", code.Op) return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr: case encoder.OpPtr:
p := load(ctxptr, code.Idx) p := load(ctxptr, code.Idx)
code = code.Next code = code.Next
@ -121,7 +89,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpFloat64: case encoder.OpFloat64:
v := ptrToFloat64(load(ctxptr, code.Idx)) v := ptrToFloat64(load(ctxptr, code.Idx))
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, encoder.ErrUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendComma(b) b = appendComma(b)
@ -216,35 +184,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.Ptrs = oldPtrs
ctxptr = ctx.Ptr() ctxptr = ctx.Ptr()
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -327,11 +270,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.Length, uintptr(slice.Len)) store(ctxptr, code.Length, uintptr(slice.Len))
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
if slice.Len > 0 { if slice.Len > 0 {
b = append(b, '[') b = appendArrayHead(ctx, code, b)
code = code.Next code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
} else { } else {
b = append(b, '[', ']', ',') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpSliceElem: case encoder.OpSliceElem:
@ -339,15 +282,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if idx < length { if idx < length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx) data := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, data+idx*size) store(ctxptr, code.Idx, data+idx*size)
} else { } else {
last := len(b) - 1 b = appendArrayEnd(ctx, code, b)
b[last] = ']'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayPtr: case encoder.OpArrayPtr:
@ -369,27 +311,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break break
} }
if code.Length > 0 { if code.Length > 0 {
b = append(b, '[') b = appendArrayHead(ctx, code, b)
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
} else { } else {
b = append(b, '[', ']', ',') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayElem: case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
idx++ idx++
if idx < code.Length { if idx < code.Length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, p+idx*size) store(ctxptr, code.Idx, p+idx*size)
} else { } else {
last := len(b) - 1 b = appendArrayEnd(ctx, code, b)
b[last] = ']'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpMapPtr: case encoder.OpMapPtr:
@ -413,11 +354,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p) uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr) mlen := maplen(uptr)
if mlen <= 0 { if mlen <= 0 {
b = append(b, '{', '}', ',') b = appendEmptyObject(b)
code = code.End.Next code = code.End.Next
break break
} }
b = append(b, '{') b = appendStructHead(b)
iter := mapiterinit(code.Type, uptr) iter := mapiterinit(code.Type, uptr)
ctx.KeepRefs = append(ctx.KeepRefs, iter) ctx.KeepRefs = append(ctx.KeepRefs, iter)
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
@ -428,6 +369,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
mapCtx.Pos = append(mapCtx.Pos, len(b)) mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -438,16 +381,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length { if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter) ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr) iter := ptrToUnsafePtr(ptr)
store(ctxptr, code.ElemIdx, idx)
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
} else { } else {
last := len(b) - 1 b = appendObjectEnd(ctx, code, b)
b[last] = '}'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
} else { } else {
@ -467,8 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
last := len(b) - 1 b = appendColon(b)
b[last] = ':'
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr)) mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -503,12 +444,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice) sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items { for _, item := range mapCtx.Slice.Items {
buf = append(buf, item.Key...) buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
buf[len(buf)-1] = ':'
buf = append(buf, item.Value...)
} }
buf[len(buf)-1] = '}' buf = appendMapEnd(ctx, code, buf)
buf = append(buf, ',')
b = b[:pos[0]] b = b[:pos[0]]
b = append(b, buf...) b = append(b, buf...)
mapCtx.Buf = buf mapCtx.Buf = buf
@ -539,6 +477,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
offsetNum := ptrOffset / uintptrSize offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset oldOffset := ptrOffset
ptrOffset += code.Jmp.CurLen * uintptrSize ptrOffset += code.Jmp.CurLen * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent - 1
newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen
if curlen < newLen { if curlen < newLen {
@ -549,12 +489,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, c.Idx, ptr) store(ctxptr, c.Idx, ptr)
store(ctxptr, c.End.Next.Idx, oldOffset) store(ctxptr, c.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, c, uintptr(oldBaseIndent))
code = c code = c
recursiveLevel++ recursiveLevel++
case encoder.OpRecursiveEnd: case encoder.OpRecursiveEnd:
recursiveLevel-- recursiveLevel--
// restore ctxptr // restore ctxptr
restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx) offset := load(ctxptr, code.Idx)
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -587,7 +529,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if !code.AnonymousHead { if !code.AnonymousHead {
b = appendStructHead(b) b = appendStructHead(b)
} }
if !code.AnonymousKey { if !code.AnonymousKey && len(code.Key) > 0 {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
} }
p += code.Offset p += code.Offset
@ -1883,8 +1825,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset) b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString: case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2404,11 +2345,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
v := ptrToBytes(p + code.Offset) v := ptrToBytes(p + code.Offset)
if v == nil { if len(v) == 0 {
code = code.NextField code = code.NextField
} else { } else {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p)) b = appendByteSlice(b, v)
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
} }
@ -3500,9 +3441,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldIntPtr: case encoder.OpStructFieldIntPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3580,9 +3521,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldUintPtr: case encoder.OpStructFieldUintPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3658,9 +3599,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldFloat32Ptr: case encoder.OpStructFieldFloat32Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3750,9 +3691,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldFloat64Ptr: case encoder.OpStructFieldFloat64Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
b = appendComma(b) b = appendComma(b)
@ -3828,8 +3769,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
case encoder.OpStructFieldStringString: case encoder.OpStructFieldStringString:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset) s := ptrToString(p + code.Offset)
b = appendStructKey(ctx, code, b)
b = appendString(b, string(appendString([]byte{}, s))) b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
@ -3843,9 +3784,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldStringPtr: case encoder.OpStructFieldStringPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3863,9 +3804,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldStringPtrString: case encoder.OpStructFieldStringPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3917,9 +3858,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBoolPtr: case encoder.OpStructFieldBoolPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3937,9 +3878,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBoolPtrString: case encoder.OpStructFieldBoolPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3976,9 +3917,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBytesPtr: case encoder.OpStructFieldBytesPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4042,9 +3983,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldNumberPtr: case encoder.OpStructFieldNumberPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4069,9 +4010,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldNumberPtrString: case encoder.OpStructFieldNumberPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4234,9 +4175,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyArray: case encoder.OpStructFieldOmitEmptyArray:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p += code.Offset p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructFieldArrayPtr: case encoder.OpStructFieldArrayPtr:
@ -4332,17 +4273,21 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p += code.Offset p += code.Offset
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructEnd: case encoder.OpStructFieldOmitEmptyStruct:
last := len(b) - 1 p := load(ctxptr, code.HeadIdx)
if b[last] == ',' { p += code.Offset
b[last] = '}' if ptrToPtr(p) == 0 && code.IsNextOpPtrType {
code = code.NextField
} else { } else {
b = append(b, '}') b = appendStructKey(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, p)
} }
b = appendComma(b)
code = code.Next
case encoder.OpStructAnonymousEnd: case encoder.OpStructAnonymousEnd:
code = code.Next code = code.Next
case encoder.OpStructEnd:
b = appendStructEndSkipLast(ctx, code, b)
code = code.Next
case encoder.OpStructEndInt: case encoder.OpStructEndInt:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
@ -4607,11 +4552,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
case encoder.OpStructEndFloat64: case encoder.OpStructEndFloat64:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4787,8 +4732,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
v := ptrToString(p) b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendString(b, string(appendString([]byte{}, v)))
} }
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4797,8 +4741,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
if p != 0 { if p != 0 {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
v := ptrToString(p) b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendString(b, string(appendString([]byte{}, v)))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
} else { } else {
b = appendStructEndSkipLast(ctx, code, b) b = appendStructEndSkipLast(ctx, code, b)

View File

@ -2,12 +2,41 @@ package vm_debug
import ( import (
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (debug): opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
addr := base + idx addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr)) return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +105,57 @@ func appendComma(b []byte) []byte {
return append(b, ',') return append(b, ',')
} }
func appendColon(b []byte) []byte {
last := len(b) - 1
b[last] = ':'
return b
}
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
b = append(b, key...)
b[len(b)-1] = ':'
return append(b, value...)
}
func appendMapEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b[len(b)-1] = '}'
b = append(b, ',')
return b
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalJSON(ctx, code, b, v, false) return encoder.AppendMarshalJSON(ctx, code, b, v, false)
} }
@ -84,14 +164,38 @@ func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, e
return encoder.AppendMarshalText(code, b, v, false) return encoder.AppendMarshalText(code, b, v, false)
} }
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, code.Key...) return append(b, '[')
}
func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = ']'
return append(b, ',')
}
func appendEmptyArray(b []byte) []byte {
return append(b, '[', ']', ',')
}
func appendEmptyObject(b []byte) []byte {
return append(b, '{', '}', ',')
}
func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = '}'
return append(b, ',')
} }
func appendStructHead(b []byte) []byte { func appendStructHead(b []byte) []byte {
return append(b, '{') return append(b, '{')
} }
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return append(b, code.Key...)
}
func appendStructEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { func appendStructEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, '}', ',') return append(b, '}', ',')
} }
@ -104,3 +208,8 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
} }
return appendStructEnd(ctx, code, b) return appendStructEnd(ctx, code, b)
} }
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {}
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {}
func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }

View File

@ -7,33 +7,8 @@ import (
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
@ -62,7 +37,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for { for {
switch code.Op { switch code.Op {
default: default:
return nil, fmt.Errorf("encoder: opcode %s has not been implemented", code.Op) return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr: case encoder.OpPtr:
p := load(ctxptr, code.Idx) p := load(ctxptr, code.Idx)
code = code.Next code = code.Next
@ -134,7 +109,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpFloat64: case encoder.OpFloat64:
v := ptrToFloat64(load(ctxptr, code.Idx)) v := ptrToFloat64(load(ctxptr, code.Idx))
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, encoder.ErrUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendComma(b) b = appendComma(b)
@ -229,35 +204,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.Ptrs = oldPtrs
ctxptr = ctx.Ptr() ctxptr = ctx.Ptr()
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -340,11 +290,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.Length, uintptr(slice.Len)) store(ctxptr, code.Length, uintptr(slice.Len))
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
if slice.Len > 0 { if slice.Len > 0 {
b = append(b, '[') b = appendArrayHead(ctx, code, b)
code = code.Next code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
} else { } else {
b = append(b, '[', ']', ',') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpSliceElem: case encoder.OpSliceElem:
@ -352,15 +302,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if idx < length { if idx < length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx) data := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, data+idx*size) store(ctxptr, code.Idx, data+idx*size)
} else { } else {
last := len(b) - 1 b = appendArrayEnd(ctx, code, b)
b[last] = ']'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayPtr: case encoder.OpArrayPtr:
@ -382,27 +331,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break break
} }
if code.Length > 0 { if code.Length > 0 {
b = append(b, '[') b = appendArrayHead(ctx, code, b)
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
} else { } else {
b = append(b, '[', ']', ',') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayElem: case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
idx++ idx++
if idx < code.Length { if idx < code.Length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, p+idx*size) store(ctxptr, code.Idx, p+idx*size)
} else { } else {
last := len(b) - 1 b = appendArrayEnd(ctx, code, b)
b[last] = ']'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpMapPtr: case encoder.OpMapPtr:
@ -426,11 +374,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p) uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr) mlen := maplen(uptr)
if mlen <= 0 { if mlen <= 0 {
b = append(b, '{', '}', ',') b = appendEmptyObject(b)
code = code.End.Next code = code.End.Next
break break
} }
b = append(b, '{') b = appendStructHead(b)
iter := mapiterinit(code.Type, uptr) iter := mapiterinit(code.Type, uptr)
ctx.KeepRefs = append(ctx.KeepRefs, iter) ctx.KeepRefs = append(ctx.KeepRefs, iter)
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
@ -441,6 +389,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
mapCtx.Pos = append(mapCtx.Pos, len(b)) mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -451,16 +401,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length { if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter) ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr) iter := ptrToUnsafePtr(ptr)
store(ctxptr, code.ElemIdx, idx)
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
} else { } else {
last := len(b) - 1 b = appendObjectEnd(ctx, code, b)
b[last] = '}'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
} else { } else {
@ -480,8 +429,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
last := len(b) - 1 b = appendColon(b)
b[last] = ':'
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr)) mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -516,12 +464,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice) sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items { for _, item := range mapCtx.Slice.Items {
buf = append(buf, item.Key...) buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
buf[len(buf)-1] = ':'
buf = append(buf, item.Value...)
} }
buf[len(buf)-1] = '}' buf = appendMapEnd(ctx, code, buf)
buf = append(buf, ',')
b = b[:pos[0]] b = b[:pos[0]]
b = append(b, buf...) b = append(b, buf...)
mapCtx.Buf = buf mapCtx.Buf = buf
@ -552,6 +497,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
offsetNum := ptrOffset / uintptrSize offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset oldOffset := ptrOffset
ptrOffset += code.Jmp.CurLen * uintptrSize ptrOffset += code.Jmp.CurLen * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent - 1
newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen
if curlen < newLen { if curlen < newLen {
@ -562,12 +509,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, c.Idx, ptr) store(ctxptr, c.Idx, ptr)
store(ctxptr, c.End.Next.Idx, oldOffset) store(ctxptr, c.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, c, uintptr(oldBaseIndent))
code = c code = c
recursiveLevel++ recursiveLevel++
case encoder.OpRecursiveEnd: case encoder.OpRecursiveEnd:
recursiveLevel-- recursiveLevel--
// restore ctxptr // restore ctxptr
restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx) offset := load(ctxptr, code.Idx)
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -600,7 +549,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if !code.AnonymousHead { if !code.AnonymousHead {
b = appendStructHead(b) b = appendStructHead(b)
} }
if !code.AnonymousKey { if !code.AnonymousKey && len(code.Key) > 0 {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
} }
p += code.Offset p += code.Offset
@ -1896,8 +1845,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset) b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString: case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2417,11 +2365,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
v := ptrToBytes(p + code.Offset) v := ptrToBytes(p + code.Offset)
if v == nil { if len(v) == 0 {
code = code.NextField code = code.NextField
} else { } else {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p)) b = appendByteSlice(b, v)
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
} }
@ -3513,9 +3461,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldIntPtr: case encoder.OpStructFieldIntPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3593,9 +3541,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldUintPtr: case encoder.OpStructFieldUintPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3671,9 +3619,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldFloat32Ptr: case encoder.OpStructFieldFloat32Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3763,9 +3711,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldFloat64Ptr: case encoder.OpStructFieldFloat64Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
b = appendComma(b) b = appendComma(b)
@ -3841,8 +3789,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
case encoder.OpStructFieldStringString: case encoder.OpStructFieldStringString:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset) s := ptrToString(p + code.Offset)
b = appendStructKey(ctx, code, b)
b = appendString(b, string(appendString([]byte{}, s))) b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
@ -3856,9 +3804,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldStringPtr: case encoder.OpStructFieldStringPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3876,9 +3824,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldStringPtrString: case encoder.OpStructFieldStringPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3930,9 +3878,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBoolPtr: case encoder.OpStructFieldBoolPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3950,9 +3898,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBoolPtrString: case encoder.OpStructFieldBoolPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3989,9 +3937,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBytesPtr: case encoder.OpStructFieldBytesPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4055,9 +4003,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldNumberPtr: case encoder.OpStructFieldNumberPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4082,9 +4030,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldNumberPtrString: case encoder.OpStructFieldNumberPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4247,9 +4195,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyArray: case encoder.OpStructFieldOmitEmptyArray:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p += code.Offset p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructFieldArrayPtr: case encoder.OpStructFieldArrayPtr:
@ -4345,17 +4293,21 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p += code.Offset p += code.Offset
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructEnd: case encoder.OpStructFieldOmitEmptyStruct:
last := len(b) - 1 p := load(ctxptr, code.HeadIdx)
if b[last] == ',' { p += code.Offset
b[last] = '}' if ptrToPtr(p) == 0 && code.IsNextOpPtrType {
code = code.NextField
} else { } else {
b = append(b, '}') b = appendStructKey(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, p)
} }
b = appendComma(b)
code = code.Next
case encoder.OpStructAnonymousEnd: case encoder.OpStructAnonymousEnd:
code = code.Next code = code.Next
case encoder.OpStructEnd:
b = appendStructEndSkipLast(ctx, code, b)
code = code.Next
case encoder.OpStructEndInt: case encoder.OpStructEndInt:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
@ -4620,11 +4572,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
case encoder.OpStructEndFloat64: case encoder.OpStructEndFloat64:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4800,8 +4752,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
v := ptrToString(p) b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendString(b, string(appendString([]byte{}, v)))
} }
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4810,8 +4761,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
if p != 0 { if p != 0 {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
v := ptrToString(p) b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendString(b, string(appendString([]byte{}, v)))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
} else { } else {
b = appendStructEndSkipLast(ctx, code, b) b = appendStructEndSkipLast(ctx, code, b)

View File

@ -0,0 +1,9 @@
package vm_escaped
import (
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent`, `vm_debug` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent => vm_debug
_ "github.com/goccy/go-json/internal/encoder/vm_indent"
)

View File

@ -2,12 +2,41 @@ package vm_escaped
import ( import (
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendEscapedString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (escaped): opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
addr := base + idx addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr)) return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +105,57 @@ func appendComma(b []byte) []byte {
return append(b, ',') return append(b, ',')
} }
func appendColon(b []byte) []byte {
last := len(b) - 1
b[last] = ':'
return b
}
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
b = append(b, key...)
b[len(b)-1] = ':'
return append(b, value...)
}
func appendMapEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b[len(b)-1] = '}'
b = append(b, ',')
return b
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalJSON(ctx, code, b, v, true) return encoder.AppendMarshalJSON(ctx, code, b, v, true)
} }
@ -84,6 +164,30 @@ func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, e
return encoder.AppendMarshalText(code, b, v, true) return encoder.AppendMarshalText(code, b, v, true)
} }
func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, '[')
}
func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = ']'
return append(b, ',')
}
func appendEmptyArray(b []byte) []byte {
return append(b, '[', ']', ',')
}
func appendEmptyObject(b []byte) []byte {
return append(b, '{', '}', ',')
}
func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = '}'
return append(b, ',')
}
func appendStructHead(b []byte) []byte { func appendStructHead(b []byte) []byte {
return append(b, '{') return append(b, '{')
} }
@ -104,3 +208,8 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
} }
return appendStructEnd(ctx, code, b) return appendStructEnd(ctx, code, b)
} }
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {}
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {}
func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }

View File

@ -1,45 +1,13 @@
package vm_escaped package vm_escaped
import ( import (
"fmt"
"math" "math"
"sort" "sort"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent`, `vm_debug` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent => vm_debug
_ "github.com/goccy/go-json/internal/encoder/vm_indent"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendEscapedString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
@ -49,7 +17,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for { for {
switch code.Op { switch code.Op {
default: default:
return nil, fmt.Errorf("encoder: opcode %s has not been implemented", code.Op) return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr: case encoder.OpPtr:
p := load(ctxptr, code.Idx) p := load(ctxptr, code.Idx)
code = code.Next code = code.Next
@ -121,7 +89,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpFloat64: case encoder.OpFloat64:
v := ptrToFloat64(load(ctxptr, code.Idx)) v := ptrToFloat64(load(ctxptr, code.Idx))
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, encoder.ErrUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendComma(b) b = appendComma(b)
@ -216,35 +184,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.Ptrs = oldPtrs
ctxptr = ctx.Ptr() ctxptr = ctx.Ptr()
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -327,11 +270,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.Length, uintptr(slice.Len)) store(ctxptr, code.Length, uintptr(slice.Len))
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
if slice.Len > 0 { if slice.Len > 0 {
b = append(b, '[') b = appendArrayHead(ctx, code, b)
code = code.Next code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
} else { } else {
b = append(b, '[', ']', ',') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpSliceElem: case encoder.OpSliceElem:
@ -339,15 +282,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if idx < length { if idx < length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx) data := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, data+idx*size) store(ctxptr, code.Idx, data+idx*size)
} else { } else {
last := len(b) - 1 b = appendArrayEnd(ctx, code, b)
b[last] = ']'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayPtr: case encoder.OpArrayPtr:
@ -369,27 +311,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break break
} }
if code.Length > 0 { if code.Length > 0 {
b = append(b, '[') b = appendArrayHead(ctx, code, b)
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
} else { } else {
b = append(b, '[', ']', ',') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayElem: case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
idx++ idx++
if idx < code.Length { if idx < code.Length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, p+idx*size) store(ctxptr, code.Idx, p+idx*size)
} else { } else {
last := len(b) - 1 b = appendArrayEnd(ctx, code, b)
b[last] = ']'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpMapPtr: case encoder.OpMapPtr:
@ -413,11 +354,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p) uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr) mlen := maplen(uptr)
if mlen <= 0 { if mlen <= 0 {
b = append(b, '{', '}', ',') b = appendEmptyObject(b)
code = code.End.Next code = code.End.Next
break break
} }
b = append(b, '{') b = appendStructHead(b)
iter := mapiterinit(code.Type, uptr) iter := mapiterinit(code.Type, uptr)
ctx.KeepRefs = append(ctx.KeepRefs, iter) ctx.KeepRefs = append(ctx.KeepRefs, iter)
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
@ -428,6 +369,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
mapCtx.Pos = append(mapCtx.Pos, len(b)) mapCtx.Pos = append(mapCtx.Pos, len(b))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -438,16 +381,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length { if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter) ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr) iter := ptrToUnsafePtr(ptr)
store(ctxptr, code.ElemIdx, idx)
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
} else { } else {
last := len(b) - 1 b = appendObjectEnd(ctx, code, b)
b[last] = '}'
b = appendComma(b)
code = code.End.Next code = code.End.Next
} }
} else { } else {
@ -467,8 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
last := len(b) - 1 b = appendColon(b)
b[last] = ':'
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr)) mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -503,12 +444,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice) sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items { for _, item := range mapCtx.Slice.Items {
buf = append(buf, item.Key...) buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
buf[len(buf)-1] = ':'
buf = append(buf, item.Value...)
} }
buf[len(buf)-1] = '}' buf = appendMapEnd(ctx, code, buf)
buf = append(buf, ',')
b = b[:pos[0]] b = b[:pos[0]]
b = append(b, buf...) b = append(b, buf...)
mapCtx.Buf = buf mapCtx.Buf = buf
@ -539,6 +477,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
offsetNum := ptrOffset / uintptrSize offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset oldOffset := ptrOffset
ptrOffset += code.Jmp.CurLen * uintptrSize ptrOffset += code.Jmp.CurLen * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent - 1
newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen
if curlen < newLen { if curlen < newLen {
@ -549,12 +489,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, c.Idx, ptr) store(ctxptr, c.Idx, ptr)
store(ctxptr, c.End.Next.Idx, oldOffset) store(ctxptr, c.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, c, uintptr(oldBaseIndent))
code = c code = c
recursiveLevel++ recursiveLevel++
case encoder.OpRecursiveEnd: case encoder.OpRecursiveEnd:
recursiveLevel-- recursiveLevel--
// restore ctxptr // restore ctxptr
restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx) offset := load(ctxptr, code.Idx)
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -587,7 +529,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if !code.AnonymousHead { if !code.AnonymousHead {
b = appendStructHead(b) b = appendStructHead(b)
} }
if !code.AnonymousKey { if !code.AnonymousKey && len(code.Key) > 0 {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
} }
p += code.Offset p += code.Offset
@ -1883,8 +1825,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset) b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString: case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2404,11 +2345,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
v := ptrToBytes(p + code.Offset) v := ptrToBytes(p + code.Offset)
if v == nil { if len(v) == 0 {
code = code.NextField code = code.NextField
} else { } else {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p)) b = appendByteSlice(b, v)
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
} }
@ -3500,9 +3441,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldIntPtr: case encoder.OpStructFieldIntPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3580,9 +3521,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldUintPtr: case encoder.OpStructFieldUintPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3658,9 +3599,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldFloat32Ptr: case encoder.OpStructFieldFloat32Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3750,9 +3691,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldFloat64Ptr: case encoder.OpStructFieldFloat64Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
b = appendComma(b) b = appendComma(b)
@ -3828,8 +3769,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
case encoder.OpStructFieldStringString: case encoder.OpStructFieldStringString:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset) s := ptrToString(p + code.Offset)
b = appendStructKey(ctx, code, b)
b = appendString(b, string(appendString([]byte{}, s))) b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
@ -3843,9 +3784,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldStringPtr: case encoder.OpStructFieldStringPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3863,9 +3804,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldStringPtrString: case encoder.OpStructFieldStringPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3917,9 +3858,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBoolPtr: case encoder.OpStructFieldBoolPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3937,9 +3878,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBoolPtrString: case encoder.OpStructFieldBoolPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -3976,9 +3917,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldBytesPtr: case encoder.OpStructFieldBytesPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4042,9 +3983,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldNumberPtr: case encoder.OpStructFieldNumberPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4069,9 +4010,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructFieldNumberPtrString: case encoder.OpStructFieldNumberPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
@ -4234,9 +4175,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyArray: case encoder.OpStructFieldOmitEmptyArray:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p += code.Offset p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructFieldArrayPtr: case encoder.OpStructFieldArrayPtr:
@ -4332,17 +4273,21 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p += code.Offset p += code.Offset
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructEnd: case encoder.OpStructFieldOmitEmptyStruct:
last := len(b) - 1 p := load(ctxptr, code.HeadIdx)
if b[last] == ',' { p += code.Offset
b[last] = '}' if ptrToPtr(p) == 0 && code.IsNextOpPtrType {
code = code.NextField
} else { } else {
b = append(b, '}') b = appendStructKey(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, p)
} }
b = appendComma(b)
code = code.Next
case encoder.OpStructAnonymousEnd: case encoder.OpStructAnonymousEnd:
code = code.Next code = code.Next
case encoder.OpStructEnd:
b = appendStructEndSkipLast(ctx, code, b)
code = code.Next
case encoder.OpStructEndInt: case encoder.OpStructEndInt:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
@ -4607,11 +4552,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
case encoder.OpStructEndFloat64: case encoder.OpStructEndFloat64:
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4787,8 +4732,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
v := ptrToString(p) b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendString(b, string(appendString([]byte{}, v)))
} }
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4797,8 +4741,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
if p != 0 { if p != 0 {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
v := ptrToString(p) b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendString(b, string(appendString([]byte{}, v)))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
} else { } else {
b = appendStructEndSkipLast(ctx, code, b) b = appendStructEndSkipLast(ctx, code, b)

View File

@ -0,0 +1,9 @@
package vm_escaped_indent
import (
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent`, `vm_debug` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent => vm_debug
_ "github.com/goccy/go-json/internal/encoder/vm_debug"
)

View File

@ -2,12 +2,43 @@ package vm_escaped_indent
import ( import (
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
appendStructEnd = encoder.AppendStructEndIndent
appendIndent = encoder.AppendIndent
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (indent+escaped): opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
addr := base + idx addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr)) return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +107,89 @@ func appendComma(b []byte) []byte {
return append(b, ',', '\n') return append(b, ',', '\n')
} }
func appendColon(b []byte) []byte {
return append(b, ':', ' ')
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.BaseIndent = oldBaseIndent
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
b = appendIndent(ctx, b, code.Indent+1)
b = append(b, key...)
b[len(b)-2] = ':'
b[len(b)-1] = ' '
return append(b, value...)
}
func appendMapEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b = b[:len(b)-2]
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
return append(b, '}', ',', '\n')
}
func appendArrayHead(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b = append(b, '[', '\n')
return appendIndent(ctx, b, code.Indent+1)
}
func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b = b[:len(b)-2]
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
return append(b, ']', ',', '\n')
}
func appendEmptyArray(b []byte) []byte {
return append(b, '[', ']', ',', '\n')
}
func appendEmptyObject(b []byte) []byte {
return append(b, '{', '}', ',', '\n')
}
func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = '\n'
b = appendIndent(ctx, b, code.Indent-1)
return append(b, '}', ',', '\n')
}
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalJSONIndent(ctx, code, b, v, true) return encoder.AppendMarshalJSONIndent(ctx, code, b, v, true)
} }
@ -109,3 +223,19 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
} }
return appendComma(b) return appendComma(b)
} }
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {
ctx.BaseIndent = int(load(ctxptr, code.Length))
}
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {
store(ctxptr, code.End.Next.Length, indent)
}
func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return appendIndent(ctx, b, code.Indent+1)
}
func appendMapKeyIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return appendIndent(ctx, b, code.Indent)
}

View File

@ -1,47 +1,13 @@
package vm_escaped_indent package vm_escaped_indent
import ( import (
"fmt"
"math" "math"
"sort" "sort"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent`, `vm_debug` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent => vm_debug
_ "github.com/goccy/go-json/internal/encoder/vm_debug"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendEscapedString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
appendStructEnd = encoder.AppendStructEndIndent
appendIndent = encoder.AppendIndent
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
@ -51,7 +17,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for { for {
switch code.Op { switch code.Op {
default: default:
return nil, fmt.Errorf("encoder (indent): opcode %s has not been implemented", code.Op) return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr: case encoder.OpPtr:
p := load(ctxptr, code.Idx) p := load(ctxptr, code.Idx)
code = code.Next code = code.Next
@ -218,38 +184,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.BaseIndent = oldBaseIndent
ctx.Ptrs = oldPtrs
ctxptr = ctx.Ptr() ctxptr = ctx.Ptr()
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -332,12 +270,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.Length, uintptr(slice.Len)) store(ctxptr, code.Length, uintptr(slice.Len))
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
if slice.Len > 0 { if slice.Len > 0 {
b = append(b, '[', '\n') b = appendArrayHead(ctx, code, b)
b = appendIndent(ctx, b, code.Indent+1)
code = code.Next code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
} else { } else {
b = append(b, '[', ']', ',', '\n') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpSliceElem: case encoder.OpSliceElem:
@ -345,17 +282,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if idx < length { if idx < length {
b = appendIndent(ctx, b, code.Indent+1) b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx) data := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, data+idx*size) store(ctxptr, code.Idx, data+idx*size)
} else { } else {
b = b[:len(b)-2] b = appendArrayEnd(ctx, code, b)
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, ']', ',', '\n')
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayPtr: case encoder.OpArrayPtr:
@ -377,30 +311,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break break
} }
if code.Length > 0 { if code.Length > 0 {
b = append(b, '[', '\n') b = appendArrayHead(ctx, code, b)
b = appendIndent(ctx, b, code.Indent+1)
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
} else { } else {
b = append(b, '[', ']', ',', '\n') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayElem: case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
idx++ idx++
if idx < code.Length { if idx < code.Length {
b = appendIndent(ctx, b, code.Indent+1) b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, p+idx*size) store(ctxptr, code.Idx, p+idx*size)
} else { } else {
b = b[:len(b)-2] b = appendArrayEnd(ctx, code, b)
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, ']', ',', '\n')
code = code.End.Next code = code.End.Next
} }
case encoder.OpMapPtr: case encoder.OpMapPtr:
@ -424,7 +354,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p) uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr) mlen := maplen(uptr)
if mlen <= 0 { if mlen <= 0 {
b = append(b, '{', '}', ',', '\n') b = appendEmptyObject(b)
code = code.End.Next code = code.End.Next
break break
} }
@ -440,7 +370,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else { } else {
b = appendIndent(ctx, b, code.Next.Indent) b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -451,7 +381,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length { if idx < length {
b = appendIndent(ctx, b, code.Indent) b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter) ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr) iter := ptrToUnsafePtr(ptr)
@ -459,10 +389,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
code = code.Next code = code.Next
} else { } else {
last := len(b) - 1 b = appendObjectEnd(ctx, code, b)
b[last] = '\n'
b = appendIndent(ctx, b, code.Indent-1)
b = append(b, '}', ',', '\n')
code = code.End.Next code = code.End.Next
} }
} else { } else {
@ -482,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
b = append(b, ':', ' ') b = appendColon(b)
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr)) mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -495,7 +422,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
mapiternext(iter) mapiternext(iter)
code = code.Next code = code.Next
case encoder.OpMapEnd: case encoder.OpMapEnd:
// this operation only used by sorted map // this operation only used by sorted map.
length := int(load(ctxptr, code.Length)) length := int(load(ctxptr, code.Length))
ptr := load(ctxptr, code.MapPos) ptr := load(ctxptr, code.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr)) mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -517,17 +444,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice) sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items { for _, item := range mapCtx.Slice.Items {
buf = appendIndent(ctx, buf, code.Indent+1) buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
buf = append(buf, item.Key...)
buf[len(buf)-2] = ':'
buf[len(buf)-1] = ' '
buf = append(buf, item.Value...)
} }
buf = buf[:len(buf)-2] buf = appendMapEnd(ctx, code, buf)
buf = append(buf, '\n')
buf = appendIndent(ctx, buf, code.Indent)
buf = append(buf, '}', ',', '\n')
b = b[:pos[0]] b = b[:pos[0]]
b = append(b, buf...) b = append(b, buf...)
mapCtx.Buf = buf mapCtx.Buf = buf
@ -570,14 +489,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, c.Idx, ptr) store(ctxptr, c.Idx, ptr)
store(ctxptr, c.End.Next.Idx, oldOffset) store(ctxptr, c.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
store(ctxptr, c.End.Next.Length, uintptr(oldBaseIndent)) storeIndent(ctxptr, c, uintptr(oldBaseIndent))
code = c code = c
recursiveLevel++ recursiveLevel++
case encoder.OpRecursiveEnd: case encoder.OpRecursiveEnd:
recursiveLevel-- recursiveLevel--
// restore ctxptr // restore ctxptr
ctx.BaseIndent = int(load(ctxptr, code.Length)) restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx) offset := load(ctxptr, code.Idx)
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -1382,7 +1301,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
if code.Indirect { if code.Indirect {
p = ptrToNPtr(p, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
} }
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
@ -1562,12 +1481,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
if v == 0 { if v == 0 {
code = code.NextField code = code.NextField
} else { } else {
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendComma(b) b = appendComma(b)
@ -1600,12 +1519,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if !code.AnonymousHead { if !code.AnonymousHead {
b = appendStructHead(b) b = appendStructHead(b)
} }
b = appendStructKey(ctx, code, b)
b = append(b, '"')
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = append(b, '"') b = append(b, '"')
b = appendComma(b) b = appendComma(b)
@ -1638,12 +1557,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
if v == 0 { if v == 0 {
code = code.NextField code = code.NextField
} else { } else {
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = append(b, '"') b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
@ -1906,8 +1825,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
v := ptrToString(p + code.Offset) b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendString(b, string(appendString([]byte{}, v)))
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString: case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2251,12 +2169,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
if code.Indirect { if code.Indirect {
p = ptrToNPtr(p, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
} }
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
b = appendBool(b, ptrToBool(p+code.Offset)) b = appendBool(b, ptrToBool(p))
} }
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
@ -3778,13 +3696,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { b = appendComma(b)
v := ptrToFloat64(p) code = code.Next
if math.IsInf(v, 0) || math.IsNaN(v) { break
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
} }
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructFieldOmitEmptyFloat64Ptr: case encoder.OpStructFieldOmitEmptyFloat64Ptr:
@ -3807,11 +3727,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
b = append(b, '"')
v := ptrToFloat64(p) v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = append(b, '"') b = append(b, '"')
} }
@ -4348,9 +4268,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField code = code.NextField
} }
case encoder.OpStructFieldStruct: case encoder.OpStructFieldStruct:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p += code.Offset p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyStruct: case encoder.OpStructFieldOmitEmptyStruct:
@ -4366,25 +4286,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpStructAnonymousEnd: case encoder.OpStructAnonymousEnd:
code = code.Next code = code.Next
case encoder.OpStructEnd: case encoder.OpStructEnd:
last := len(b) - 1 b = appendStructEndSkipLast(ctx, code, b)
if b[last-1] == '{' {
b[last] = '}'
b = appendComma(b)
code = code.Next
break
}
if b[last] == '\n' {
// to remove ',' and '\n' characters
b = b[:len(b)-2]
}
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, '}')
b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructEndInt: case encoder.OpStructEndInt:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendInt(b, ptrToUint64(p+code.Offset), code) b = appendInt(b, ptrToUint64(p+code.Offset), code)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4471,8 +4377,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndUint: case encoder.OpStructEndUint:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendUint(b, ptrToUint64(p+code.Offset), code) b = appendUint(b, ptrToUint64(p+code.Offset), code)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4559,8 +4465,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndFloat32: case encoder.OpStructEndFloat32:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendFloat32(b, ptrToFloat32(p+code.Offset)) b = appendFloat32(b, ptrToFloat32(p+code.Offset))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4645,12 +4551,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndFloat64: case encoder.OpStructEndFloat64:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4658,10 +4564,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if v != 0 { if v != 0 {
b = appendStructKey(ctx, code, b)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
} else { } else {
@ -4684,11 +4590,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if v != 0 { if v != 0 {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = append(b, '"') b = append(b, '"')
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
@ -4702,13 +4608,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { b = appendStructEnd(ctx, code, b)
v := ptrToFloat64(p) code = code.Next
if math.IsInf(v, 0) || math.IsNaN(v) { break
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
} }
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
case encoder.OpStructEndOmitEmptyFloat64Ptr: case encoder.OpStructEndOmitEmptyFloat64Ptr:
@ -4748,11 +4656,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
if p != 0 { if p != 0 {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = append(b, '"')
v := ptrToFloat64(p) v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = append(b, '"') b = append(b, '"')
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
@ -4761,8 +4669,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndString: case encoder.OpStructEndString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendString(b, ptrToString(p+code.Offset)) b = appendString(b, ptrToString(p+code.Offset))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4840,8 +4748,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndBool: case encoder.OpStructEndBool:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendBool(b, ptrToBool(p+code.Offset)) b = appendBool(b, ptrToBool(p+code.Offset))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4926,8 +4834,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndBytes: case encoder.OpStructEndBytes:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p+code.Offset)) b = appendByteSlice(b, ptrToBytes(p+code.Offset))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4965,8 +4873,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndNumber: case encoder.OpStructEndNumber:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
bb, err := appendNumber(b, ptrToNumber(p+code.Offset)) bb, err := appendNumber(b, ptrToNumber(p+code.Offset))
if err != nil { if err != nil {
return nil, err return nil, err
@ -4988,9 +4896,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndNumberString: case encoder.OpStructEndNumberString:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = append(b, '"') b = append(b, '"')
p := load(ctxptr, code.HeadIdx)
bb, err := appendNumber(b, ptrToNumber(p+code.Offset)) bb, err := appendNumber(b, ptrToNumber(p+code.Offset))
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -0,0 +1,9 @@
package vm_indent
import (
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent`, `vm_debug` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent => vm_debug
_ "github.com/goccy/go-json/internal/encoder/vm_escaped_indent"
)

View File

@ -2,12 +2,43 @@ package vm_indent
import ( import (
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
appendStructEnd = encoder.AppendStructEndIndent
appendIndent = encoder.AppendIndent
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
addr := base + idx addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr)) return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,8 +107,87 @@ func appendComma(b []byte) []byte {
return append(b, ',', '\n') return append(b, ',', '\n')
} }
func appendStructHead(b []byte) []byte { func appendColon(b []byte) []byte {
return append(b, '{', '\n') return append(b, ':', ' ')
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.BaseIndent = oldBaseIndent
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
b = appendIndent(ctx, b, code.Indent+1)
b = append(b, key...)
b[len(b)-2] = ':'
b[len(b)-1] = ' '
return append(b, value...)
}
func appendMapEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b = b[:len(b)-2]
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
return append(b, '}', ',', '\n')
}
func appendArrayHead(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b = append(b, '[', '\n')
return appendIndent(ctx, b, code.Indent+1)
}
func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b = b[:len(b)-2]
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
return append(b, ']', ',', '\n')
}
func appendEmptyArray(b []byte) []byte {
return append(b, '[', ']', ',', '\n')
}
func appendEmptyObject(b []byte) []byte {
return append(b, '{', '}', ',', '\n')
}
func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = '\n'
b = appendIndent(ctx, b, code.Indent-1)
return append(b, '}', ',', '\n')
} }
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
@ -88,6 +198,10 @@ func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, e
return encoder.AppendMarshalTextIndent(code, b, v, false) return encoder.AppendMarshalTextIndent(code, b, v, false)
} }
func appendStructHead(b []byte) []byte {
return append(b, '{', '\n')
}
func appendStructKey(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { func appendStructKey(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
b = appendIndent(ctx, b, code.Indent) b = appendIndent(ctx, b, code.Indent)
b = append(b, code.Key...) b = append(b, code.Key...)
@ -109,3 +223,19 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
} }
return appendComma(b) return appendComma(b)
} }
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {
ctx.BaseIndent = int(load(ctxptr, code.Length))
}
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {
store(ctxptr, code.End.Next.Length, indent)
}
func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return appendIndent(ctx, b, code.Indent+1)
}
func appendMapKeyIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return appendIndent(ctx, b, code.Indent)
}

View File

@ -1,47 +1,13 @@
package vm_indent package vm_indent
import ( import (
"fmt"
"math" "math"
"sort" "sort"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent`, `vm_debug` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent => vm_debug
_ "github.com/goccy/go-json/internal/encoder/vm_escaped_indent"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
appendStructEnd = encoder.AppendStructEndIndent
appendIndent = encoder.AppendIndent
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) { func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
recursiveLevel := 0 recursiveLevel := 0
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
@ -51,7 +17,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for { for {
switch code.Op { switch code.Op {
default: default:
return nil, fmt.Errorf("encoder (indent): opcode %s has not been implemented", code.Op) return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr: case encoder.OpPtr:
p := load(ctxptr, code.Idx) p := load(ctxptr, code.Idx)
code = code.Next code = code.Next
@ -218,38 +184,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.BaseIndent = oldBaseIndent
ctx.Ptrs = oldPtrs
ctxptr = ctx.Ptr() ctxptr = ctx.Ptr()
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -332,12 +270,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.Length, uintptr(slice.Len)) store(ctxptr, code.Length, uintptr(slice.Len))
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
if slice.Len > 0 { if slice.Len > 0 {
b = append(b, '[', '\n') b = appendArrayHead(ctx, code, b)
b = appendIndent(ctx, b, code.Indent+1)
code = code.Next code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data)) store(ctxptr, code.Idx, uintptr(slice.Data))
} else { } else {
b = append(b, '[', ']', ',', '\n') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpSliceElem: case encoder.OpSliceElem:
@ -345,17 +282,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length) length := load(ctxptr, code.Length)
idx++ idx++
if idx < length { if idx < length {
b = appendIndent(ctx, b, code.Indent+1) b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx) data := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, data+idx*size) store(ctxptr, code.Idx, data+idx*size)
} else { } else {
b = b[:len(b)-2] b = appendArrayEnd(ctx, code, b)
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, ']', ',', '\n')
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayPtr: case encoder.OpArrayPtr:
@ -377,30 +311,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break break
} }
if code.Length > 0 { if code.Length > 0 {
b = append(b, '[', '\n') b = appendArrayHead(ctx, code, b)
b = appendIndent(ctx, b, code.Indent+1)
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
} else { } else {
b = append(b, '[', ']', ',', '\n') b = appendEmptyArray(b)
code = code.End.Next code = code.End.Next
} }
case encoder.OpArrayElem: case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx) idx := load(ctxptr, code.ElemIdx)
idx++ idx++
if idx < code.Length { if idx < code.Length {
b = appendIndent(ctx, b, code.Indent+1) b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
size := code.Size size := code.Size
code = code.Next code = code.Next
store(ctxptr, code.Idx, p+idx*size) store(ctxptr, code.Idx, p+idx*size)
} else { } else {
b = b[:len(b)-2] b = appendArrayEnd(ctx, code, b)
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, ']', ',', '\n')
code = code.End.Next code = code.End.Next
} }
case encoder.OpMapPtr: case encoder.OpMapPtr:
@ -424,7 +354,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p) uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr) mlen := maplen(uptr)
if mlen <= 0 { if mlen <= 0 {
b = append(b, '{', '}', ',', '\n') b = appendEmptyObject(b)
code = code.End.Next code = code.End.Next
break break
} }
@ -440,7 +370,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx)) ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx))) store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else { } else {
b = appendIndent(ctx, b, code.Next.Indent) b = appendMapKeyIndent(ctx, code.Next, b)
} }
key := mapiterkey(iter) key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
@ -451,7 +381,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++ idx++
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length { if idx < length {
b = appendIndent(ctx, b, code.Indent) b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx) store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter) ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr) iter := ptrToUnsafePtr(ptr)
@ -459,10 +389,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, code.Next.Idx, uintptr(key)) store(ctxptr, code.Next.Idx, uintptr(key))
code = code.Next code = code.Next
} else { } else {
last := len(b) - 1 b = appendObjectEnd(ctx, code, b)
b[last] = '\n'
b = appendIndent(ctx, b, code.Indent-1)
b = append(b, '}', ',', '\n')
code = code.End.Next code = code.End.Next
} }
} else { } else {
@ -482,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
case encoder.OpMapValue: case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 { if (opt & encoder.UnorderedMapOption) != 0 {
b = append(b, ':', ' ') b = appendColon(b)
} else { } else {
ptr := load(ctxptr, code.End.MapPos) ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr)) mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -495,7 +422,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
mapiternext(iter) mapiternext(iter)
code = code.Next code = code.Next
case encoder.OpMapEnd: case encoder.OpMapEnd:
// this operation only used by sorted map // this operation only used by sorted map.
length := int(load(ctxptr, code.Length)) length := int(load(ctxptr, code.Length))
ptr := load(ctxptr, code.MapPos) ptr := load(ctxptr, code.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr)) mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -517,17 +444,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice) sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items { for _, item := range mapCtx.Slice.Items {
buf = appendIndent(ctx, buf, code.Indent+1) buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
buf = append(buf, item.Key...)
buf[len(buf)-2] = ':'
buf[len(buf)-1] = ' '
buf = append(buf, item.Value...)
} }
buf = buf[:len(buf)-2] buf = appendMapEnd(ctx, code, buf)
buf = append(buf, '\n')
buf = appendIndent(ctx, buf, code.Indent)
buf = append(buf, '}', ',', '\n')
b = b[:pos[0]] b = b[:pos[0]]
b = append(b, buf...) b = append(b, buf...)
mapCtx.Buf = buf mapCtx.Buf = buf
@ -570,14 +489,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
store(ctxptr, c.Idx, ptr) store(ctxptr, c.Idx, ptr)
store(ctxptr, c.End.Next.Idx, oldOffset) store(ctxptr, c.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
store(ctxptr, c.End.Next.Length, uintptr(oldBaseIndent)) storeIndent(ctxptr, c, uintptr(oldBaseIndent))
code = c code = c
recursiveLevel++ recursiveLevel++
case encoder.OpRecursiveEnd: case encoder.OpRecursiveEnd:
recursiveLevel-- recursiveLevel--
// restore ctxptr // restore ctxptr
ctx.BaseIndent = int(load(ctxptr, code.Length)) restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx) offset := load(ctxptr, code.Idx)
ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1] ctx.SeenPtr = ctx.SeenPtr[:len(ctx.SeenPtr)-1]
@ -1382,7 +1301,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
if code.Indirect { if code.Indirect {
p = ptrToNPtr(p, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
} }
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
@ -1562,12 +1481,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
if v == 0 { if v == 0 {
code = code.NextField code = code.NextField
} else { } else {
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendComma(b) b = appendComma(b)
@ -1600,12 +1519,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if !code.AnonymousHead { if !code.AnonymousHead {
b = appendStructHead(b) b = appendStructHead(b)
} }
b = appendStructKey(ctx, code, b)
b = append(b, '"')
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = append(b, '"') b = append(b, '"')
b = appendComma(b) b = appendComma(b)
@ -1638,12 +1557,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
if v == 0 { if v == 0 {
code = code.NextField code = code.NextField
} else { } else {
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = append(b, '"') b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
@ -1906,8 +1825,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b) b = appendStructHead(b)
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
v := ptrToString(p + code.Offset) b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendString(b, string(appendString([]byte{}, v)))
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString: case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2251,12 +2169,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
if code.Indirect { if code.Indirect {
p = ptrToNPtr(p, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
} }
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
b = appendBool(b, ptrToBool(p+code.Offset)) b = appendBool(b, ptrToBool(p))
} }
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
@ -3778,13 +3696,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { b = appendComma(b)
v := ptrToFloat64(p) code = code.Next
if math.IsInf(v, 0) || math.IsNaN(v) { break
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
} }
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructFieldOmitEmptyFloat64Ptr: case encoder.OpStructFieldOmitEmptyFloat64Ptr:
@ -3807,11 +3727,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { } else {
b = append(b, '"')
v := ptrToFloat64(p) v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = append(b, '"') b = append(b, '"')
} }
@ -4348,9 +4268,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField code = code.NextField
} }
case encoder.OpStructFieldStruct: case encoder.OpStructFieldStruct:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
p += code.Offset p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next code = code.Next
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyStruct: case encoder.OpStructFieldOmitEmptyStruct:
@ -4366,25 +4286,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpStructAnonymousEnd: case encoder.OpStructAnonymousEnd:
code = code.Next code = code.Next
case encoder.OpStructEnd: case encoder.OpStructEnd:
last := len(b) - 1 b = appendStructEndSkipLast(ctx, code, b)
if b[last-1] == '{' {
b[last] = '}'
b = appendComma(b)
code = code.Next
break
}
if b[last] == '\n' {
// to remove ',' and '\n' characters
b = b[:len(b)-2]
}
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, '}')
b = appendComma(b)
code = code.Next code = code.Next
case encoder.OpStructEndInt: case encoder.OpStructEndInt:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendInt(b, ptrToUint64(p+code.Offset), code) b = appendInt(b, ptrToUint64(p+code.Offset), code)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4471,8 +4377,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndUint: case encoder.OpStructEndUint:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendUint(b, ptrToUint64(p+code.Offset), code) b = appendUint(b, ptrToUint64(p+code.Offset), code)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4559,8 +4465,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndFloat32: case encoder.OpStructEndFloat32:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendFloat32(b, ptrToFloat32(p+code.Offset)) b = appendFloat32(b, ptrToFloat32(p+code.Offset))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4645,12 +4551,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndFloat64: case encoder.OpStructEndFloat64:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4658,10 +4564,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if v != 0 { if v != 0 {
b = appendStructKey(ctx, code, b)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
} else { } else {
@ -4684,11 +4590,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset) v := ptrToFloat64(p + code.Offset)
if v != 0 { if v != 0 {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = append(b, '"') b = append(b, '"')
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
@ -4702,13 +4608,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
} else { b = appendStructEnd(ctx, code, b)
v := ptrToFloat64(p) code = code.Next
if math.IsInf(v, 0) || math.IsNaN(v) { break
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
} }
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
case encoder.OpStructEndOmitEmptyFloat64Ptr: case encoder.OpStructEndOmitEmptyFloat64Ptr:
@ -4748,11 +4656,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p = ptrToNPtr(p+code.Offset, code.PtrNum) p = ptrToNPtr(p+code.Offset, code.PtrNum)
if p != 0 { if p != 0 {
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = append(b, '"')
v := ptrToFloat64(p) v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) { if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v) return nil, errUnsupportedFloat(v)
} }
b = append(b, '"')
b = appendFloat64(b, v) b = appendFloat64(b, v)
b = append(b, '"') b = append(b, '"')
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
@ -4761,8 +4669,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndString: case encoder.OpStructEndString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendString(b, ptrToString(p+code.Offset)) b = appendString(b, ptrToString(p+code.Offset))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4840,8 +4748,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndBool: case encoder.OpStructEndBool:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendBool(b, ptrToBool(p+code.Offset)) b = appendBool(b, ptrToBool(p+code.Offset))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4926,8 +4834,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndBytes: case encoder.OpStructEndBytes:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p+code.Offset)) b = appendByteSlice(b, ptrToBytes(p+code.Offset))
b = appendStructEnd(ctx, code, b) b = appendStructEnd(ctx, code, b)
code = code.Next code = code.Next
@ -4965,8 +4873,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndNumber: case encoder.OpStructEndNumber:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx) p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
bb, err := appendNumber(b, ptrToNumber(p+code.Offset)) bb, err := appendNumber(b, ptrToNumber(p+code.Offset))
if err != nil { if err != nil {
return nil, err return nil, err
@ -4988,9 +4896,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
code = code.Next code = code.Next
case encoder.OpStructEndNumberString: case encoder.OpStructEndNumberString:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b) b = appendStructKey(ctx, code, b)
b = append(b, '"') b = append(b, '"')
p := load(ctxptr, code.HeadIdx)
bb, err := appendNumber(b, ptrToNumber(p+code.Offset)) bb, err := appendNumber(b, ptrToNumber(p+code.Offset))
if err != nil { if err != nil {
return nil, err return nil, err