mirror of https://github.com/goccy/go-json.git
Move encoder source to internal package
This commit is contained in:
parent
cccf9f9f33
commit
9cbe7b3991
299
encode.go
299
encode.go
|
@ -1,21 +1,15 @@
|
||||||
package json
|
package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"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"
|
"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.
|
// An Encoder writes JSON values to an output stream.
|
||||||
|
@ -41,15 +35,6 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
encRuntimeContextPool = sync.Pool{
|
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{} {
|
New: func() interface{} {
|
||||||
return &encoder.RuntimeContext{
|
return &encoder.RuntimeContext{
|
||||||
Buf: make([]byte, 0, bufSize),
|
Buf: make([]byte, 0, bufSize),
|
||||||
|
@ -60,22 +45,14 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func takeEncodeRuntimeContext() *encodeRuntimeContext {
|
func takeEncodeRuntimeContext() *encoder.RuntimeContext {
|
||||||
return encRuntimeContextPool.Get().(*encodeRuntimeContext)
|
return encRuntimeContextPool.Get().(*encoder.RuntimeContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
func releaseEncodeRuntimeContext(ctx *encodeRuntimeContext) {
|
func releaseEncodeRuntimeContext(ctx *encoder.RuntimeContext) {
|
||||||
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}
|
||||||
|
@ -98,7 +75,7 @@ func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc)
|
||||||
return err
|
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
|
var opt EncodeOption
|
||||||
if e.enabledHTMLEscape {
|
if e.enabledHTMLEscape {
|
||||||
opt |= EncodeOptionHTMLEscape
|
opt |= EncodeOptionHTMLEscape
|
||||||
|
@ -113,9 +90,7 @@ 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
|
||||||
|
@ -153,11 +128,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 := takeEncodeRuntimeContext2()
|
ctx := takeEncodeRuntimeContext()
|
||||||
|
|
||||||
buf, err := encode(ctx, v, opt|EncodeOptionHTMLEscape)
|
buf, err := encode(ctx, v, opt|EncodeOptionHTMLEscape)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
releaseEncodeRuntimeContext2(ctx)
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,16 +144,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)
|
||||||
|
|
||||||
releaseEncodeRuntimeContext2(ctx)
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return copied, nil
|
return copied, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
|
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
ctx := takeEncodeRuntimeContext2()
|
ctx := takeEncodeRuntimeContext()
|
||||||
|
|
||||||
buf, err := encodeNoEscape(ctx, v, opt|EncodeOptionHTMLEscape)
|
buf, err := encodeNoEscape(ctx, v, opt|EncodeOptionHTMLEscape)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
releaseEncodeRuntimeContext2(ctx)
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +165,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)
|
||||||
|
|
||||||
releaseEncodeRuntimeContext2(ctx)
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return copied, nil
|
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) {
|
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 = encoder.AppendNull(b)
|
||||||
b = encodeComma(b)
|
b = encoder.AppendComma(b)
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
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) {
|
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 = encoder.AppendNull(b)
|
||||||
b = encodeComma(b)
|
b = encoder.AppendComma(b)
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||||
|
@ -266,33 +241,33 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeIndent(ctx *encodeRuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||||
b := ctx.buf[:0]
|
b := ctx.Buf[:0]
|
||||||
if v == nil {
|
if v == nil {
|
||||||
b = encodeNull(b)
|
b = encoder.AppendNull(b)
|
||||||
b = encodeIndentComma(b)
|
b = encoder.AppendCommaIndent(b)
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||||
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 := encodeRunIndentCode(ctx, b, codeSet, prefix, indent, opt)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.buf = buf
|
ctx.Buf = buf
|
||||||
return buf, nil
|
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))
|
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 *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||||
ctx.prefix = []byte(prefix)
|
ctx.Prefix = []byte(prefix)
|
||||||
ctx.indentStr = []byte(indent)
|
ctx.IndentStr = []byte(indent)
|
||||||
if (opt & EncodeOptionHTMLEscape) != 0 {
|
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)
|
return vm_indent.Run(ctx, b, codeSet, encoder.Option(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
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
4532
encode_vm_indent.go
4532
encode_vm_indent.go
File diff suppressed because it is too large
Load Diff
|
@ -9,6 +9,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
@ -439,6 +440,42 @@ func AppendMarshalJSON(code *Opcode, b []byte, v interface{}, escape bool) ([]by
|
||||||
return buf.Bytes(), nil
|
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) {
|
func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||||
if code.AddrForMarshaler {
|
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
|
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 {
|
func AppendNull(b []byte) []byte {
|
||||||
return append(b, "null"...)
|
return append(b, "null"...)
|
||||||
}
|
}
|
||||||
|
@ -473,10 +536,26 @@ func AppendComma(b []byte) []byte {
|
||||||
return append(b, ',')
|
return append(b, ',')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AppendCommaIndent(b []byte) []byte {
|
||||||
|
return append(b, ',', '\n')
|
||||||
|
}
|
||||||
|
|
||||||
func AppendStructEnd(b []byte) []byte {
|
func AppendStructEnd(b []byte) []byte {
|
||||||
return append(b, '}', ',')
|
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 {
|
func IsNilForMarshaler(v interface{}) bool {
|
||||||
rv := reflect.ValueOf(v)
|
rv := reflect.ValueOf(v)
|
||||||
switch rv.Kind() {
|
switch rv.Kind() {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -409,7 +409,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
|
||||||
if p == 0 {
|
if p == 0 {
|
||||||
b = appendNull(b)
|
b = appendNull(b)
|
||||||
b = appendComma(b)
|
b = appendComma(b)
|
||||||
code = code.Next
|
code = code.End.Next
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
store(ctxptr, code.Idx, p)
|
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 := load(ctxptr, code.HeadIdx)
|
||||||
p += code.Offset
|
p += code.Offset
|
||||||
slice := ptrToSlice(p)
|
slice := ptrToSlice(p)
|
||||||
if p == 0 || slice.Data == nil {
|
if slice.Data == nil {
|
||||||
code = code.NextField
|
code = code.NextField
|
||||||
} else {
|
} else {
|
||||||
b = append(b, code.Key...)
|
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:
|
case encoder.OpStructFieldMap, encoder.OpStructFieldStringTagMap:
|
||||||
b = append(b, code.Key...)
|
b = append(b, code.Key...)
|
||||||
p := load(ctxptr, code.HeadIdx)
|
p := load(ctxptr, code.HeadIdx)
|
||||||
p = ptrToNPtr(p+code.Offset, code.PtrNum+1)
|
p = ptrToPtr(p + code.Offset)
|
||||||
code = code.Next
|
code = code.Next
|
||||||
store(ctxptr, code.Idx, p)
|
store(ctxptr, code.Idx, p)
|
||||||
case encoder.OpStructFieldOmitEmptyMap:
|
case encoder.OpStructFieldOmitEmptyMap:
|
||||||
p := load(ctxptr, code.HeadIdx)
|
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 {
|
if p == 0 || maplen(ptrToUnsafePtr(p)) == 0 {
|
||||||
code = code.NextField
|
code = code.NextField
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3794,7 +3794,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt
|
||||||
p := load(ctxptr, code.HeadIdx)
|
p := load(ctxptr, code.HeadIdx)
|
||||||
p += code.Offset
|
p += code.Offset
|
||||||
slice := ptrToSlice(p)
|
slice := ptrToSlice(p)
|
||||||
if p == 0 || slice.Data == nil {
|
if slice.Data == nil {
|
||||||
code = code.NextField
|
code = code.NextField
|
||||||
} else {
|
} else {
|
||||||
b = append(b, code.EscapedKey...)
|
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:
|
case encoder.OpStructFieldMap, encoder.OpStructFieldStringTagMap:
|
||||||
b = append(b, code.EscapedKey...)
|
b = append(b, code.EscapedKey...)
|
||||||
p := load(ctxptr, code.HeadIdx)
|
p := load(ctxptr, code.HeadIdx)
|
||||||
p = ptrToNPtr(p+code.Offset, code.PtrNum+1)
|
p = ptrToPtr(p + code.Offset)
|
||||||
code = code.Next
|
code = code.Next
|
||||||
store(ctxptr, code.Idx, p)
|
store(ctxptr, code.Idx, p)
|
||||||
case encoder.OpStructFieldOmitEmptyMap:
|
case encoder.OpStructFieldOmitEmptyMap:
|
||||||
p := load(ctxptr, code.HeadIdx)
|
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 {
|
if p == 0 || maplen(ptrToUnsafePtr(p)) == 0 {
|
||||||
code = code.NextField
|
code = code.NextField
|
||||||
} else {
|
} else {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue