forked from mirror/go-json
Fix vm code
This commit is contained in:
parent
62b7d3ba0a
commit
10c4118a45
22
encode.go
22
encode.go
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue