mirror of https://github.com/goccy/go-json.git
Merge pull request #73 from goccy/feature/improve-performance
Improve encoding performance
This commit is contained in:
commit
88316cfbeb
69
encode.go
69
encode.go
|
@ -15,16 +15,15 @@ import (
|
|||
|
||||
// An Encoder writes JSON values to an output stream.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
ctx *encodeRuntimeContext
|
||||
buf []byte
|
||||
enabledIndent bool
|
||||
enabledHTMLEscape bool
|
||||
unorderedMap bool
|
||||
prefix []byte
|
||||
indentStr []byte
|
||||
structTypeToCompiledCode map[uintptr]*compiledCode
|
||||
structTypeToCompiledIndentCode map[uintptr]*compiledCode
|
||||
w io.Writer
|
||||
ctx *encodeRuntimeContext
|
||||
ptr unsafe.Pointer
|
||||
buf []byte
|
||||
enabledIndent bool
|
||||
enabledHTMLEscape bool
|
||||
unorderedMap bool
|
||||
prefix []byte
|
||||
indentStr []byte
|
||||
}
|
||||
|
||||
type compiledCode struct {
|
||||
|
@ -73,9 +72,7 @@ func init() {
|
|||
ptrs: make([]uintptr, 128),
|
||||
keepRefs: make([]unsafe.Pointer, 0, 8),
|
||||
},
|
||||
buf: make([]byte, 0, bufSize),
|
||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||
structTypeToCompiledIndentCode: map[uintptr]*compiledCode{},
|
||||
buf: make([]byte, 0, bufSize),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -105,20 +102,20 @@ func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
var err error
|
||||
if e.buf, err = e.encode(v); err != nil {
|
||||
buf, err := e.encode(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.enabledIndent {
|
||||
e.buf = e.buf[:len(e.buf)-2]
|
||||
buf = buf[:len(buf)-2]
|
||||
} else {
|
||||
e.buf = e.buf[:len(e.buf)-1]
|
||||
buf = buf[:len(buf)-1]
|
||||
}
|
||||
e.buf = append(e.buf, '\n')
|
||||
if _, err := e.w.Write(e.buf); err != nil {
|
||||
buf = append(buf, '\n')
|
||||
if _, err := e.w.Write(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
e.buf = e.buf[:0]
|
||||
e.buf = buf[:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -148,29 +145,31 @@ func (e *Encoder) release() {
|
|||
}
|
||||
|
||||
func (e *Encoder) reset() {
|
||||
e.buf = e.buf[:0]
|
||||
e.enabledHTMLEscape = true
|
||||
e.enabledIndent = false
|
||||
e.unorderedMap = false
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
|
||||
var err error
|
||||
if e.buf, err = e.encode(v); err != nil {
|
||||
buf, err := e.encode(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.buf = buf
|
||||
|
||||
if e.enabledIndent {
|
||||
copied := make([]byte, len(e.buf)-2)
|
||||
copy(copied, e.buf)
|
||||
copied := make([]byte, len(buf)-2)
|
||||
copy(copied, buf)
|
||||
return copied, nil
|
||||
}
|
||||
copied := make([]byte, len(e.buf)-1)
|
||||
copy(copied, e.buf)
|
||||
copied := make([]byte, len(buf)-1)
|
||||
copy(copied, buf)
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
func (e *Encoder) encode(v interface{}) ([]byte, error) {
|
||||
b := e.buf
|
||||
b := e.buf[:0]
|
||||
if v == nil {
|
||||
b = encodeNull(b)
|
||||
if e.enabledIndent {
|
||||
|
@ -202,17 +201,19 @@ func (e *Encoder) encode(v interface{}) ([]byte, error) {
|
|||
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
|
||||
|
||||
codeIndent, err := e.compileHead(&encodeCompileContext{
|
||||
typ: copiedType,
|
||||
root: true,
|
||||
withIndent: true,
|
||||
typ: copiedType,
|
||||
root: true,
|
||||
withIndent: true,
|
||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code, err := e.compileHead(&encodeCompileContext{
|
||||
typ: copiedType,
|
||||
root: true,
|
||||
withIndent: false,
|
||||
typ: copiedType,
|
||||
root: true,
|
||||
withIndent: false,
|
||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
@ -877,14 +876,8 @@ func (e *Encoder) recursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *o
|
|||
func (e *Encoder) compiledCode(ctx *encodeCompileContext) *opcode {
|
||||
typ := ctx.typ
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
if ctx.withIndent {
|
||||
if compiledCode, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
|
||||
return e.recursiveCode(ctx, compiledCode)
|
||||
}
|
||||
} else {
|
||||
if compiledCode, exists := e.structTypeToCompiledCode[typeptr]; exists {
|
||||
return e.recursiveCode(ctx, compiledCode)
|
||||
}
|
||||
if compiledCode, exists := ctx.structTypeToCompiledCode[typeptr]; exists {
|
||||
return e.recursiveCode(ctx, compiledCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1140,11 +1133,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
typ := ctx.typ
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
compiled := &compiledCode{}
|
||||
if ctx.withIndent {
|
||||
e.structTypeToCompiledIndentCode[typeptr] = compiled
|
||||
} else {
|
||||
e.structTypeToCompiledCode[typeptr] = compiled
|
||||
}
|
||||
ctx.structTypeToCompiledCode[typeptr] = compiled
|
||||
// header => code => structField => code => end
|
||||
// ^ |
|
||||
// |__________|
|
||||
|
@ -1212,12 +1201,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
}
|
||||
}
|
||||
key := fmt.Sprintf(`"%s":`, tag.key)
|
||||
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
enc.buf = encodeEscapedString(enc.buf, tag.key)
|
||||
escapedKey := fmt.Sprintf(`%s:`, string(enc.buf))
|
||||
enc.release()
|
||||
escapedKey := fmt.Sprintf(`%s:`, string(encodeEscapedString([]byte{}, tag.key)))
|
||||
fieldCode := &opcode{
|
||||
typ: valueCode.typ,
|
||||
displayIdx: fieldOpcodeIndex,
|
||||
|
@ -1294,11 +1278,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
ret := (*opcode)(unsafe.Pointer(head))
|
||||
compiled.code = ret
|
||||
|
||||
if ctx.withIndent {
|
||||
delete(e.structTypeToCompiledIndentCode, typeptr)
|
||||
} else {
|
||||
delete(e.structTypeToCompiledCode, typeptr)
|
||||
}
|
||||
delete(ctx.structTypeToCompiledCode, typeptr)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
|
|
@ -5,25 +5,27 @@ import (
|
|||
)
|
||||
|
||||
type encodeCompileContext struct {
|
||||
typ *rtype
|
||||
withIndent bool
|
||||
root bool
|
||||
opcodeIndex int
|
||||
ptrIndex int
|
||||
indent int
|
||||
typ *rtype
|
||||
withIndent bool
|
||||
root bool
|
||||
opcodeIndex int
|
||||
ptrIndex int
|
||||
indent int
|
||||
structTypeToCompiledCode map[uintptr]*compiledCode
|
||||
|
||||
parent *encodeCompileContext
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) context() *encodeCompileContext {
|
||||
return &encodeCompileContext{
|
||||
typ: c.typ,
|
||||
withIndent: c.withIndent,
|
||||
root: c.root,
|
||||
opcodeIndex: c.opcodeIndex,
|
||||
ptrIndex: c.ptrIndex,
|
||||
indent: c.indent,
|
||||
parent: c,
|
||||
typ: c.typ,
|
||||
withIndent: c.withIndent,
|
||||
root: c.root,
|
||||
opcodeIndex: c.opcodeIndex,
|
||||
ptrIndex: c.ptrIndex,
|
||||
indent: c.indent,
|
||||
structTypeToCompiledCode: c.structTypeToCompiledCode,
|
||||
parent: c,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
37
encode_vm.go
37
encode_vm.go
|
@ -317,10 +317,11 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
|||
}
|
||||
}
|
||||
c, err := e.compileHead(&encodeCompileContext{
|
||||
typ: header.typ,
|
||||
root: code.root,
|
||||
withIndent: e.enabledIndent,
|
||||
indent: code.indent,
|
||||
typ: header.typ,
|
||||
root: code.root,
|
||||
withIndent: e.enabledIndent,
|
||||
indent: code.indent,
|
||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -4157,17 +4158,13 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
|||
} else {
|
||||
b = append(b, '{')
|
||||
b = e.encodeKey(b, code)
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
s := e.ptrToString(ptr + code.offset)
|
||||
if e.enabledHTMLEscape {
|
||||
enc.buf = encodeEscapedString(enc.buf, s)
|
||||
b = e.encodeString(b, string(encodeEscapedString([]byte{}, s)))
|
||||
} else {
|
||||
enc.buf = encodeNoEscapedString(enc.buf, s)
|
||||
b = e.encodeString(b, string(encodeNoEscapedString([]byte{}, s)))
|
||||
}
|
||||
b = e.encodeString(b, string(enc.buf))
|
||||
b = encodeComma(b)
|
||||
enc.release()
|
||||
code = code.next
|
||||
}
|
||||
case opStructFieldPtrAnonymousHeadStringTagString:
|
||||
|
@ -4706,15 +4703,12 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
|||
b = e.encodeIndent(b, code.indent+1)
|
||||
b = e.encodeKey(b, code)
|
||||
b = append(b, ' ')
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
s := e.ptrToString(ptr + code.offset)
|
||||
if e.enabledHTMLEscape {
|
||||
enc.buf = encodeEscapedString(enc.buf, s)
|
||||
b = e.encodeString(b, string(encodeEscapedString([]byte{}, s)))
|
||||
} else {
|
||||
enc.buf = encodeNoEscapedString(enc.buf, s)
|
||||
b = e.encodeString(b, string(encodeNoEscapedString([]byte{}, s)))
|
||||
}
|
||||
b = e.encodeString(b, string(enc.buf))
|
||||
b = encodeIndentComma(b)
|
||||
code = code.next
|
||||
}
|
||||
|
@ -5908,10 +5902,8 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
|||
case opStructFieldStringTagString:
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
b = e.encodeKey(b, code)
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
enc.buf = enc.encodeString(enc.buf, e.ptrToString(ptr+code.offset))
|
||||
b = e.encodeString(b, string(enc.buf))
|
||||
s := e.ptrToString(ptr + code.offset)
|
||||
b = e.encodeString(b, string(encodeEscapedString([]byte{}, s)))
|
||||
code = code.next
|
||||
case opStructFieldStringTagBool:
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
|
@ -6065,12 +6057,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
|||
b = e.encodeIndent(b, code.indent)
|
||||
b = e.encodeKey(b, code)
|
||||
b = append(b, ' ')
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
enc.buf = enc.encodeString(enc.buf, e.ptrToString(ptr+code.offset))
|
||||
b = e.encodeString(b, string(enc.buf))
|
||||
s := e.ptrToString(ptr + code.offset)
|
||||
b = e.encodeString(b, string(encodeEscapedString([]byte{}, s)))
|
||||
b = encodeIndentComma(b)
|
||||
enc.release()
|
||||
code = code.next
|
||||
case opStructFieldStringTagBoolIndent:
|
||||
ptr := load(ctxptr, code.headIdx)
|
||||
|
|
Loading…
Reference in New Issue