Replace vm and escaped vm codes with internal package

This commit is contained in:
Masaaki Goshima 2021-03-17 12:32:23 +09:00
parent 10c4118a45
commit cccf9f9f33
8 changed files with 4565 additions and 8188 deletions

View File

@ -7,7 +7,7 @@ services:
deploy: deploy:
resources: resources:
limits: limits:
memory: 2048M memory: 1200M
working_dir: /go/src/go-json working_dir: /go/src/go-json
command: | command: |
sh -c "go test -c . && ls go-json.test" sh -c "go test -c . && ls go-json.test"

View File

@ -15,6 +15,7 @@ import (
"github.com/goccy/go-json/internal/encoder" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/encoder/vm" "github.com/goccy/go-json/internal/encoder/vm"
"github.com/goccy/go-json/internal/encoder/vm_escaped"
) )
// An Encoder writes JSON values to an output stream. // An Encoder writes JSON values to an output stream.
@ -48,6 +49,15 @@ var (
} }
}, },
} }
encRuntimeContextPool2 = sync.Pool{
New: func() interface{} {
return &encoder.RuntimeContext{
Buf: make([]byte, 0, bufSize),
Ptrs: make([]uintptr, 128),
KeepRefs: make([]unsafe.Pointer, 0, 8),
}
},
}
) )
func takeEncodeRuntimeContext() *encodeRuntimeContext { func takeEncodeRuntimeContext() *encodeRuntimeContext {
@ -58,6 +68,14 @@ func releaseEncodeRuntimeContext(ctx *encodeRuntimeContext) {
encRuntimeContextPool.Put(ctx) encRuntimeContextPool.Put(ctx)
} }
func takeEncodeRuntimeContext2() *encoder.RuntimeContext {
return encRuntimeContextPool2.Get().(*encoder.RuntimeContext)
}
func releaseEncodeRuntimeContext2(ctx *encoder.RuntimeContext) {
encRuntimeContextPool2.Put(ctx)
}
// NewEncoder returns a new encoder that writes to w. // NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder { func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w, enabledHTMLEscape: true} return &Encoder{w: w, enabledHTMLEscape: true}
@ -95,7 +113,9 @@ func (e *Encoder) encodeWithOption(ctx *encodeRuntimeContext, v interface{}, opt
if e.enabledIndent { if e.enabledIndent {
buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr, opt) buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr, opt)
} else { } else {
ctx := takeEncodeRuntimeContext2()
buf, err = encode(ctx, v, opt) buf, err = encode(ctx, v, opt)
releaseEncodeRuntimeContext2(ctx)
} }
if err != nil { if err != nil {
return err return err
@ -133,11 +153,11 @@ func (e *Encoder) SetIndent(prefix, indent string) {
} }
func marshal(v interface{}, opt EncodeOption) ([]byte, error) { func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
ctx := takeEncodeRuntimeContext() ctx := takeEncodeRuntimeContext2()
buf, err := encode(ctx, v, opt|EncodeOptionHTMLEscape) buf, err := encode(ctx, v, opt|EncodeOptionHTMLEscape)
if err != nil { if err != nil {
releaseEncodeRuntimeContext(ctx) releaseEncodeRuntimeContext2(ctx)
return nil, err return nil, err
} }
@ -149,16 +169,16 @@ func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
copied := make([]byte, len(buf)) copied := make([]byte, len(buf))
copy(copied, buf) copy(copied, buf)
releaseEncodeRuntimeContext(ctx) releaseEncodeRuntimeContext2(ctx)
return copied, nil return copied, nil
} }
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) { func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
ctx := takeEncodeRuntimeContext() ctx := takeEncodeRuntimeContext2()
buf, err := encodeNoEscape(ctx, v, opt|EncodeOptionHTMLEscape) buf, err := encodeNoEscape(ctx, v, opt|EncodeOptionHTMLEscape)
if err != nil { if err != nil {
releaseEncodeRuntimeContext(ctx) releaseEncodeRuntimeContext2(ctx)
return nil, err return nil, err
} }
@ -170,7 +190,7 @@ func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
copied := make([]byte, len(buf)) copied := make([]byte, len(buf))
copy(copied, buf) copy(copied, buf)
releaseEncodeRuntimeContext(ctx) releaseEncodeRuntimeContext2(ctx)
return copied, nil return copied, nil
} }
@ -191,8 +211,8 @@ func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]by
return copied, nil return copied, nil
} }
func encode(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) { func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
b := ctx.buf[:0] b := ctx.Buf[:0]
if v == nil { if v == nil {
b = encodeNull(b) b = encodeNull(b)
b = encodeComma(b) b = encodeComma(b)
@ -202,40 +222,25 @@ func encode(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte,
typ := header.typ typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ)) typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := encodeCompileToGetCodeSet(typeptr) codeSet, err := encoder.CompileToGetCodeSet(typeptr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
p := uintptr(header.ptr) p := uintptr(header.ptr)
ctx.init(p, codeSet.codeLength)
ctx.keepRefs = append(ctx.keepRefs, header.ptr)
if (opt & EncodeOptionHTMLEscape) != 0 {
buf, err := encodeRunCode(ctx, b, codeSet, opt)
if err != nil {
return nil, err
}
ctx.buf = buf
return buf, nil
} else {
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
if err != nil {
return nil, err
}
ctx := &encoder.RuntimeContext{}
ctx.Init(p, codeSet.CodeLength) ctx.Init(p, codeSet.CodeLength)
buf, err := vm.Run(ctx, b, codeSet, encoder.Option(opt)) ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
buf, err := encodeRunCode(ctx, b, codeSet, opt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx.Buf = buf ctx.Buf = buf
return buf, nil return buf, nil
}
} }
func encodeNoEscape(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) { func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
b := ctx.buf[:0] b := ctx.Buf[:0]
if v == nil { if v == nil {
b = encodeNull(b) b = encodeNull(b)
b = encodeComma(b) b = encodeComma(b)
@ -245,19 +250,19 @@ func encodeNoEscape(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption)
typ := header.typ typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ)) typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := encodeCompileToGetCodeSet(typeptr) codeSet, err := encoder.CompileToGetCodeSet(typeptr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
p := uintptr(header.ptr) p := uintptr(header.ptr)
ctx.init(p, codeSet.codeLength) ctx.Init(p, codeSet.CodeLength)
buf, err := encodeRunCode(ctx, b, codeSet, opt) buf, err := encodeRunCode(ctx, b, codeSet, opt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx.buf = buf ctx.Buf = buf
return buf, nil return buf, nil
} }
@ -291,11 +296,11 @@ func encodeIndent(ctx *encodeRuntimeContext, v interface{}, prefix, indent strin
return buf, nil return buf, nil
} }
func encodeRunCode(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt EncodeOption) ([]byte, error) { func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) {
if (opt & EncodeOptionHTMLEscape) != 0 { if (opt & EncodeOptionHTMLEscape) != 0 {
return encodeRunEscaped(ctx, b, codeSet, opt) return vm_escaped.Run(ctx, b, codeSet, encoder.Option(opt))
} }
return encodeRun(ctx, b, codeSet, opt) return vm.Run(ctx, b, codeSet, encoder.Option(opt))
} }
func encodeRunIndentCode(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) { func encodeRunIndentCode(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ import (
var setsMu sync.RWMutex var setsMu sync.RWMutex
func CompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) { func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
if typeptr > typeAddr.MaxTypeAddr { if typeptr > typeAddr.MaxTypeAddr {
return compileToGetCodeSetSlowPath(typeptr) return compileToGetCodeSetSlowPath(typeptr)
} }

View File

@ -476,3 +476,14 @@ func AppendComma(b []byte) []byte {
func AppendStructEnd(b []byte) []byte { func AppendStructEnd(b []byte) []byte {
return append(b, '}', ',') return append(b, '}', ',')
} }
func IsNilForMarshaler(v interface{}) bool {
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Interface, reflect.Map, reflect.Ptr:
return rv.IsNil()
case reflect.Slice:
return rv.IsNil() || rv.Len() == 0
}
return false
}

View File

@ -269,14 +269,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = bb b = bb
code = code.Next code = code.Next
case encoder.OpMarshalJSONPtr: case encoder.OpMarshalJSONPtr:
p := loadNPtr(ctxptr, code.Idx, code.PtrNum) p := load(ctxptr, code.Idx)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
break break
} }
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, ptrToPtr(p))
fallthrough fallthrough
case encoder.OpMarshalJSON: case encoder.OpMarshalJSON:
p := load(ctxptr, code.Idx) p := load(ctxptr, code.Idx)
@ -293,14 +293,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
b = appendComma(bb) b = appendComma(bb)
code = code.Next code = code.Next
case encoder.OpMarshalTextPtr: case encoder.OpMarshalTextPtr:
p := loadNPtr(ctxptr, code.Idx, code.PtrNum) p := load(ctxptr, code.Idx)
if p == 0 { if p == 0 {
b = appendNull(b) b = appendNull(b)
b = appendComma(b) b = appendComma(b)
code = code.Next code = code.Next
break break
} }
store(ctxptr, code.Idx, p) store(ctxptr, code.Idx, ptrToPtr(p))
fallthrough fallthrough
case encoder.OpMarshalText: case encoder.OpMarshalText:
p := load(ctxptr, code.Idx) p := load(ctxptr, code.Idx)
@ -337,6 +337,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
} }
store(ctxptr, code.ElemIdx, 0) store(ctxptr, code.ElemIdx, 0)
store(ctxptr, code.Length, uintptr(slice.Len)) store(ctxptr, code.Length, uintptr(slice.Len))
store(ctxptr, code.Idx, uintptr(slice.Data))
if slice.Len > 0 { if slice.Len > 0 {
b = append(b, '[') b = append(b, '[')
code = code.Next code = code.Next
@ -2815,11 +2816,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p = ptrToPtr(p + code.Offset) p = ptrToPtr(p + code.Offset)
} }
} }
if p == 0 && code.Nilcheck { iface := ptrToInterface(code, p)
if code.Nilcheck && encoder.IsNilForMarshaler(iface) {
code = code.NextField code = code.NextField
} else { } else {
b = append(b, code.Key...) b = append(b, code.Key...)
bb, err := appendMarshalJSON(code, b, ptrToInterface(code, p), false) bb, err := appendMarshalJSON(code, b, iface, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

File diff suppressed because it is too large Load Diff