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
|
||||
|
||||
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
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"
|
||||
"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() {
|
||||
|
|
|
@ -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 {
|
||||
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 {
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue