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++
}
ctx = ctx.decIndent()
structEndCode := &Opcode{
Op: OpStructEnd,
@ -1459,6 +1458,8 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
Next: newEndOp(ctx),
}
ctx = ctx.decIndent()
// no struct field
if head == nil {
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 (
"encoding/json"
"fmt"
"unsafe"
"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 errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr {
addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +105,57 @@ func appendComma(b []byte) []byte {
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) {
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)
}
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return append(b, code.Key...)
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 {
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 {
return append(b, '}', ',')
}
@ -104,3 +208,8 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
}
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
import (
"fmt"
"math"
"sort"
"unsafe"
"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) {
recursiveLevel := 0
ptrOffset := uintptr(0)
@ -49,7 +17,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for {
switch code.Op {
default:
return nil, fmt.Errorf("encoder: opcode %s has not been implemented", code.Op)
return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr:
p := load(ctxptr, code.Idx)
code = code.Next
@ -121,7 +89,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpFloat64:
v := ptrToFloat64(load(ctxptr, code.Idx))
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, encoder.ErrUnsupportedFloat(v)
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b)
@ -216,35 +184,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
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
ctxptr = ctx.Ptr()
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.Idx, uintptr(slice.Data))
if slice.Len > 0 {
b = append(b, '[')
b = appendArrayHead(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data))
} else {
b = append(b, '[', ']', ',')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpSliceElem:
@ -339,15 +282,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length)
idx++
if idx < length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, data+idx*size)
} else {
last := len(b) - 1
b[last] = ']'
b = appendComma(b)
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpArrayPtr:
@ -369,27 +311,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break
}
if code.Length > 0 {
b = append(b, '[')
b = appendArrayHead(ctx, code, b)
store(ctxptr, code.ElemIdx, 0)
code = code.Next
store(ctxptr, code.Idx, p)
} else {
b = append(b, '[', ']', ',')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx)
idx++
if idx < code.Length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, p+idx*size)
} else {
last := len(b) - 1
b[last] = ']'
b = appendComma(b)
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpMapPtr:
@ -413,11 +354,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr)
if mlen <= 0 {
b = append(b, '{', '}', ',')
b = appendEmptyObject(b)
code = code.End.Next
break
}
b = append(b, '{')
b = appendStructHead(b)
iter := mapiterinit(code.Type, uptr)
ctx.KeepRefs = append(ctx.KeepRefs, iter)
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))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key))
@ -438,16 +381,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr)
store(ctxptr, code.ElemIdx, idx)
key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key))
code = code.Next
} else {
last := len(b) - 1
b[last] = '}'
b = appendComma(b)
b = appendObjectEnd(ctx, code, b)
code = code.End.Next
}
} else {
@ -467,8 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
last := len(b) - 1
b[last] = ':'
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -503,12 +444,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items {
buf = append(buf, item.Key...)
buf[len(buf)-1] = ':'
buf = append(buf, item.Value...)
buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
}
buf[len(buf)-1] = '}'
buf = append(buf, ',')
buf = appendMapEnd(ctx, code, buf)
b = b[:pos[0]]
b = append(b, buf...)
mapCtx.Buf = buf
@ -539,6 +477,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset
ptrOffset += code.Jmp.CurLen * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent - 1
newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen
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.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, c, uintptr(oldBaseIndent))
code = c
recursiveLevel++
case encoder.OpRecursiveEnd:
recursiveLevel--
// restore ctxptr
restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx)
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 {
b = appendStructHead(b)
}
if !code.AnonymousKey {
if !code.AnonymousKey && len(code.Key) > 0 {
b = appendStructKey(ctx, code, b)
}
p += code.Offset
@ -1883,8 +1825,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset)
b = appendString(b, string(appendString([]byte{}, s)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendComma(b)
code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2404,11 +2345,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
v := ptrToBytes(p + code.Offset)
if v == nil {
if len(v) == 0 {
code = code.NextField
} else {
b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p))
b = appendByteSlice(b, v)
b = appendComma(b)
code = code.Next
}
@ -3500,9 +3441,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldIntPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3580,9 +3521,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldUintPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3658,9 +3599,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldFloat32Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3750,9 +3691,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldFloat64Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
b = appendComma(b)
@ -3828,8 +3769,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
case encoder.OpStructFieldStringString:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset)
b = appendStructKey(ctx, code, b)
b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b)
code = code.Next
@ -3843,9 +3784,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldStringPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3863,9 +3804,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldStringPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3917,9 +3858,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBoolPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3937,9 +3878,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBoolPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3976,9 +3917,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBytesPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4042,9 +3983,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldNumberPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4069,9 +4010,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldNumberPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4234,9 +4175,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyArray:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldArrayPtr:
@ -4332,17 +4273,21 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p += code.Offset
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructEnd:
last := len(b) - 1
if b[last] == ',' {
b[last] = '}'
case encoder.OpStructFieldOmitEmptyStruct:
p := load(ctxptr, code.HeadIdx)
p += code.Offset
if ptrToPtr(p) == 0 && code.IsNextOpPtrType {
code = code.NextField
} 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:
code = code.Next
case encoder.OpStructEnd:
b = appendStructEndSkipLast(ctx, code, b)
code = code.Next
case encoder.OpStructEndInt:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
@ -4607,11 +4552,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
case encoder.OpStructEndFloat64:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4787,8 +4732,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 {
b = appendNull(b)
} else {
v := ptrToString(p)
b = appendString(b, string(appendString([]byte{}, v)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
}
b = appendStructEnd(ctx, code, b)
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)
if p != 0 {
b = appendStructKey(ctx, code, b)
v := ptrToString(p)
b = appendString(b, string(appendString([]byte{}, v)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendStructEnd(ctx, code, b)
} else {
b = appendStructEndSkipLast(ctx, code, b)

View File

@ -2,12 +2,41 @@ package vm_debug
import (
"encoding/json"
"fmt"
"unsafe"
"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 errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (debug): opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr {
addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +105,57 @@ func appendComma(b []byte) []byte {
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) {
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)
}
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return append(b, code.Key...)
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 {
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 {
return append(b, '}', ',')
}
@ -104,3 +208,8 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
}
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"
"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) {
recursiveLevel := 0
ptrOffset := uintptr(0)
@ -62,7 +37,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for {
switch code.Op {
default:
return nil, fmt.Errorf("encoder: opcode %s has not been implemented", code.Op)
return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr:
p := load(ctxptr, code.Idx)
code = code.Next
@ -134,7 +109,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpFloat64:
v := ptrToFloat64(load(ctxptr, code.Idx))
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, encoder.ErrUnsupportedFloat(v)
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b)
@ -229,35 +204,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
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
ctxptr = ctx.Ptr()
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.Idx, uintptr(slice.Data))
if slice.Len > 0 {
b = append(b, '[')
b = appendArrayHead(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data))
} else {
b = append(b, '[', ']', ',')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpSliceElem:
@ -352,15 +302,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length)
idx++
if idx < length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, data+idx*size)
} else {
last := len(b) - 1
b[last] = ']'
b = appendComma(b)
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpArrayPtr:
@ -382,27 +331,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break
}
if code.Length > 0 {
b = append(b, '[')
b = appendArrayHead(ctx, code, b)
store(ctxptr, code.ElemIdx, 0)
code = code.Next
store(ctxptr, code.Idx, p)
} else {
b = append(b, '[', ']', ',')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx)
idx++
if idx < code.Length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, p+idx*size)
} else {
last := len(b) - 1
b[last] = ']'
b = appendComma(b)
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpMapPtr:
@ -426,11 +374,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr)
if mlen <= 0 {
b = append(b, '{', '}', ',')
b = appendEmptyObject(b)
code = code.End.Next
break
}
b = append(b, '{')
b = appendStructHead(b)
iter := mapiterinit(code.Type, uptr)
ctx.KeepRefs = append(ctx.KeepRefs, iter)
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))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key))
@ -451,16 +401,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr)
store(ctxptr, code.ElemIdx, idx)
key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key))
code = code.Next
} else {
last := len(b) - 1
b[last] = '}'
b = appendComma(b)
b = appendObjectEnd(ctx, code, b)
code = code.End.Next
}
} else {
@ -480,8 +429,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
last := len(b) - 1
b[last] = ':'
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -516,12 +464,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items {
buf = append(buf, item.Key...)
buf[len(buf)-1] = ':'
buf = append(buf, item.Value...)
buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
}
buf[len(buf)-1] = '}'
buf = append(buf, ',')
buf = appendMapEnd(ctx, code, buf)
b = b[:pos[0]]
b = append(b, buf...)
mapCtx.Buf = buf
@ -552,6 +497,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset
ptrOffset += code.Jmp.CurLen * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent - 1
newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen
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.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, c, uintptr(oldBaseIndent))
code = c
recursiveLevel++
case encoder.OpRecursiveEnd:
recursiveLevel--
// restore ctxptr
restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx)
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 {
b = appendStructHead(b)
}
if !code.AnonymousKey {
if !code.AnonymousKey && len(code.Key) > 0 {
b = appendStructKey(ctx, code, b)
}
p += code.Offset
@ -1896,8 +1845,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset)
b = appendString(b, string(appendString([]byte{}, s)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendComma(b)
code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2417,11 +2365,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
v := ptrToBytes(p + code.Offset)
if v == nil {
if len(v) == 0 {
code = code.NextField
} else {
b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p))
b = appendByteSlice(b, v)
b = appendComma(b)
code = code.Next
}
@ -3513,9 +3461,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldIntPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3593,9 +3541,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldUintPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3671,9 +3619,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldFloat32Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3763,9 +3711,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldFloat64Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
b = appendComma(b)
@ -3841,8 +3789,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
case encoder.OpStructFieldStringString:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset)
b = appendStructKey(ctx, code, b)
b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b)
code = code.Next
@ -3856,9 +3804,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldStringPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3876,9 +3824,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldStringPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3930,9 +3878,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBoolPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3950,9 +3898,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBoolPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3989,9 +3937,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBytesPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4055,9 +4003,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldNumberPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4082,9 +4030,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldNumberPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4247,9 +4195,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyArray:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldArrayPtr:
@ -4345,17 +4293,21 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p += code.Offset
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructEnd:
last := len(b) - 1
if b[last] == ',' {
b[last] = '}'
case encoder.OpStructFieldOmitEmptyStruct:
p := load(ctxptr, code.HeadIdx)
p += code.Offset
if ptrToPtr(p) == 0 && code.IsNextOpPtrType {
code = code.NextField
} 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:
code = code.Next
case encoder.OpStructEnd:
b = appendStructEndSkipLast(ctx, code, b)
code = code.Next
case encoder.OpStructEndInt:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
@ -4620,11 +4572,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
case encoder.OpStructEndFloat64:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4800,8 +4752,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 {
b = appendNull(b)
} else {
v := ptrToString(p)
b = appendString(b, string(appendString([]byte{}, v)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
}
b = appendStructEnd(ctx, code, b)
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)
if p != 0 {
b = appendStructKey(ctx, code, b)
v := ptrToString(p)
b = appendString(b, string(appendString([]byte{}, v)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendStructEnd(ctx, code, b)
} else {
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 (
"encoding/json"
"fmt"
"unsafe"
"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.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 {
addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +105,57 @@ func appendComma(b []byte) []byte {
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) {
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)
}
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 {
return append(b, '{')
}
@ -104,3 +208,8 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
}
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
import (
"fmt"
"math"
"sort"
"unsafe"
"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) {
recursiveLevel := 0
ptrOffset := uintptr(0)
@ -49,7 +17,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for {
switch code.Op {
default:
return nil, fmt.Errorf("encoder: opcode %s has not been implemented", code.Op)
return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr:
p := load(ctxptr, code.Idx)
code = code.Next
@ -121,7 +89,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpFloat64:
v := ptrToFloat64(load(ctxptr, code.Idx))
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, encoder.ErrUnsupportedFloat(v)
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b)
@ -216,35 +184,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
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
ctxptr = ctx.Ptr()
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.Idx, uintptr(slice.Data))
if slice.Len > 0 {
b = append(b, '[')
b = appendArrayHead(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data))
} else {
b = append(b, '[', ']', ',')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpSliceElem:
@ -339,15 +282,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length)
idx++
if idx < length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, data+idx*size)
} else {
last := len(b) - 1
b[last] = ']'
b = appendComma(b)
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpArrayPtr:
@ -369,27 +311,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break
}
if code.Length > 0 {
b = append(b, '[')
b = appendArrayHead(ctx, code, b)
store(ctxptr, code.ElemIdx, 0)
code = code.Next
store(ctxptr, code.Idx, p)
} else {
b = append(b, '[', ']', ',')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx)
idx++
if idx < code.Length {
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, p+idx*size)
} else {
last := len(b) - 1
b[last] = ']'
b = appendComma(b)
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpMapPtr:
@ -413,11 +354,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr)
if mlen <= 0 {
b = append(b, '{', '}', ',')
b = appendEmptyObject(b)
code = code.End.Next
break
}
b = append(b, '{')
b = appendStructHead(b)
iter := mapiterinit(code.Type, uptr)
ctx.KeepRefs = append(ctx.KeepRefs, iter)
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))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key))
@ -438,16 +381,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length {
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter)
iter := ptrToUnsafePtr(ptr)
store(ctxptr, code.ElemIdx, idx)
key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key))
code = code.Next
} else {
last := len(b) - 1
b[last] = '}'
b = appendComma(b)
b = appendObjectEnd(ctx, code, b)
code = code.End.Next
}
} else {
@ -467,8 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
last := len(b) - 1
b[last] = ':'
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -503,12 +444,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items {
buf = append(buf, item.Key...)
buf[len(buf)-1] = ':'
buf = append(buf, item.Value...)
buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
}
buf[len(buf)-1] = '}'
buf = append(buf, ',')
buf = appendMapEnd(ctx, code, buf)
b = b[:pos[0]]
b = append(b, buf...)
mapCtx.Buf = buf
@ -539,6 +477,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset
ptrOffset += code.Jmp.CurLen * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent - 1
newLen := offsetNum + code.Jmp.CurLen + code.Jmp.NextLen
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.End.Next.Idx, oldOffset)
store(ctxptr, c.End.Next.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, c, uintptr(oldBaseIndent))
code = c
recursiveLevel++
case encoder.OpRecursiveEnd:
recursiveLevel--
// restore ctxptr
restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx)
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 {
b = appendStructHead(b)
}
if !code.AnonymousKey {
if !code.AnonymousKey && len(code.Key) > 0 {
b = appendStructKey(ctx, code, b)
}
p += code.Offset
@ -1883,8 +1825,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset)
b = appendString(b, string(appendString([]byte{}, s)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendComma(b)
code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2404,11 +2345,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
v := ptrToBytes(p + code.Offset)
if v == nil {
if len(v) == 0 {
code = code.NextField
} else {
b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p))
b = appendByteSlice(b, v)
b = appendComma(b)
code = code.Next
}
@ -3500,9 +3441,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldIntPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3580,9 +3521,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldUintPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3658,9 +3599,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldFloat32Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3750,9 +3691,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldFloat64Ptr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
b = appendComma(b)
@ -3828,8 +3769,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
case encoder.OpStructFieldStringString:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
s := ptrToString(p + code.Offset)
b = appendStructKey(ctx, code, b)
b = appendString(b, string(appendString([]byte{}, s)))
b = appendComma(b)
code = code.Next
@ -3843,9 +3784,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldStringPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3863,9 +3804,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldStringPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3917,9 +3858,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBoolPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3937,9 +3878,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBoolPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -3976,9 +3917,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldBytesPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4042,9 +3983,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldNumberPtr:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4069,9 +4010,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructFieldNumberPtrString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
@ -4234,9 +4175,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyArray:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldArrayPtr:
@ -4332,17 +4273,21 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p += code.Offset
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructEnd:
last := len(b) - 1
if b[last] == ',' {
b[last] = '}'
case encoder.OpStructFieldOmitEmptyStruct:
p := load(ctxptr, code.HeadIdx)
p += code.Offset
if ptrToPtr(p) == 0 && code.IsNextOpPtrType {
code = code.NextField
} 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:
code = code.Next
case encoder.OpStructEnd:
b = appendStructEndSkipLast(ctx, code, b)
code = code.Next
case encoder.OpStructEndInt:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
@ -4607,11 +4552,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
case encoder.OpStructEndFloat64:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4787,8 +4732,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 {
b = appendNull(b)
} else {
v := ptrToString(p)
b = appendString(b, string(appendString([]byte{}, v)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
}
b = appendStructEnd(ctx, code, b)
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)
if p != 0 {
b = appendStructKey(ctx, code, b)
v := ptrToString(p)
b = appendString(b, string(appendString([]byte{}, v)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p))))
b = appendStructEnd(ctx, code, b)
} else {
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 (
"encoding/json"
"fmt"
"unsafe"
"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
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 {
addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +107,89 @@ func appendComma(b []byte) []byte {
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) {
return encoder.AppendMarshalJSONIndent(ctx, code, b, v, true)
}
@ -109,3 +223,19 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
}
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
import (
"fmt"
"math"
"sort"
"unsafe"
"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) {
recursiveLevel := 0
ptrOffset := uintptr(0)
@ -51,7 +17,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for {
switch code.Op {
default:
return nil, fmt.Errorf("encoder (indent): opcode %s has not been implemented", code.Op)
return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr:
p := load(ctxptr, code.Idx)
code = code.Next
@ -218,38 +184,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
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
ctxptr = ctx.Ptr()
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.Idx, uintptr(slice.Data))
if slice.Len > 0 {
b = append(b, '[', '\n')
b = appendIndent(ctx, b, code.Indent+1)
b = appendArrayHead(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data))
} else {
b = append(b, '[', ']', ',', '\n')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpSliceElem:
@ -345,17 +282,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length)
idx++
if idx < length {
b = appendIndent(ctx, b, code.Indent+1)
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, data+idx*size)
} else {
b = b[:len(b)-2]
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, ']', ',', '\n')
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpArrayPtr:
@ -377,30 +311,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break
}
if code.Length > 0 {
b = append(b, '[', '\n')
b = appendIndent(ctx, b, code.Indent+1)
b = appendArrayHead(ctx, code, b)
store(ctxptr, code.ElemIdx, 0)
code = code.Next
store(ctxptr, code.Idx, p)
} else {
b = append(b, '[', ']', ',', '\n')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx)
idx++
if idx < code.Length {
b = appendIndent(ctx, b, code.Indent+1)
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, p+idx*size)
} else {
b = b[:len(b)-2]
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, ']', ',', '\n')
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpMapPtr:
@ -424,7 +354,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr)
if mlen <= 0 {
b = append(b, '{', '}', ',', '\n')
b = appendEmptyObject(b)
code = code.End.Next
break
}
@ -440,7 +370,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendIndent(ctx, b, code.Next.Indent)
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key))
@ -451,7 +381,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length {
b = appendIndent(ctx, b, code.Indent)
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter)
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))
code = code.Next
} else {
last := len(b) - 1
b[last] = '\n'
b = appendIndent(ctx, b, code.Indent-1)
b = append(b, '}', ',', '\n')
b = appendObjectEnd(ctx, code, b)
code = code.End.Next
}
} else {
@ -482,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
b = append(b, ':', ' ')
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -495,7 +422,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
mapiternext(iter)
code = code.Next
case encoder.OpMapEnd:
// this operation only used by sorted map
// this operation only used by sorted map.
length := int(load(ctxptr, code.Length))
ptr := load(ctxptr, code.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -517,17 +444,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items {
buf = appendIndent(ctx, buf, code.Indent+1)
buf = append(buf, item.Key...)
buf[len(buf)-2] = ':'
buf[len(buf)-1] = ' '
buf = append(buf, item.Value...)
buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
}
buf = buf[:len(buf)-2]
buf = append(buf, '\n')
buf = appendIndent(ctx, buf, code.Indent)
buf = append(buf, '}', ',', '\n')
buf = appendMapEnd(ctx, code, buf)
b = b[:pos[0]]
b = append(b, 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.End.Next.Idx, oldOffset)
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
recursiveLevel++
case encoder.OpRecursiveEnd:
recursiveLevel--
// restore ctxptr
ctx.BaseIndent = int(load(ctxptr, code.Length))
restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx)
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)
if code.Indirect {
p = ptrToNPtr(p, code.PtrNum)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
}
if p == 0 {
b = appendNull(b)
@ -1562,12 +1481,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
if v == 0 {
code = code.NextField
} else {
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendComma(b)
@ -1600,12 +1519,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if !code.AnonymousHead {
b = appendStructHead(b)
}
b = appendStructKey(ctx, code, b)
b = append(b, '"')
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v)
b = append(b, '"')
b = appendComma(b)
@ -1638,12 +1557,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
if v == 0 {
code = code.NextField
} else {
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v)
@ -1906,8 +1825,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
b = appendStructKey(ctx, code, b)
v := ptrToString(p + code.Offset)
b = appendString(b, string(appendString([]byte{}, v)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendComma(b)
code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2251,12 +2169,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
b = appendStructKey(ctx, code, b)
if code.Indirect {
p = ptrToNPtr(p, code.PtrNum)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
}
if p == 0 {
b = appendNull(b)
} else {
b = appendBool(b, ptrToBool(p+code.Offset))
b = appendBool(b, ptrToBool(p))
}
b = appendComma(b)
code = code.Next
@ -3778,13 +3696,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b)
code = code.Next
break
}
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b)
code = code.Next
case encoder.OpStructFieldOmitEmptyFloat64Ptr:
@ -3807,11 +3727,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 {
b = appendNull(b)
} else {
b = append(b, '"')
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = append(b, '"')
b = appendFloat64(b, v)
b = append(b, '"')
}
@ -4348,9 +4268,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField
}
case encoder.OpStructFieldStruct:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyStruct:
@ -4366,25 +4286,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpStructAnonymousEnd:
code = code.Next
case encoder.OpStructEnd:
last := len(b) - 1
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)
b = appendStructEndSkipLast(ctx, code, b)
code = code.Next
case encoder.OpStructEndInt:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendInt(b, ptrToUint64(p+code.Offset), code)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4471,8 +4377,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndUint:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendUint(b, ptrToUint64(p+code.Offset), code)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4559,8 +4465,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndFloat32:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendFloat32(b, ptrToFloat32(p+code.Offset))
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4645,12 +4551,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndFloat64:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4658,10 +4564,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset)
if v != 0 {
b = appendStructKey(ctx, code, b)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
} else {
@ -4684,11 +4590,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset)
if v != 0 {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v)
b = append(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)
if p == 0 {
b = appendNull(b)
} else {
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
break
}
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
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)
if p != 0 {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = append(b, '"')
b = appendFloat64(b, v)
b = append(b, '"')
b = appendStructEnd(ctx, code, b)
@ -4761,8 +4669,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendString(b, ptrToString(p+code.Offset))
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4840,8 +4748,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndBool:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendBool(b, ptrToBool(p+code.Offset))
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4926,8 +4834,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndBytes:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p+code.Offset))
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4965,8 +4873,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndNumber:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
bb, err := appendNumber(b, ptrToNumber(p+code.Offset))
if err != nil {
return nil, err
@ -4988,9 +4896,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndNumberString:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = append(b, '"')
p := load(ctxptr, code.HeadIdx)
bb, err := appendNumber(b, ptrToNumber(p+code.Offset))
if err != nil {
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 (
"encoding/json"
"fmt"
"unsafe"
"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
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 {
addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,8 +107,87 @@ func appendComma(b []byte) []byte {
return append(b, ',', '\n')
}
func appendStructHead(b []byte) []byte {
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) {
@ -88,6 +198,10 @@ func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, e
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 {
b = appendIndent(ctx, b, code.Indent)
b = append(b, code.Key...)
@ -109,3 +223,19 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
}
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
import (
"fmt"
"math"
"sort"
"unsafe"
"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) {
recursiveLevel := 0
ptrOffset := uintptr(0)
@ -51,7 +17,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
for {
switch code.Op {
default:
return nil, fmt.Errorf("encoder (indent): opcode %s has not been implemented", code.Op)
return nil, errUnimplementedOp(code.Op)
case encoder.OpPtr:
p := load(ctxptr, code.Idx)
code = code.Next
@ -218,38 +184,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
bb, err := appendInterface(ctx, codeSet, opt, code, b, iface, ptrOffset)
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
ctxptr = ctx.Ptr()
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.Idx, uintptr(slice.Data))
if slice.Len > 0 {
b = append(b, '[', '\n')
b = appendIndent(ctx, b, code.Indent+1)
b = appendArrayHead(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, uintptr(slice.Data))
} else {
b = append(b, '[', ']', ',', '\n')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpSliceElem:
@ -345,17 +282,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
length := load(ctxptr, code.Length)
idx++
if idx < length {
b = appendIndent(ctx, b, code.Indent+1)
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
data := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, data+idx*size)
} else {
b = b[:len(b)-2]
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, ']', ',', '\n')
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpArrayPtr:
@ -377,30 +311,26 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
break
}
if code.Length > 0 {
b = append(b, '[', '\n')
b = appendIndent(ctx, b, code.Indent+1)
b = appendArrayHead(ctx, code, b)
store(ctxptr, code.ElemIdx, 0)
code = code.Next
store(ctxptr, code.Idx, p)
} else {
b = append(b, '[', ']', ',', '\n')
b = appendEmptyArray(b)
code = code.End.Next
}
case encoder.OpArrayElem:
idx := load(ctxptr, code.ElemIdx)
idx++
if idx < code.Length {
b = appendIndent(ctx, b, code.Indent+1)
b = appendArrayElemIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
p := load(ctxptr, code.HeadIdx)
size := code.Size
code = code.Next
store(ctxptr, code.Idx, p+idx*size)
} else {
b = b[:len(b)-2]
b = append(b, '\n')
b = appendIndent(ctx, b, code.Indent)
b = append(b, ']', ',', '\n')
b = appendArrayEnd(ctx, code, b)
code = code.End.Next
}
case encoder.OpMapPtr:
@ -424,7 +354,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
uptr := ptrToUnsafePtr(p)
mlen := maplen(uptr)
if mlen <= 0 {
b = append(b, '{', '}', ',', '\n')
b = appendEmptyObject(b)
code = code.End.Next
break
}
@ -440,7 +370,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
store(ctxptr, code.End.MapPos, uintptr(unsafe.Pointer(mapCtx)))
} else {
b = appendIndent(ctx, b, code.Next.Indent)
b = appendMapKeyIndent(ctx, code.Next, b)
}
key := mapiterkey(iter)
store(ctxptr, code.Next.Idx, uintptr(key))
@ -451,7 +381,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
idx++
if (opt & encoder.UnorderedMapOption) != 0 {
if idx < length {
b = appendIndent(ctx, b, code.Indent)
b = appendMapKeyIndent(ctx, code, b)
store(ctxptr, code.ElemIdx, idx)
ptr := load(ctxptr, code.MapIter)
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))
code = code.Next
} else {
last := len(b) - 1
b[last] = '\n'
b = appendIndent(ctx, b, code.Indent-1)
b = append(b, '}', ',', '\n')
b = appendObjectEnd(ctx, code, b)
code = code.End.Next
}
} else {
@ -482,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
case encoder.OpMapValue:
if (opt & encoder.UnorderedMapOption) != 0 {
b = append(b, ':', ' ')
b = appendColon(b)
} else {
ptr := load(ctxptr, code.End.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -495,7 +422,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
mapiternext(iter)
code = code.Next
case encoder.OpMapEnd:
// this operation only used by sorted map
// this operation only used by sorted map.
length := int(load(ctxptr, code.Length))
ptr := load(ctxptr, code.MapPos)
mapCtx := (*encoder.MapContext)(ptrToUnsafePtr(ptr))
@ -517,17 +444,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
sort.Sort(mapCtx.Slice)
buf := mapCtx.Buf
for _, item := range mapCtx.Slice.Items {
buf = appendIndent(ctx, buf, code.Indent+1)
buf = append(buf, item.Key...)
buf[len(buf)-2] = ':'
buf[len(buf)-1] = ' '
buf = append(buf, item.Value...)
buf = appendMapKeyValue(ctx, code, buf, item.Key, item.Value)
}
buf = buf[:len(buf)-2]
buf = append(buf, '\n')
buf = appendIndent(ctx, buf, code.Indent)
buf = append(buf, '}', ',', '\n')
buf = appendMapEnd(ctx, code, buf)
b = b[:pos[0]]
b = append(b, 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.End.Next.Idx, oldOffset)
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
recursiveLevel++
case encoder.OpRecursiveEnd:
recursiveLevel--
// restore ctxptr
ctx.BaseIndent = int(load(ctxptr, code.Length))
restoreIndent(ctx, code, ctxptr)
offset := load(ctxptr, code.Idx)
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)
if code.Indirect {
p = ptrToNPtr(p, code.PtrNum)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
}
if p == 0 {
b = appendNull(b)
@ -1562,12 +1481,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
if v == 0 {
code = code.NextField
} else {
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendComma(b)
@ -1600,12 +1519,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if !code.AnonymousHead {
b = appendStructHead(b)
}
b = appendStructKey(ctx, code, b)
b = append(b, '"')
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v)
b = append(b, '"')
b = appendComma(b)
@ -1638,12 +1557,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
if v == 0 {
code = code.NextField
} else {
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v)
@ -1906,8 +1825,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructHead(b)
}
b = appendStructKey(ctx, code, b)
v := ptrToString(p + code.Offset)
b = appendString(b, string(appendString([]byte{}, v)))
b = appendString(b, string(appendString([]byte{}, ptrToString(p+code.Offset))))
b = appendComma(b)
code = code.Next
case encoder.OpStructPtrHeadOmitEmptyStringString:
@ -2251,12 +2169,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
b = appendStructKey(ctx, code, b)
if code.Indirect {
p = ptrToNPtr(p, code.PtrNum)
p = ptrToNPtr(p+code.Offset, code.PtrNum)
}
if p == 0 {
b = appendNull(b)
} else {
b = appendBool(b, ptrToBool(p+code.Offset))
b = appendBool(b, ptrToBool(p))
}
b = appendComma(b)
code = code.Next
@ -3778,13 +3696,15 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendStructKey(ctx, code, b)
if p == 0 {
b = appendNull(b)
} else {
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b)
code = code.Next
break
}
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendComma(b)
code = code.Next
case encoder.OpStructFieldOmitEmptyFloat64Ptr:
@ -3807,11 +3727,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 {
b = appendNull(b)
} else {
b = append(b, '"')
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = append(b, '"')
b = appendFloat64(b, v)
b = append(b, '"')
}
@ -4348,9 +4268,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
code = code.NextField
}
case encoder.OpStructFieldStruct:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
p += code.Offset
b = appendStructKey(ctx, code, b)
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyStruct:
@ -4366,25 +4286,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpStructAnonymousEnd:
code = code.Next
case encoder.OpStructEnd:
last := len(b) - 1
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)
b = appendStructEndSkipLast(ctx, code, b)
code = code.Next
case encoder.OpStructEndInt:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendInt(b, ptrToUint64(p+code.Offset), code)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4471,8 +4377,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndUint:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendUint(b, ptrToUint64(p+code.Offset), code)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4559,8 +4465,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndFloat32:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendFloat32(b, ptrToFloat32(p+code.Offset))
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4645,12 +4551,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndFloat64:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4658,10 +4564,10 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset)
if v != 0 {
b = appendStructKey(ctx, code, b)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
} else {
@ -4684,11 +4590,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx)
v := ptrToFloat64(p + code.Offset)
if v != 0 {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendFloat64(b, v)
b = append(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)
if p == 0 {
b = appendNull(b)
} else {
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
break
}
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = appendFloat64(b, v)
b = appendStructEnd(ctx, code, b)
code = code.Next
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)
if p != 0 {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
v := ptrToFloat64(p)
if math.IsInf(v, 0) || math.IsNaN(v) {
return nil, errUnsupportedFloat(v)
}
b = append(b, '"')
b = appendFloat64(b, v)
b = append(b, '"')
b = appendStructEnd(ctx, code, b)
@ -4761,8 +4669,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndString:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendString(b, ptrToString(p+code.Offset))
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4840,8 +4748,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndBool:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendBool(b, ptrToBool(p+code.Offset))
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4926,8 +4834,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndBytes:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = appendByteSlice(b, ptrToBytes(p+code.Offset))
b = appendStructEnd(ctx, code, b)
code = code.Next
@ -4965,8 +4873,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndNumber:
b = appendStructKey(ctx, code, b)
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
bb, err := appendNumber(b, ptrToNumber(p+code.Offset))
if err != nil {
return nil, err
@ -4988,9 +4896,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
}
code = code.Next
case encoder.OpStructEndNumberString:
p := load(ctxptr, code.HeadIdx)
b = appendStructKey(ctx, code, b)
b = append(b, '"')
p := load(ctxptr, code.HeadIdx)
bb, err := appendNumber(b, ptrToNumber(p+code.Offset))
if err != nil {
return nil, err