Merge pull request #225 from goccy/feature/refactor-vm

Refactor the VM source for encoding ( use the same source for each VM )
This commit is contained in:
Masaaki Goshima 2021-05-18 14:18:10 +09:00 committed by GitHub
commit bbea516a68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1524 additions and 1218 deletions

View File

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

View File

@ -390,7 +390,7 @@ func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{
return compactedBuf, nil return compactedBuf, nil
} }
func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}, indent int, escape bool) ([]byte, error) { func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.AddrForMarshaler { if code.AddrForMarshaler {
if rv.CanAddr() { if rv.CanAddr() {
@ -415,7 +415,7 @@ func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v inte
indentedBuf, err := doIndent( indentedBuf, err := doIndent(
b, b,
marshalBuf, marshalBuf,
string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), ctx.BaseIndent+indent), string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), ctx.BaseIndent+code.Indent),
string(ctx.IndentStr), string(ctx.IndentStr),
escape, escape,
) )
@ -494,10 +494,10 @@ func AppendStructEnd(b []byte) []byte {
return append(b, '}', ',') return append(b, '}', ',')
} }
func AppendStructEndIndent(ctx *RuntimeContext, b []byte, indent int) []byte { func AppendStructEndIndent(ctx *RuntimeContext, code *Opcode, b []byte) []byte {
b = append(b, '\n') b = append(b, '\n')
b = append(b, ctx.Prefix...) b = append(b, ctx.Prefix...)
indentNum := ctx.BaseIndent + indent indentNum := ctx.BaseIndent + code.Indent - 1
for i := 0; i < indentNum; i++ { for i := 0; i < indentNum; i++ {
b = append(b, ctx.IndentStr...) b = append(b, ctx.IndentStr...)
} }

View File

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

View File

@ -2,12 +2,41 @@ package vm
import ( import (
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
addr := base + idx addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr)) return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,23 +105,111 @@ func appendComma(b []byte) []byte {
return append(b, ',') return append(b, ',')
} }
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { func appendColon(b []byte) []byte {
return append(b, code.Key...) last := len(b) - 1
b[last] = ':'
return b
}
func appendMapKeyValue(_ *encoder.RuntimeContext, _ *encoder.Opcode, b, key, value []byte) []byte {
b = append(b, key...)
b[len(b)-1] = ':'
return append(b, value...)
}
func appendMapEnd(_ *encoder.RuntimeContext, _ *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, _ *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)
}
func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalText(code, b, v, false)
}
func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, '[')
}
func appendArrayEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = ']'
return append(b, ',')
}
func appendEmptyArray(b []byte) []byte {
return append(b, '[', ']', ',')
}
func appendEmptyObject(b []byte) []byte {
return append(b, '{', '}', ',')
}
func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = '}'
return append(b, ',')
} }
func appendStructHead(b []byte) []byte { func appendStructHead(b []byte) []byte {
return append(b, '{') return append(b, '{')
} }
func appendStructEnd(b []byte) []byte { 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, '}', ',') return append(b, '}', ',')
} }
func appendStructEndSkipLast(b []byte) []byte { func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1 last := len(b) - 1
if b[last] == ',' { if b[last] == ',' {
b[last] = '}' b[last] = '}'
return appendComma(b) return appendComma(b)
} }
return appendStructEnd(b) return appendStructEnd(ctx, code, b)
} }
func restoreIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, _ uintptr) {}
func storeIndent(_ uintptr, _ *encoder.Opcode, _ uintptr) {}
func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,41 @@ package vm_debug
import ( import (
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (debug): opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
addr := base + idx addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr)) return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,23 +105,111 @@ func appendComma(b []byte) []byte {
return append(b, ',') return append(b, ',')
} }
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { func appendColon(b []byte) []byte {
return append(b, code.Key...) last := len(b) - 1
b[last] = ':'
return b
}
func appendMapKeyValue(_ *encoder.RuntimeContext, _ *encoder.Opcode, b, key, value []byte) []byte {
b = append(b, key...)
b[len(b)-1] = ':'
return append(b, value...)
}
func appendMapEnd(_ *encoder.RuntimeContext, _ *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, _ *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)
}
func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalText(code, b, v, false)
}
func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, '[')
}
func appendArrayEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = ']'
return append(b, ',')
}
func appendEmptyArray(b []byte) []byte {
return append(b, '[', ']', ',')
}
func appendEmptyObject(b []byte) []byte {
return append(b, '{', '}', ',')
}
func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = '}'
return append(b, ',')
} }
func appendStructHead(b []byte) []byte { func appendStructHead(b []byte) []byte {
return append(b, '{') return append(b, '{')
} }
func appendStructEnd(b []byte) []byte { 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, '}', ',') return append(b, '}', ',')
} }
func appendStructEndSkipLast(b []byte) []byte { func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1 last := len(b) - 1
if b[last] == ',' { if b[last] == ',' {
b[last] = '}' b[last] = '}'
return appendComma(b) return appendComma(b)
} }
return appendStructEnd(b) return appendStructEnd(ctx, code, b)
} }
func restoreIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, _ uintptr) {}
func storeIndent(_ uintptr, _ *encoder.Opcode, _ uintptr) {}
func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -2,12 +2,41 @@ package vm_escaped
import ( import (
"encoding/json" "encoding/json"
"fmt"
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
) )
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendEscapedString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
mapiterkey = encoder.MapIterKey
mapitervalue = encoder.MapIterValue
mapiternext = encoder.MapIterNext
maplen = encoder.MapLen
)
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (escaped): opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr { func load(base uintptr, idx uintptr) uintptr {
addr := base + idx addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr)) return **(**uintptr)(unsafe.Pointer(&addr))
@ -76,6 +105,89 @@ func appendComma(b []byte) []byte {
return append(b, ',') return append(b, ',')
} }
func appendColon(b []byte) []byte {
last := len(b) - 1
b[last] = ':'
return b
}
func appendMapKeyValue(_ *encoder.RuntimeContext, _ *encoder.Opcode, b, key, value []byte) []byte {
b = append(b, key...)
b[len(b)-1] = ':'
return append(b, value...)
}
func appendMapEnd(_ *encoder.RuntimeContext, _ *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, _ *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)
}
func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalText(code, b, v, true)
}
func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, '[')
}
func appendArrayEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = ']'
return append(b, ',')
}
func appendEmptyArray(b []byte) []byte {
return append(b, '[', ']', ',')
}
func appendEmptyObject(b []byte) []byte {
return append(b, '{', '}', ',')
}
func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
last := len(b) - 1
b[last] = '}'
return append(b, ',')
}
func appendStructHead(b []byte) []byte { func appendStructHead(b []byte) []byte {
return append(b, '{') return append(b, '{')
} }
@ -84,15 +196,20 @@ func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte)
return append(b, code.EscapedKey...) return append(b, code.EscapedKey...)
} }
func appendStructEnd(b []byte) []byte { func appendStructEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
return append(b, '}', ',') return append(b, '}', ',')
} }
func appendStructEndSkipLast(b []byte) []byte { func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
last := len(b) - 1 last := len(b) - 1
if b[last] == ',' { if b[last] == ',' {
b[last] = '}' b[last] = '}'
return appendComma(b) return appendComma(b)
} }
return appendStructEnd(b) return appendStructEnd(ctx, code, b)
} }
func restoreIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, _ uintptr) {}
func storeIndent(_ uintptr, _ *encoder.Opcode, _ uintptr) {}
func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff