Move encoder source to internal package

This commit is contained in:
Masaaki Goshima 2021-03-18 15:46:55 +09:00
parent cccf9f9f33
commit 9cbe7b3991
9 changed files with 10213 additions and 9340 deletions

299
encode.go
View File

@ -1,21 +1,15 @@
package json
import (
"bytes"
"encoding"
"encoding/base64"
"fmt"
"io"
"math"
"reflect"
"strconv"
"strings"
"sync"
"unsafe"
"github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/encoder/vm"
"github.com/goccy/go-json/internal/encoder/vm_escaped"
"github.com/goccy/go-json/internal/encoder/vm_escaped_indent"
"github.com/goccy/go-json/internal/encoder/vm_indent"
)
// An Encoder writes JSON values to an output stream.
@ -41,15 +35,6 @@ const (
var (
encRuntimeContextPool = sync.Pool{
New: func() interface{} {
return &encodeRuntimeContext{
buf: make([]byte, 0, bufSize),
ptrs: make([]uintptr, 128),
keepRefs: make([]unsafe.Pointer, 0, 8),
}
},
}
encRuntimeContextPool2 = sync.Pool{
New: func() interface{} {
return &encoder.RuntimeContext{
Buf: make([]byte, 0, bufSize),
@ -60,22 +45,14 @@ var (
}
)
func takeEncodeRuntimeContext() *encodeRuntimeContext {
return encRuntimeContextPool.Get().(*encodeRuntimeContext)
func takeEncodeRuntimeContext() *encoder.RuntimeContext {
return encRuntimeContextPool.Get().(*encoder.RuntimeContext)
}
func releaseEncodeRuntimeContext(ctx *encodeRuntimeContext) {
func releaseEncodeRuntimeContext(ctx *encoder.RuntimeContext) {
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.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w, enabledHTMLEscape: true}
@ -98,7 +75,7 @@ func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc)
return err
}
func (e *Encoder) encodeWithOption(ctx *encodeRuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
var opt EncodeOption
if e.enabledHTMLEscape {
opt |= EncodeOptionHTMLEscape
@ -113,9 +90,7 @@ func (e *Encoder) encodeWithOption(ctx *encodeRuntimeContext, v interface{}, opt
if e.enabledIndent {
buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr, opt)
} else {
ctx := takeEncodeRuntimeContext2()
buf, err = encode(ctx, v, opt)
releaseEncodeRuntimeContext2(ctx)
}
if err != nil {
return err
@ -153,11 +128,11 @@ func (e *Encoder) SetIndent(prefix, indent string) {
}
func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
ctx := takeEncodeRuntimeContext2()
ctx := takeEncodeRuntimeContext()
buf, err := encode(ctx, v, opt|EncodeOptionHTMLEscape)
if err != nil {
releaseEncodeRuntimeContext2(ctx)
releaseEncodeRuntimeContext(ctx)
return nil, err
}
@ -169,16 +144,16 @@ func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
copied := make([]byte, len(buf))
copy(copied, buf)
releaseEncodeRuntimeContext2(ctx)
releaseEncodeRuntimeContext(ctx)
return copied, nil
}
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
ctx := takeEncodeRuntimeContext2()
ctx := takeEncodeRuntimeContext()
buf, err := encodeNoEscape(ctx, v, opt|EncodeOptionHTMLEscape)
if err != nil {
releaseEncodeRuntimeContext2(ctx)
releaseEncodeRuntimeContext(ctx)
return nil, err
}
@ -190,7 +165,7 @@ func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
copied := make([]byte, len(buf))
copy(copied, buf)
releaseEncodeRuntimeContext2(ctx)
releaseEncodeRuntimeContext(ctx)
return copied, nil
}
@ -214,8 +189,8 @@ func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]by
func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
b := ctx.Buf[:0]
if v == nil {
b = encodeNull(b)
b = encodeComma(b)
b = encoder.AppendNull(b)
b = encoder.AppendComma(b)
return b, nil
}
header := (*emptyInterface)(unsafe.Pointer(&v))
@ -242,8 +217,8 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
b := ctx.Buf[:0]
if v == nil {
b = encodeNull(b)
b = encodeComma(b)
b = encoder.AppendNull(b)
b = encoder.AppendComma(b)
return b, nil
}
header := (*emptyInterface)(unsafe.Pointer(&v))
@ -266,33 +241,33 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption
return buf, nil
}
func encodeIndent(ctx *encodeRuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
b := ctx.buf[:0]
func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
b := ctx.Buf[:0]
if v == nil {
b = encodeNull(b)
b = encodeIndentComma(b)
b = encoder.AppendNull(b)
b = encoder.AppendCommaIndent(b)
return b, nil
}
header := (*emptyInterface)(unsafe.Pointer(&v))
typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := encodeCompileToGetCodeSet(typeptr)
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
if err != nil {
return nil, err
}
p := uintptr(header.ptr)
ctx.init(p, codeSet.codeLength)
ctx.Init(p, codeSet.CodeLength)
buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent, opt)
ctx.keepRefs = append(ctx.keepRefs, header.ptr)
ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
if err != nil {
return nil, err
}
ctx.buf = buf
ctx.Buf = buf
return buf, nil
}
@ -303,227 +278,11 @@ func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.Opcod
return vm.Run(ctx, b, codeSet, encoder.Option(opt))
}
func encodeRunIndentCode(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) {
ctx.prefix = []byte(prefix)
ctx.indentStr = []byte(indent)
func encodeRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) {
ctx.Prefix = []byte(prefix)
ctx.IndentStr = []byte(indent)
if (opt & EncodeOptionHTMLEscape) != 0 {
return encodeRunEscapedIndent(ctx, b, codeSet, opt)
return vm_escaped_indent.Run(ctx, b, codeSet, encoder.Option(opt))
}
return encodeRunIndent(ctx, b, codeSet, opt)
}
func encodeFloat32(b []byte, v float32) []byte {
f64 := float64(v)
abs := math.Abs(f64)
fmt := byte('f')
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs != 0 {
f32 := float32(abs)
if f32 < 1e-6 || f32 >= 1e21 {
fmt = 'e'
}
}
return strconv.AppendFloat(b, f64, fmt, -1, 32)
}
func encodeFloat64(b []byte, v float64) []byte {
abs := math.Abs(v)
fmt := byte('f')
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs != 0 {
if abs < 1e-6 || abs >= 1e21 {
fmt = 'e'
}
}
return strconv.AppendFloat(b, v, fmt, -1, 64)
}
func encodeBool(b []byte, v bool) []byte {
if v {
return append(b, "true"...)
}
return append(b, "false"...)
}
func encodeNull(b []byte) []byte {
return append(b, "null"...)
}
func encodeComma(b []byte) []byte {
return append(b, ',')
}
func encodeIndentComma(b []byte) []byte {
return append(b, ',', '\n')
}
func appendStructEnd(b []byte) []byte {
return append(b, '}', ',')
}
func appendStructEndIndent(ctx *encodeRuntimeContext, b []byte, indent int) []byte {
b = append(b, '\n')
b = append(b, ctx.prefix...)
b = append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
return append(b, '}', ',', '\n')
}
func encodeByteSlice(b []byte, src []byte) []byte {
encodedLen := base64.StdEncoding.EncodedLen(len(src))
b = append(b, '"')
pos := len(b)
remainLen := cap(b[pos:])
var buf []byte
if remainLen > encodedLen {
buf = b[pos : pos+encodedLen]
} else {
buf = make([]byte, encodedLen)
}
base64.StdEncoding.Encode(buf, src)
return append(append(b, buf...), '"')
}
func encodeNumber(b []byte, n Number) ([]byte, error) {
if len(n) == 0 {
return append(b, '0'), nil
}
for i := 0; i < len(n); i++ {
if !floatTable[n[i]] {
return nil, fmt.Errorf("json: invalid number literal %q", n)
}
}
b = append(b, n...)
return b, nil
}
func appendIndent(ctx *encodeRuntimeContext, b []byte, indent int) []byte {
b = append(b, ctx.prefix...)
return append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
}
func encodeIsNilForMarshaler(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
}
func encodeMarshalJSON(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.addrForMarshaler {
if rv.CanAddr() {
rv = rv.Addr()
} else {
newV := reflect.New(rv.Type())
newV.Elem().Set(rv)
rv = newV
}
}
v = rv.Interface()
marshaler, ok := v.(Marshaler)
if !ok {
return encodeNull(b), nil
}
bb, err := marshaler.MarshalJSON()
if err != nil {
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
buf := bytes.NewBuffer(b)
//TODO: we should validate buffer with `compact`
if err := compact(buf, bb, escape); err != nil {
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
return buf.Bytes(), nil
}
func encodeMarshalJSONIndent(ctx *encodeRuntimeContext, code *opcode, b []byte, v interface{}, indent int, escape bool) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.addrForMarshaler {
if rv.CanAddr() {
rv = rv.Addr()
} else {
newV := reflect.New(rv.Type())
newV.Elem().Set(rv)
rv = newV
}
}
v = rv.Interface()
marshaler, ok := v.(Marshaler)
if !ok {
return encodeNull(b), nil
}
bb, err := marshaler.MarshalJSON()
if err != nil {
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
var compactBuf bytes.Buffer
if err := compact(&compactBuf, bb, escape); err != nil {
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
var indentBuf bytes.Buffer
if err := encodeWithIndent(
&indentBuf,
compactBuf.Bytes(),
string(ctx.prefix)+strings.Repeat(string(ctx.indentStr), ctx.baseIndent+indent+1),
string(ctx.indentStr),
); err != nil {
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
return append(b, indentBuf.Bytes()...), nil
}
func encodeMarshalText(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.addrForMarshaler {
if rv.CanAddr() {
rv = rv.Addr()
} else {
newV := reflect.New(rv.Type())
newV.Elem().Set(rv)
rv = newV
}
}
v = rv.Interface()
marshaler, ok := v.(encoding.TextMarshaler)
if !ok {
return encodeNull(b), nil
}
bytes, err := marshaler.MarshalText()
if err != nil {
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
if escape {
return encodeEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
}
return encodeNoEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
}
func encodeMarshalTextIndent(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.addrForMarshaler {
if rv.CanAddr() {
rv = rv.Addr()
} else {
newV := reflect.New(rv.Type())
newV.Elem().Set(rv)
rv = newV
}
}
v = rv.Interface()
marshaler, ok := v.(encoding.TextMarshaler)
if !ok {
return encodeNull(b), nil
}
bytes, err := marshaler.MarshalText()
if err != nil {
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
if escape {
return encodeEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
}
return encodeNoEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
return vm_indent.Run(ctx, b, codeSet, encoder.Option(opt))
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@ import (
"math"
"reflect"
"strconv"
"strings"
"sync"
"unsafe"
@ -439,6 +440,42 @@ func AppendMarshalJSON(code *Opcode, b []byte, v interface{}, escape bool) ([]by
return buf.Bytes(), nil
}
func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}, indent int, escape bool) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.AddrForMarshaler {
if rv.CanAddr() {
rv = rv.Addr()
} else {
newV := reflect.New(rv.Type())
newV.Elem().Set(rv)
rv = newV
}
}
v = rv.Interface()
marshaler, ok := v.(json.Marshaler)
if !ok {
return AppendNull(b), nil
}
bb, err := marshaler.MarshalJSON()
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
var compactBuf bytes.Buffer
if err := compact(&compactBuf, bb, escape); err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
var indentBuf bytes.Buffer
if err := encodeIndent(
&indentBuf,
compactBuf.Bytes(),
string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), ctx.BaseIndent+indent+1),
string(ctx.IndentStr),
); err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
return append(b, indentBuf.Bytes()...), nil
}
func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.AddrForMarshaler {
@ -465,6 +502,32 @@ func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]by
return AppendString(b, *(*string)(unsafe.Pointer(&bytes))), nil
}
func AppendMarshalTextIndent(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.AddrForMarshaler {
if rv.CanAddr() {
rv = rv.Addr()
} else {
newV := reflect.New(rv.Type())
newV.Elem().Set(rv)
rv = newV
}
}
v = rv.Interface()
marshaler, ok := v.(encoding.TextMarshaler)
if !ok {
return AppendNull(b), nil
}
bytes, err := marshaler.MarshalText()
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
if escape {
return AppendEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
}
return AppendString(b, *(*string)(unsafe.Pointer(&bytes))), nil
}
func AppendNull(b []byte) []byte {
return append(b, "null"...)
}
@ -473,10 +536,26 @@ func AppendComma(b []byte) []byte {
return append(b, ',')
}
func AppendCommaIndent(b []byte) []byte {
return append(b, ',', '\n')
}
func AppendStructEnd(b []byte) []byte {
return append(b, '}', ',')
}
func AppendStructEndIndent(ctx *RuntimeContext, b []byte, indent int) []byte {
b = append(b, '\n')
b = append(b, ctx.Prefix...)
b = append(b, bytes.Repeat(ctx.IndentStr, ctx.BaseIndent+indent)...)
return append(b, '}', ',', '\n')
}
func AppendIndent(ctx *RuntimeContext, b []byte, indent int) []byte {
b = append(b, ctx.Prefix...)
return append(b, bytes.Repeat(ctx.IndentStr, ctx.BaseIndent+indent)...)
}
func IsNilForMarshaler(v interface{}) bool {
rv := reflect.ValueOf(v)
switch rv.Kind() {

112
internal/encoder/indent.go Normal file
View File

@ -0,0 +1,112 @@
package encoder
import (
"bytes"
"github.com/goccy/go-json/internal/errors"
)
func encodeIndent(dst *bytes.Buffer, src []byte, prefix, indentStr string) error {
length := int64(len(src))
indentNum := 0
indentBytes := []byte(indentStr)
for cursor := int64(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++
if err := dst.WriteByte(src[cursor]); err != nil {
return err
}
switch src[cursor] {
case '\\':
cursor++
if err := dst.WriteByte(src[cursor]); err != nil {
return err
}
case '"':
goto LOOP_END
case '\000':
return errors.ErrUnexpectedEndOfJSON("string", length)
}
}
case '{':
if cursor+1 < length && src[cursor+1] == '}' {
if _, err := dst.Write([]byte{'{', '}'}); err != nil {
return err
}
cursor++
} else {
indentNum++
b := []byte{c, '\n'}
b = append(b, prefix...)
b = append(b, bytes.Repeat(indentBytes, indentNum)...)
if _, err := dst.Write(b); err != nil {
return err
}
}
case '}':
indentNum--
if indentNum < 0 {
return errors.ErrInvalidCharacter('}', "}", cursor)
}
b := []byte{'\n'}
b = append(b, prefix...)
b = append(b, bytes.Repeat(indentBytes, indentNum)...)
b = append(b, c)
if _, err := dst.Write(b); err != nil {
return err
}
case '[':
if cursor+1 < length && src[cursor+1] == ']' {
if _, err := dst.Write([]byte{'[', ']'}); err != nil {
return err
}
cursor++
} else {
indentNum++
b := []byte{c, '\n'}
b = append(b, prefix...)
b = append(b, bytes.Repeat(indentBytes, indentNum)...)
if _, err := dst.Write(b); err != nil {
return err
}
}
case ']':
indentNum--
if indentNum < 0 {
return errors.ErrInvalidCharacter(']', "]", cursor)
}
b := []byte{'\n'}
b = append(b, prefix...)
b = append(b, bytes.Repeat(indentBytes, indentNum)...)
b = append(b, c)
if _, err := dst.Write(b); err != nil {
return err
}
case ':':
if _, err := dst.Write([]byte{':', ' '}); err != nil {
return err
}
case ',':
b := []byte{',', '\n'}
b = append(b, prefix...)
b = append(b, bytes.Repeat(indentBytes, indentNum)...)
if _, err := dst.Write(b); err != nil {
return err
}
default:
if err := dst.WriteByte(c); err != nil {
return err
}
}
LOOP_END:
}
return nil
}

View File

@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
if p == 0 {
b = appendNull(b)
b = appendComma(b)
code = code.Next
code = code.End.Next
break
}
store(ctxptr, code.Idx, p)
@ -3794,7 +3794,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx)
p += code.Offset
slice := ptrToSlice(p)
if p == 0 || slice.Data == nil {
if slice.Data == nil {
code = code.NextField
} else {
b = append(b, code.Key...)
@ -3820,12 +3820,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpStructFieldMap, encoder.OpStructFieldStringTagMap:
b = append(b, code.Key...)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum+1)
p = ptrToPtr(p + code.Offset)
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyMap:
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum+1)
p = ptrToPtr(p + code.Offset)
if p == 0 || maplen(ptrToUnsafePtr(p)) == 0 {
code = code.NextField
} else {

View File

@ -3794,7 +3794,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
p := load(ctxptr, code.HeadIdx)
p += code.Offset
slice := ptrToSlice(p)
if p == 0 || slice.Data == nil {
if slice.Data == nil {
code = code.NextField
} else {
b = append(b, code.EscapedKey...)
@ -3820,12 +3820,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
case encoder.OpStructFieldMap, encoder.OpStructFieldStringTagMap:
b = append(b, code.EscapedKey...)
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum+1)
p = ptrToPtr(p + code.Offset)
code = code.Next
store(ctxptr, code.Idx, p)
case encoder.OpStructFieldOmitEmptyMap:
p := load(ctxptr, code.HeadIdx)
p = ptrToNPtr(p+code.Offset, code.PtrNum+1)
p = ptrToPtr(p + code.Offset)
if p == 0 || maplen(ptrToUnsafePtr(p)) == 0 {
code = code.NextField
} else {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff