Fix vm code

This commit is contained in:
Masaaki Goshima 2021-03-17 00:22:19 +09:00
parent 62b7d3ba0a
commit 10c4118a45
9 changed files with 941 additions and 117 deletions

View File

@ -13,7 +13,8 @@ import (
"sync" "sync"
"unsafe" "unsafe"
_ "github.com/goccy/go-json/internal/encoder/vm" "github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/encoder/vm"
) )
// An Encoder writes JSON values to an output stream. // An Encoder writes JSON values to an output stream.
@ -208,16 +209,29 @@ func encode(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte,
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)
ctx.keepRefs = append(ctx.keepRefs, header.ptr) ctx.keepRefs = append(ctx.keepRefs, header.ptr)
if (opt & EncodeOptionHTMLEscape) != 0 {
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
} else {
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
if err != nil {
return nil, err
}
ctx := &encoder.RuntimeContext{}
ctx.Init(p, codeSet.CodeLength)
buf, err := vm.Run(ctx, b, codeSet, encoder.Option(opt))
if err != nil {
return nil, err
}
ctx.Buf = buf
return buf, nil
}
} }
func encodeNoEscape(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) { func encodeNoEscape(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {

View File

@ -140,7 +140,7 @@ func (t OpType) HeadToPtrHead() OpType {
if idx == -1 { if idx == -1 {
return t return t
} }
suffix := "Ptr"+t.String()[idx+len("Head"):] suffix := "PtrHead"+t.String()[idx+len("Head"):]
const toPtrOffset = 3 const toPtrOffset = 3
if strings.Contains(OpType(int(t) + toPtrOffset).String(), suffix) { if strings.Contains(OpType(int(t) + toPtrOffset).String(), suffix) {
@ -186,6 +186,9 @@ func (t OpType) FieldToEnd() OpType {
return t return t
} }
suffix := t.String()[idx+len("Field"):] suffix := t.String()[idx+len("Field"):]
if suffix == "" || suffix == "OmitEmpty" || suffix == "StringTag" {
return t
}
const toEndOffset = 3 const toEndOffset = 3
if strings.Contains(OpType(int(t) + toEndOffset).String(), "End"+suffix) { if strings.Contains(OpType(int(t) + toEndOffset).String(), "End"+suffix) {
return OpType(int(t) + toEndOffset) return OpType(int(t) + toEndOffset)

View File

@ -0,0 +1,56 @@
package encoder
import (
"bytes"
"github.com/goccy/go-json/internal/errors"
)
func compact(dst *bytes.Buffer, src []byte, escape bool) error {
if len(src) == 0 {
return errors.ErrUnexpectedEndOfJSON("", 0)
}
length := len(src)
for cursor := 0; cursor < length; cursor++ {
c := src[cursor]
switch c {
case ' ', '\t', '\n', '\r':
continue
case '"':
if err := dst.WriteByte(c); err != nil {
return err
}
for {
cursor++
c := src[cursor]
if escape && (c == '<' || c == '>' || c == '&') {
if _, err := dst.WriteString(`\u00`); err != nil {
return err
}
if _, err := dst.Write([]byte{hex[c>>4], hex[c&0xF]}); err != nil {
return err
}
} else if err := dst.WriteByte(c); err != nil {
return err
}
switch c {
case '\\':
cursor++
if err := dst.WriteByte(src[cursor]); err != nil {
return err
}
case '"':
goto LOOP_END
case '\000':
return errors.ErrUnexpectedEndOfJSON("string", int64(length))
}
}
default:
if err := dst.WriteByte(c); err != nil {
return err
}
}
LOOP_END:
}
return nil
}

View File

@ -299,6 +299,10 @@ func compile(ctx *compileContext, isPtr bool) (*Opcode, error) {
func convertPtrOp(code *Opcode) OpType { func convertPtrOp(code *Opcode) OpType {
ptrHeadOp := code.Op.HeadToPtrHead() ptrHeadOp := code.Op.HeadToPtrHead()
if code.Op != ptrHeadOp { if code.Op != ptrHeadOp {
if code.PtrNum > 0 {
// ptr field and ptr head
code.PtrNum--
}
return ptrHeadOp return ptrHeadOp
} }
switch code.Op { switch code.Op {

View File

@ -12,10 +12,10 @@ import (
var setsMu sync.RWMutex var setsMu sync.RWMutex
func CompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) { func CompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
if typeptr > maxTypeAddr { if typeptr > typeAddr.MaxTypeAddr {
return compileToGetCodeSetSlowPath(typeptr) return compileToGetCodeSetSlowPath(typeptr)
} }
index := typeptr - baseTypeAddr index := typeptr - typeAddr.BaseTypeAddr
setsMu.RLock() setsMu.RLock()
if codeSet := cachedOpcodeSets[index]; codeSet != nil { if codeSet := cachedOpcodeSets[index]; codeSet != nil {
setsMu.RUnlock() setsMu.RUnlock()
@ -35,7 +35,7 @@ func CompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
} }
code = copyOpcode(code) code = copyOpcode(code)
codeLength := code.TotalLength() codeLength := code.TotalLength()
codeSet := &opcodeSet{ codeSet := &OpcodeSet{
Code: code, Code: code,
CodeLength: codeLength, CodeLength: codeLength,
} }

View File

@ -152,17 +152,22 @@ func Store(base uintptr, idx uintptr, p uintptr) {
**(**uintptr)(unsafe.Pointer(&addr)) = p **(**uintptr)(unsafe.Pointer(&addr)) = p
} }
func LoadAndStoreNPtr(base uintptr, idx uintptr, ptrNum int) { func LoadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
addr := base + idx addr := base + idx
p := **(**uintptr)(unsafe.Pointer(&addr)) p := **(**uintptr)(unsafe.Pointer(&addr))
if p == 0 {
return 0
}
return PtrToPtr(p)
/*
for i := 0; i < ptrNum; i++ { for i := 0; i < ptrNum; i++ {
if p == 0 { if p == 0 {
**(**uintptr)(unsafe.Pointer(&addr)) = 0 return p
return
} }
p = PtrToPtr(p) p = PtrToPtr(p)
} }
**(**uintptr)(unsafe.Pointer(&addr)) = p return p
*/
} }
func PtrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) } func PtrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
@ -426,13 +431,12 @@ func AppendMarshalJSON(code *Opcode, b []byte, v interface{}, escape bool) ([]by
if err != nil { if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err} return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
} }
return bb, nil buf := bytes.NewBuffer(b)
//buf := bytes.NewBuffer(b)
// TODO: we should validate buffer with `compact` // TODO: we should validate buffer with `compact`
// if err := compact(buf, bb, escape); err != nil { if err := compact(buf, bb, escape); err != nil {
// return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err} return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
// } }
//return buf.Bytes(), nil return buf.Bytes(), nil
} }
func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) { func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {

View File

@ -427,7 +427,7 @@ func (c *Opcode) dumpValue(code *Opcode) string {
) )
} }
func (c *Opcode) dump() string { func (c *Opcode) Dump() string {
codes := []string{} codes := []string{}
for code := c; code.Op != OpEnd; { for code := c; code.Op != OpEnd; {
switch code.Op.CodeType() { switch code.Op.CodeType() {

View File

@ -955,7 +955,7 @@ func (t OpType) HeadToPtrHead() OpType {
if idx == -1 { if idx == -1 {
return t return t
} }
suffix := "Ptr" + t.String()[idx+len("Head"):] suffix := "PtrHead" + t.String()[idx+len("Head"):]
const toPtrOffset = 3 const toPtrOffset = 3
if strings.Contains(OpType(int(t)+toPtrOffset).String(), suffix) { if strings.Contains(OpType(int(t)+toPtrOffset).String(), suffix) {
@ -1001,6 +1001,9 @@ func (t OpType) FieldToEnd() OpType {
return t return t
} }
suffix := t.String()[idx+len("Field"):] suffix := t.String()[idx+len("Field"):]
if suffix == "" || suffix == "OmitEmpty" || suffix == "StringTag" {
return t
}
const toEndOffset = 3 const toEndOffset = 3
if strings.Contains(OpType(int(t)+toEndOffset).String(), "End"+suffix) { if strings.Contains(OpType(int(t)+toEndOffset).String(), "End"+suffix) {
return OpType(int(t) + toEndOffset) return OpType(int(t) + toEndOffset)

File diff suppressed because it is too large Load Diff