Add MarshalIndent

This commit is contained in:
Masaaki Goshima 2020-05-02 23:35:41 +09:00
parent 03a21193fc
commit 3d7267abc8
6 changed files with 2210 additions and 252 deletions

122
encode.go
View File

@ -1,6 +1,7 @@
package json
import (
"bytes"
"io"
"reflect"
"strconv"
@ -10,9 +11,13 @@ import (
// An Encoder writes JSON values to an output stream.
type Encoder struct {
w io.Writer
buf []byte
pool sync.Pool
w io.Writer
buf []byte
pool sync.Pool
enabledIndent bool
prefix []byte
indentStr []byte
indent int
}
const (
@ -23,14 +28,19 @@ type opcodeMap struct {
sync.Map
}
func (m *opcodeMap) Get(k *rtype) *opcode {
type opcodeSet struct {
codeIndent *opcode
code *opcode
}
func (m *opcodeMap) get(k *rtype) *opcodeSet {
if v, ok := m.Load(k); ok {
return v.(*opcode)
return v.(*opcodeSet)
}
return nil
}
func (m *opcodeMap) Set(k *rtype, op *opcode) {
func (m *opcodeMap) set(k *rtype, op *opcodeSet) {
m.Store(k, op)
}
@ -55,6 +65,7 @@ func init() {
func NewEncoder(w io.Writer) *Encoder {
enc := encPool.Get().(*Encoder)
enc.w = w
enc.indent = 0
enc.reset()
return enc
}
@ -72,15 +83,6 @@ func (e *Encoder) Encode(v interface{}) error {
return nil
}
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
if err := e.encode(v); err != nil {
return nil, err
}
copied := make([]byte, len(e.buf))
copy(copied, e.buf)
return copied, nil
}
// SetEscapeHTML specifies whether problematic HTML characters should be escaped inside JSON quoted strings.
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e to avoid certain safety problems that can arise when embedding JSON in HTML.
//
@ -92,7 +94,13 @@ func (e *Encoder) SetEscapeHTML(on bool) {
// SetIndent instructs the encoder to format each subsequent encoded value as if indented by the package-level function Indent(dst, src, prefix, indent).
// Calling SetIndent("", "") disables indentation.
func (e *Encoder) SetIndent(prefix, indent string) {
if prefix == "" && indent == "" {
e.enabledIndent = false
return
}
e.prefix = []byte(prefix)
e.indentStr = []byte(indent)
e.enabledIndent = true
}
func (e *Encoder) release() {
@ -104,6 +112,63 @@ func (e *Encoder) reset() {
e.buf = e.buf[:0]
}
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
if err := e.encode(v); err != nil {
return nil, err
}
if e.enabledIndent {
last := len(e.buf) - 1
if e.buf[last] == '\n' {
last--
}
length := last + 1
copied := make([]byte, length)
copy(copied, e.buf[0:length])
return copied, nil
}
copied := make([]byte, len(e.buf))
copy(copied, e.buf)
return copied, nil
}
func (e *Encoder) encode(v interface{}) error {
header := (*interfaceHeader)(unsafe.Pointer(&v))
typ := header.typ
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if codeSet := cachedOpcode.get(typ); codeSet != nil {
var code *opcode
if e.enabledIndent {
code = codeSet.codeIndent
} else {
code = codeSet.code
}
p := uintptr(header.ptr)
code.ptr = p
if err := e.run(code); err != nil {
return err
}
return nil
}
codeIndent, err := e.compile(typ, true)
if err != nil {
return err
}
code, err := e.compile(typ, false)
if err != nil {
return err
}
codeSet := &opcodeSet{codeIndent: codeIndent, code: code}
cachedOpcode.set(typ, codeSet)
p := uintptr(header.ptr)
code.ptr = p
if e.enabledIndent {
return e.run(codeIndent)
}
return e.run(code)
}
func (e *Encoder) encodeInt(v int) {
e.encodeInt64(int64(v))
}
@ -169,26 +234,7 @@ func (e *Encoder) encodeByte(b byte) {
e.buf = append(e.buf, b)
}
func (e *Encoder) encode(v interface{}) error {
header := (*interfaceHeader)(unsafe.Pointer(&v))
typ := header.typ
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if code := cachedOpcode.Get(typ); code != nil {
p := uintptr(header.ptr)
code.ptr = p
if err := e.run(code); err != nil {
return err
}
return nil
}
code, err := e.compile(typ)
if err != nil {
return err
}
cachedOpcode.Set(typ, code)
p := uintptr(header.ptr)
code.ptr = p
return e.run(code)
func (e *Encoder) encodeIndent(indent int) {
e.buf = append(e.buf, e.prefix...)
e.buf = append(e.buf, bytes.Repeat(e.indentStr, indent)...)
}

View File

@ -9,18 +9,18 @@ import (
"golang.org/x/xerrors"
)
func (e *Encoder) compile(typ *rtype) (*opcode, error) {
func (e *Encoder) compile(typ *rtype, withIndent bool) (*opcode, error) {
switch typ.Kind() {
case reflect.Ptr:
return e.compilePtr(typ)
return e.compilePtr(typ, withIndent)
case reflect.Slice:
return e.compileSlice(typ)
return e.compileSlice(typ, withIndent)
case reflect.Array:
return e.compileArray(typ)
return e.compileArray(typ, withIndent)
case reflect.Map:
return e.compileMap(typ)
return e.compileMap(typ, withIndent)
case reflect.Struct:
return e.compileStruct(typ)
return e.compileStruct(typ, withIndent)
case reflect.Int:
return e.compileInt(typ)
case reflect.Int8:
@ -119,14 +119,75 @@ func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
code.op = opStructFieldPtrHeadStringOmitEmpty
case opStructFieldHeadBoolOmitEmpty:
code.op = opStructFieldPtrHeadBoolOmitEmpty
case opStructFieldHeadIndent:
code.op = opStructFieldPtrHeadIndent
case opStructFieldHeadIntIndent:
code.op = opStructFieldPtrHeadIntIndent
case opStructFieldHeadInt8Indent:
code.op = opStructFieldPtrHeadInt8Indent
case opStructFieldHeadInt16Indent:
code.op = opStructFieldPtrHeadInt16Indent
case opStructFieldHeadInt32Indent:
code.op = opStructFieldPtrHeadInt32Indent
case opStructFieldHeadInt64Indent:
code.op = opStructFieldPtrHeadInt64Indent
case opStructFieldHeadUintIndent:
code.op = opStructFieldPtrHeadUintIndent
case opStructFieldHeadUint8Indent:
code.op = opStructFieldPtrHeadUint8Indent
case opStructFieldHeadUint16Indent:
code.op = opStructFieldPtrHeadUint16Indent
case opStructFieldHeadUint32Indent:
code.op = opStructFieldPtrHeadUint32Indent
case opStructFieldHeadUint64Indent:
code.op = opStructFieldPtrHeadUint64Indent
case opStructFieldHeadFloat32Indent:
code.op = opStructFieldPtrHeadFloat32Indent
case opStructFieldHeadFloat64Indent:
code.op = opStructFieldPtrHeadFloat64Indent
case opStructFieldHeadStringIndent:
code.op = opStructFieldPtrHeadStringIndent
case opStructFieldHeadBoolIndent:
code.op = opStructFieldPtrHeadBoolIndent
case opStructFieldHeadOmitEmptyIndent:
code.op = opStructFieldPtrHeadOmitEmptyIndent
case opStructFieldHeadIntOmitEmptyIndent:
code.op = opStructFieldPtrHeadIntOmitEmptyIndent
case opStructFieldHeadInt8OmitEmptyIndent:
code.op = opStructFieldPtrHeadInt8OmitEmptyIndent
case opStructFieldHeadInt16OmitEmptyIndent:
code.op = opStructFieldPtrHeadInt16OmitEmptyIndent
case opStructFieldHeadInt32OmitEmptyIndent:
code.op = opStructFieldPtrHeadInt32OmitEmptyIndent
case opStructFieldHeadInt64OmitEmptyIndent:
code.op = opStructFieldPtrHeadInt64OmitEmptyIndent
case opStructFieldHeadUintOmitEmptyIndent:
code.op = opStructFieldPtrHeadUintOmitEmptyIndent
case opStructFieldHeadUint8OmitEmptyIndent:
code.op = opStructFieldPtrHeadUint8OmitEmptyIndent
case opStructFieldHeadUint16OmitEmptyIndent:
code.op = opStructFieldPtrHeadUint16OmitEmptyIndent
case opStructFieldHeadUint32OmitEmptyIndent:
code.op = opStructFieldPtrHeadUint32OmitEmptyIndent
case opStructFieldHeadUint64OmitEmptyIndent:
code.op = opStructFieldPtrHeadUint64OmitEmptyIndent
case opStructFieldHeadFloat32OmitEmptyIndent:
code.op = opStructFieldPtrHeadFloat32OmitEmptyIndent
case opStructFieldHeadFloat64OmitEmptyIndent:
code.op = opStructFieldPtrHeadFloat64OmitEmptyIndent
case opStructFieldHeadStringOmitEmptyIndent:
code.op = opStructFieldPtrHeadStringOmitEmptyIndent
case opStructFieldHeadBoolOmitEmptyIndent:
code.op = opStructFieldPtrHeadBoolOmitEmptyIndent
default:
return newOpCode(opPtr, typ, code)
return newOpCode(opPtr, typ, e.indent, code)
}
return code
}
func (e *Encoder) compilePtr(typ *rtype) (*opcode, error) {
code, err := e.compile(typ.Elem())
func (e *Encoder) compilePtr(typ *rtype, withIndent bool) (*opcode, error) {
code, err := e.compile(typ.Elem(), withIndent)
if err != nil {
return nil, err
}
@ -134,69 +195,73 @@ func (e *Encoder) compilePtr(typ *rtype) (*opcode, error) {
}
func (e *Encoder) compileInt(typ *rtype) (*opcode, error) {
return newOpCode(opInt, typ, newEndOp()), nil
return newOpCode(opInt, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileInt8(typ *rtype) (*opcode, error) {
return newOpCode(opInt8, typ, newEndOp()), nil
return newOpCode(opInt8, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileInt16(typ *rtype) (*opcode, error) {
return newOpCode(opInt16, typ, newEndOp()), nil
return newOpCode(opInt16, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileInt32(typ *rtype) (*opcode, error) {
return newOpCode(opInt32, typ, newEndOp()), nil
return newOpCode(opInt32, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileInt64(typ *rtype) (*opcode, error) {
return newOpCode(opInt64, typ, newEndOp()), nil
return newOpCode(opInt64, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileUint(typ *rtype) (*opcode, error) {
return newOpCode(opUint, typ, newEndOp()), nil
return newOpCode(opUint, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileUint8(typ *rtype) (*opcode, error) {
return newOpCode(opUint8, typ, newEndOp()), nil
return newOpCode(opUint8, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileUint16(typ *rtype) (*opcode, error) {
return newOpCode(opUint16, typ, newEndOp()), nil
return newOpCode(opUint16, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileUint32(typ *rtype) (*opcode, error) {
return newOpCode(opUint32, typ, newEndOp()), nil
return newOpCode(opUint32, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileUint64(typ *rtype) (*opcode, error) {
return newOpCode(opUint64, typ, newEndOp()), nil
return newOpCode(opUint64, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileFloat32(typ *rtype) (*opcode, error) {
return newOpCode(opFloat32, typ, newEndOp()), nil
return newOpCode(opFloat32, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileFloat64(typ *rtype) (*opcode, error) {
return newOpCode(opFloat64, typ, newEndOp()), nil
return newOpCode(opFloat64, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileString(typ *rtype) (*opcode, error) {
return newOpCode(opString, typ, newEndOp()), nil
return newOpCode(opString, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileBool(typ *rtype) (*opcode, error) {
return newOpCode(opBool, typ, newEndOp()), nil
return newOpCode(opBool, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileInterface(typ *rtype) (*opcode, error) {
return newOpCode(opInterface, typ, newEndOp()), nil
return newOpCode(opInterface, typ, e.indent, newEndOp(e.indent)), nil
}
func (e *Encoder) compileSlice(typ *rtype) (*opcode, error) {
func (e *Encoder) compileSlice(typ *rtype, withIndent bool) (*opcode, error) {
elem := typ.Elem()
size := elem.Size()
code, err := e.compile(elem)
e.indent++
code, err := e.compile(elem, withIndent)
e.indent--
if err != nil {
return nil, err
}
@ -205,9 +270,20 @@ func (e *Encoder) compileSlice(typ *rtype) (*opcode, error) {
// ^ |
// |________|
header := newSliceHeaderCode()
elemCode := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElem}, size: size}
end := newOpCode(opSliceEnd, nil, newEndOp())
header := newSliceHeaderCode(e.indent)
elemCode := &sliceElemCode{
opcodeHeader: &opcodeHeader{
op: opSliceElem,
indent: e.indent,
},
size: size,
}
end := newOpCode(opSliceEnd, nil, e.indent, newEndOp(e.indent))
if withIndent {
header.op = opSliceHeadIndent
elemCode.op = opSliceElemIndent
end.op = opSliceEndIndent
}
header.elem = elemCode
header.end = end
@ -218,11 +294,15 @@ func (e *Encoder) compileSlice(typ *rtype) (*opcode, error) {
return (*opcode)(unsafe.Pointer(header)), nil
}
func (e *Encoder) compileArray(typ *rtype) (*opcode, error) {
func (e *Encoder) compileArray(typ *rtype, withIndent bool) (*opcode, error) {
elem := typ.Elem()
alen := typ.Len()
size := elem.Size()
code, err := e.compile(elem)
e.indent++
code, err := e.compile(elem, withIndent)
e.indent--
if err != nil {
return nil, err
}
@ -230,7 +310,7 @@ func (e *Encoder) compileArray(typ *rtype) (*opcode, error) {
// ^ |
// |________|
header := newArrayHeaderCode(alen)
header := newArrayHeaderCode(e.indent, alen)
elemCode := &arrayElemCode{
opcodeHeader: &opcodeHeader{
op: opArrayElem,
@ -238,7 +318,13 @@ func (e *Encoder) compileArray(typ *rtype) (*opcode, error) {
len: uintptr(alen),
size: size,
}
end := newOpCode(opArrayEnd, nil, newEndOp())
end := newOpCode(opArrayEnd, nil, e.indent, newEndOp(e.indent))
if withIndent {
header.op = opArrayHeadIndent
elemCode.op = opArrayElemIndent
end.op = opArrayEndIndent
}
header.elem = elemCode
header.end = end
@ -265,26 +351,38 @@ func mapiternext(it unsafe.Pointer)
//go:noescape
func maplen(m unsafe.Pointer) int
func (e *Encoder) compileMap(typ *rtype) (*opcode, error) {
func (e *Encoder) compileMap(typ *rtype, withIndent bool) (*opcode, error) {
// header => code => value => code => key => code => value => code => end
// ^ |
// |_______________________|
e.indent++
keyType := typ.Key()
keyCode, err := e.compile(keyType)
keyCode, err := e.compile(keyType, withIndent)
if err != nil {
return nil, err
}
valueType := typ.Elem()
valueCode, err := e.compile(valueType)
valueCode, err := e.compile(valueType, withIndent)
if err != nil {
return nil, err
}
header := newMapHeaderCode(typ)
key := newMapKeyCode()
value := newMapValueCode()
key := newMapKeyCode(e.indent)
value := newMapValueCode(e.indent)
e.indent--
header := newMapHeaderCode(typ, e.indent)
header.key = key
header.value = value
end := newOpCode(opMapEnd, nil, newEndOp())
end := newOpCode(opMapEnd, nil, e.indent, newEndOp(e.indent))
if withIndent {
header.op = opMapHeadIndent
key.op = opMapKeyIndent
value.op = opMapValueIndent
end.op = opMapEndIndent
}
header.next = keyCode
keyCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(value))
@ -314,7 +412,297 @@ func (e *Encoder) isIgnoredStructField(field reflect.StructField) bool {
return false
}
func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
func (e *Encoder) optimizeStructHeaderOmitEmptyIndent(op opType) opType {
switch op {
case opInt:
return opStructFieldHeadIntOmitEmptyIndent
case opInt8:
return opStructFieldHeadInt8OmitEmptyIndent
case opInt16:
return opStructFieldHeadInt16OmitEmptyIndent
case opInt32:
return opStructFieldHeadInt32OmitEmptyIndent
case opInt64:
return opStructFieldHeadInt64OmitEmptyIndent
case opUint:
return opStructFieldHeadUintOmitEmptyIndent
case opUint8:
return opStructFieldHeadUint8OmitEmptyIndent
case opUint16:
return opStructFieldHeadUint16OmitEmptyIndent
case opUint32:
return opStructFieldHeadUint32OmitEmptyIndent
case opUint64:
return opStructFieldHeadUint64OmitEmptyIndent
case opFloat32:
return opStructFieldHeadFloat32OmitEmptyIndent
case opFloat64:
return opStructFieldHeadFloat64OmitEmptyIndent
case opString:
return opStructFieldHeadStringOmitEmptyIndent
case opBool:
return opStructFieldHeadBoolOmitEmptyIndent
}
return opStructFieldHeadOmitEmptyIndent
}
func (e *Encoder) optimizeStructHeaderIndent(op opType, isOmitEmpty bool) opType {
if isOmitEmpty {
return e.optimizeStructHeaderOmitEmptyIndent(op)
}
switch op {
case opInt:
return opStructFieldHeadIntIndent
case opInt8:
return opStructFieldHeadInt8Indent
case opInt16:
return opStructFieldHeadInt16Indent
case opInt32:
return opStructFieldHeadInt32Indent
case opInt64:
return opStructFieldHeadInt64Indent
case opUint:
return opStructFieldHeadUintIndent
case opUint8:
return opStructFieldHeadUint8Indent
case opUint16:
return opStructFieldHeadUint16Indent
case opUint32:
return opStructFieldHeadUint32Indent
case opUint64:
return opStructFieldHeadUint64Indent
case opFloat32:
return opStructFieldHeadFloat32Indent
case opFloat64:
return opStructFieldHeadFloat64Indent
case opString:
return opStructFieldHeadStringIndent
case opBool:
return opStructFieldHeadBoolIndent
}
return opStructFieldHeadIndent
}
func (e *Encoder) optimizeStructHeaderOmitEmpty(op opType) opType {
switch op {
case opInt:
return opStructFieldHeadIntOmitEmpty
case opInt8:
return opStructFieldHeadInt8OmitEmpty
case opInt16:
return opStructFieldHeadInt16OmitEmpty
case opInt32:
return opStructFieldHeadInt32OmitEmpty
case opInt64:
return opStructFieldHeadInt64OmitEmpty
case opUint:
return opStructFieldHeadUintOmitEmpty
case opUint8:
return opStructFieldHeadUint8OmitEmpty
case opUint16:
return opStructFieldHeadUint16OmitEmpty
case opUint32:
return opStructFieldHeadUint32OmitEmpty
case opUint64:
return opStructFieldHeadUint64OmitEmpty
case opFloat32:
return opStructFieldHeadFloat32OmitEmpty
case opFloat64:
return opStructFieldHeadFloat64OmitEmpty
case opString:
return opStructFieldHeadStringOmitEmpty
case opBool:
return opStructFieldHeadBoolOmitEmpty
}
return opStructFieldHeadOmitEmpty
}
func (e *Encoder) optimizeStructHeader(op opType, isOmitEmpty, withIndent bool) opType {
if withIndent {
return e.optimizeStructHeaderIndent(op, isOmitEmpty)
}
if isOmitEmpty {
return e.optimizeStructHeaderOmitEmpty(op)
}
switch op {
case opInt:
return opStructFieldHeadInt
case opInt8:
return opStructFieldHeadInt8
case opInt16:
return opStructFieldHeadInt16
case opInt32:
return opStructFieldHeadInt32
case opInt64:
return opStructFieldHeadInt64
case opUint:
return opStructFieldHeadUint
case opUint8:
return opStructFieldHeadUint8
case opUint16:
return opStructFieldHeadUint16
case opUint32:
return opStructFieldHeadUint32
case opUint64:
return opStructFieldHeadUint64
case opFloat32:
return opStructFieldHeadFloat32
case opFloat64:
return opStructFieldHeadFloat64
case opString:
return opStructFieldHeadString
case opBool:
return opStructFieldHeadBool
}
return opStructFieldHead
}
func (e *Encoder) optimizeStructFieldOmitEmptyIndent(op opType) opType {
switch op {
case opInt:
return opStructFieldIntOmitEmptyIndent
case opInt8:
return opStructFieldInt8OmitEmptyIndent
case opInt16:
return opStructFieldInt16OmitEmptyIndent
case opInt32:
return opStructFieldInt32OmitEmptyIndent
case opInt64:
return opStructFieldInt64OmitEmptyIndent
case opUint:
return opStructFieldUintOmitEmptyIndent
case opUint8:
return opStructFieldUint8OmitEmptyIndent
case opUint16:
return opStructFieldUint16OmitEmptyIndent
case opUint32:
return opStructFieldUint32OmitEmptyIndent
case opUint64:
return opStructFieldUint64OmitEmptyIndent
case opFloat32:
return opStructFieldFloat32OmitEmptyIndent
case opFloat64:
return opStructFieldFloat64OmitEmptyIndent
case opString:
return opStructFieldStringOmitEmptyIndent
case opBool:
return opStructFieldBoolOmitEmptyIndent
}
return opStructFieldOmitEmptyIndent
}
func (e *Encoder) optimizeStructFieldIndent(op opType, isOmitEmpty bool) opType {
if isOmitEmpty {
return e.optimizeStructFieldOmitEmptyIndent(op)
}
switch op {
case opInt:
return opStructFieldIntIndent
case opInt8:
return opStructFieldInt8Indent
case opInt16:
return opStructFieldInt16Indent
case opInt32:
return opStructFieldInt32Indent
case opInt64:
return opStructFieldInt64Indent
case opUint:
return opStructFieldUintIndent
case opUint8:
return opStructFieldUint8Indent
case opUint16:
return opStructFieldUint16Indent
case opUint32:
return opStructFieldUint32Indent
case opUint64:
return opStructFieldUint64Indent
case opFloat32:
return opStructFieldFloat32Indent
case opFloat64:
return opStructFieldFloat64Indent
case opString:
return opStructFieldStringIndent
case opBool:
return opStructFieldBoolIndent
}
return opStructFieldIndent
}
func (e *Encoder) optimizeStructFieldOmitEmpty(op opType) opType {
switch op {
case opInt:
return opStructFieldIntOmitEmpty
case opInt8:
return opStructFieldInt8OmitEmpty
case opInt16:
return opStructFieldInt16OmitEmpty
case opInt32:
return opStructFieldInt32OmitEmpty
case opInt64:
return opStructFieldInt64OmitEmpty
case opUint:
return opStructFieldUintOmitEmpty
case opUint8:
return opStructFieldUint8OmitEmpty
case opUint16:
return opStructFieldUint16OmitEmpty
case opUint32:
return opStructFieldUint32OmitEmpty
case opUint64:
return opStructFieldUint64OmitEmpty
case opFloat32:
return opStructFieldFloat32OmitEmpty
case opFloat64:
return opStructFieldFloat64OmitEmpty
case opString:
return opStructFieldStringOmitEmpty
case opBool:
return opStructFieldBoolOmitEmpty
}
return opStructFieldOmitEmpty
}
func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) opType {
if withIndent {
return e.optimizeStructFieldIndent(op, isOmitEmpty)
}
if isOmitEmpty {
return e.optimizeStructFieldOmitEmpty(op)
}
switch op {
case opInt:
return opStructFieldInt
case opInt8:
return opStructFieldInt8
case opInt16:
return opStructFieldInt16
case opInt32:
return opStructFieldInt32
case opInt64:
return opStructFieldInt64
case opUint:
return opStructFieldUint
case opUint8:
return opStructFieldUint8
case opUint16:
return opStructFieldUint16
case opUint32:
return opStructFieldUint32
case opUint64:
return opStructFieldUint64
case opFloat32:
return opStructFieldFloat32
case opFloat64:
return opStructFieldFloat64
case opString:
return opStructFieldString
case opBool:
return opStructFieldBool
}
return opStructField
}
func (e *Encoder) compileStruct(typ *rtype, withIndent bool) (*opcode, error) {
// header => code => structField => code => end
// ^ |
// |__________|
@ -325,6 +713,7 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
code *opcode
prevField *structFieldCode
)
e.indent++
for i := 0; i < fieldNum; i++ {
field := typ.Field(i)
if e.isIgnoredStructField(field) {
@ -343,171 +732,58 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
isOmitEmpty = opts[1] == "omitempty"
}
fieldType := type2rtype(field.Type)
valueCode, err := e.compile(fieldType)
valueCode, err := e.compile(fieldType, withIndent)
if err != nil {
return nil, err
}
key := fmt.Sprintf(`"%s":`, keyName)
fieldCode := &structFieldCode{
opcodeHeader: &opcodeHeader{
typ: fieldType,
next: valueCode,
typ: fieldType,
next: valueCode,
indent: e.indent,
},
key: []byte(key),
offset: field.Offset,
}
if fieldIdx == 0 {
fieldCode.indent--
head = fieldCode
code = (*opcode)(unsafe.Pointer(fieldCode))
prevField = fieldCode
if isOmitEmpty {
fieldCode.op = opStructFieldHeadOmitEmpty
switch valueCode.op {
case opInt:
fieldCode.op = opStructFieldHeadIntOmitEmpty
case opInt8:
fieldCode.op = opStructFieldHeadInt8OmitEmpty
case opInt16:
fieldCode.op = opStructFieldHeadInt16OmitEmpty
case opInt32:
fieldCode.op = opStructFieldHeadInt32OmitEmpty
case opInt64:
fieldCode.op = opStructFieldHeadInt64OmitEmpty
case opUint:
fieldCode.op = opStructFieldHeadUintOmitEmpty
case opUint8:
fieldCode.op = opStructFieldHeadUint8OmitEmpty
case opUint16:
fieldCode.op = opStructFieldHeadUint16OmitEmpty
case opUint32:
fieldCode.op = opStructFieldHeadUint32OmitEmpty
case opUint64:
fieldCode.op = opStructFieldHeadUint64OmitEmpty
case opFloat32:
fieldCode.op = opStructFieldHeadFloat32OmitEmpty
case opFloat64:
fieldCode.op = opStructFieldHeadFloat64OmitEmpty
case opString:
fieldCode.op = opStructFieldHeadStringOmitEmpty
case opBool:
fieldCode.op = opStructFieldHeadBoolOmitEmpty
default:
code = valueCode.beforeLastCode()
}
} else {
fieldCode.op = opStructFieldHead
switch valueCode.op {
case opInt:
fieldCode.op = opStructFieldHeadInt
case opInt8:
fieldCode.op = opStructFieldHeadInt8
case opInt16:
fieldCode.op = opStructFieldHeadInt16
case opInt32:
fieldCode.op = opStructFieldHeadInt32
case opInt64:
fieldCode.op = opStructFieldHeadInt64
case opUint:
fieldCode.op = opStructFieldHeadUint
case opUint8:
fieldCode.op = opStructFieldHeadUint8
case opUint16:
fieldCode.op = opStructFieldHeadUint16
case opUint32:
fieldCode.op = opStructFieldHeadUint32
case opUint64:
fieldCode.op = opStructFieldHeadUint64
case opFloat32:
fieldCode.op = opStructFieldHeadFloat32
case opFloat64:
fieldCode.op = opStructFieldHeadFloat64
case opString:
fieldCode.op = opStructFieldHeadString
case opBool:
fieldCode.op = opStructFieldHeadBool
default:
code = valueCode.beforeLastCode()
}
op := e.optimizeStructHeader(valueCode.op, isOmitEmpty, withIndent)
fieldCode.op = op
switch op {
case opStructFieldHead,
opStructFieldHeadOmitEmpty,
opStructFieldHeadIndent,
opStructFieldHeadOmitEmptyIndent:
code = valueCode.beforeLastCode()
}
} else {
fieldCode.op = opStructField
code.next = (*opcode)(unsafe.Pointer(fieldCode))
prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode))
prevField = fieldCode
code = (*opcode)(unsafe.Pointer(fieldCode))
if isOmitEmpty {
fieldCode.op = opStructFieldOmitEmpty
switch valueCode.op {
case opInt:
fieldCode.op = opStructFieldIntOmitEmpty
case opInt8:
fieldCode.op = opStructFieldInt8OmitEmpty
case opInt16:
fieldCode.op = opStructFieldInt16OmitEmpty
case opInt32:
fieldCode.op = opStructFieldInt32OmitEmpty
case opInt64:
fieldCode.op = opStructFieldInt64OmitEmpty
case opUint:
fieldCode.op = opStructFieldUintOmitEmpty
case opUint8:
fieldCode.op = opStructFieldUint8OmitEmpty
case opUint16:
fieldCode.op = opStructFieldUint16OmitEmpty
case opUint32:
fieldCode.op = opStructFieldUint32OmitEmpty
case opUint64:
fieldCode.op = opStructFieldUint64OmitEmpty
case opFloat32:
fieldCode.op = opStructFieldFloat32OmitEmpty
case opFloat64:
fieldCode.op = opStructFieldFloat64OmitEmpty
case opString:
fieldCode.op = opStructFieldStringOmitEmpty
case opBool:
fieldCode.op = opStructFieldBoolOmitEmpty
default:
code = valueCode.beforeLastCode()
}
} else {
switch valueCode.op {
case opInt:
fieldCode.op = opStructFieldInt
case opInt8:
fieldCode.op = opStructFieldInt8
case opInt16:
fieldCode.op = opStructFieldInt16
case opInt32:
fieldCode.op = opStructFieldInt32
case opInt64:
fieldCode.op = opStructFieldInt64
case opUint:
fieldCode.op = opStructFieldUint
case opUint8:
fieldCode.op = opStructFieldUint8
case opUint16:
fieldCode.op = opStructFieldUint16
case opUint32:
fieldCode.op = opStructFieldUint32
case opUint64:
fieldCode.op = opStructFieldUint64
case opFloat32:
fieldCode.op = opStructFieldFloat32
case opFloat64:
fieldCode.op = opStructFieldFloat64
case opString:
fieldCode.op = opStructFieldString
case opBool:
fieldCode.op = opStructFieldBool
default:
code = valueCode.beforeLastCode()
}
op := e.optimizeStructField(valueCode.op, isOmitEmpty, withIndent)
fieldCode.op = op
switch op {
case opStructField,
opStructFieldOmitEmpty,
opStructFieldIndent,
opStructFieldOmitEmptyIndent:
code = valueCode.beforeLastCode()
}
}
fieldIdx++
}
e.indent--
structEndCode := newOpCode(opStructEnd, nil, nil)
structEndCode := newOpCode(opStructEnd, nil, e.indent, nil)
if withIndent {
structEndCode.op = opStructEndIndent
}
if prevField != nil && prevField.nextField == nil {
prevField.nextField = structEndCode
@ -517,15 +793,19 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
if head == nil {
head = &structFieldCode{
opcodeHeader: &opcodeHeader{
op: opStructFieldHead,
typ: typ,
op: opStructFieldHead,
typ: typ,
indent: e.indent,
},
nextField: structEndCode,
}
if withIndent {
head.op = opStructFieldHeadIndent
}
code = (*opcode)(unsafe.Pointer(head))
}
head.end = structEndCode
code.next = structEndCode
structEndCode.next = newEndOp()
structEndCode.next = newEndOp(e.indent)
return (*opcode)(unsafe.Pointer(head)), nil
}

View File

@ -27,17 +27,33 @@ const (
opBool
opInterface
opPtr
opSliceHead
opSliceElem
opSliceEnd
opSliceHeadIndent
opSliceElemIndent
opSliceEndIndent
opArrayHead
opArrayElem
opArrayEnd
opArrayHeadIndent
opArrayElemIndent
opArrayEndIndent
opMapHead
opMapKey
opMapValue
opMapEnd
opMapHeadIndent
opMapKeyIndent
opMapValueIndent
opMapEndIndent
// StructFieldHead
opStructFieldHead
opStructFieldHeadInt
@ -55,6 +71,22 @@ const (
opStructFieldHeadString
opStructFieldHeadBool
opStructFieldHeadIndent
opStructFieldHeadIntIndent
opStructFieldHeadInt8Indent
opStructFieldHeadInt16Indent
opStructFieldHeadInt32Indent
opStructFieldHeadInt64Indent
opStructFieldHeadUintIndent
opStructFieldHeadUint8Indent
opStructFieldHeadUint16Indent
opStructFieldHeadUint32Indent
opStructFieldHeadUint64Indent
opStructFieldHeadFloat32Indent
opStructFieldHeadFloat64Indent
opStructFieldHeadStringIndent
opStructFieldHeadBoolIndent
// StructFieldHead with omitempty
opStructFieldHeadOmitEmpty
opStructFieldHeadIntOmitEmpty
@ -72,6 +104,22 @@ const (
opStructFieldHeadStringOmitEmpty
opStructFieldHeadBoolOmitEmpty
opStructFieldHeadOmitEmptyIndent
opStructFieldHeadIntOmitEmptyIndent
opStructFieldHeadInt8OmitEmptyIndent
opStructFieldHeadInt16OmitEmptyIndent
opStructFieldHeadInt32OmitEmptyIndent
opStructFieldHeadInt64OmitEmptyIndent
opStructFieldHeadUintOmitEmptyIndent
opStructFieldHeadUint8OmitEmptyIndent
opStructFieldHeadUint16OmitEmptyIndent
opStructFieldHeadUint32OmitEmptyIndent
opStructFieldHeadUint64OmitEmptyIndent
opStructFieldHeadFloat32OmitEmptyIndent
opStructFieldHeadFloat64OmitEmptyIndent
opStructFieldHeadStringOmitEmptyIndent
opStructFieldHeadBoolOmitEmptyIndent
// StructFieldHead for pointer structure
opStructFieldPtrHead
opStructFieldPtrHeadInt
@ -89,6 +137,22 @@ const (
opStructFieldPtrHeadString
opStructFieldPtrHeadBool
opStructFieldPtrHeadIndent
opStructFieldPtrHeadIntIndent
opStructFieldPtrHeadInt8Indent
opStructFieldPtrHeadInt16Indent
opStructFieldPtrHeadInt32Indent
opStructFieldPtrHeadInt64Indent
opStructFieldPtrHeadUintIndent
opStructFieldPtrHeadUint8Indent
opStructFieldPtrHeadUint16Indent
opStructFieldPtrHeadUint32Indent
opStructFieldPtrHeadUint64Indent
opStructFieldPtrHeadFloat32Indent
opStructFieldPtrHeadFloat64Indent
opStructFieldPtrHeadStringIndent
opStructFieldPtrHeadBoolIndent
// StructFieldPtrHead with omitempty
opStructFieldPtrHeadOmitEmpty
opStructFieldPtrHeadIntOmitEmpty
@ -106,6 +170,22 @@ const (
opStructFieldPtrHeadStringOmitEmpty
opStructFieldPtrHeadBoolOmitEmpty
opStructFieldPtrHeadOmitEmptyIndent
opStructFieldPtrHeadIntOmitEmptyIndent
opStructFieldPtrHeadInt8OmitEmptyIndent
opStructFieldPtrHeadInt16OmitEmptyIndent
opStructFieldPtrHeadInt32OmitEmptyIndent
opStructFieldPtrHeadInt64OmitEmptyIndent
opStructFieldPtrHeadUintOmitEmptyIndent
opStructFieldPtrHeadUint8OmitEmptyIndent
opStructFieldPtrHeadUint16OmitEmptyIndent
opStructFieldPtrHeadUint32OmitEmptyIndent
opStructFieldPtrHeadUint64OmitEmptyIndent
opStructFieldPtrHeadFloat32OmitEmptyIndent
opStructFieldPtrHeadFloat64OmitEmptyIndent
opStructFieldPtrHeadStringOmitEmptyIndent
opStructFieldPtrHeadBoolOmitEmptyIndent
// StructField
opStructField
opStructFieldInt
@ -123,6 +203,22 @@ const (
opStructFieldString
opStructFieldBool
opStructFieldIndent
opStructFieldIntIndent
opStructFieldInt8Indent
opStructFieldInt16Indent
opStructFieldInt32Indent
opStructFieldInt64Indent
opStructFieldUintIndent
opStructFieldUint8Indent
opStructFieldUint16Indent
opStructFieldUint32Indent
opStructFieldUint64Indent
opStructFieldFloat32Indent
opStructFieldFloat64Indent
opStructFieldStringIndent
opStructFieldBoolIndent
// StructField with omitempty
opStructFieldOmitEmpty
opStructFieldIntOmitEmpty
@ -140,7 +236,24 @@ const (
opStructFieldStringOmitEmpty
opStructFieldBoolOmitEmpty
opStructFieldOmitEmptyIndent
opStructFieldIntOmitEmptyIndent
opStructFieldInt8OmitEmptyIndent
opStructFieldInt16OmitEmptyIndent
opStructFieldInt32OmitEmptyIndent
opStructFieldInt64OmitEmptyIndent
opStructFieldUintOmitEmptyIndent
opStructFieldUint8OmitEmptyIndent
opStructFieldUint16OmitEmptyIndent
opStructFieldUint32OmitEmptyIndent
opStructFieldUint64OmitEmptyIndent
opStructFieldFloat32OmitEmptyIndent
opStructFieldFloat64OmitEmptyIndent
opStructFieldStringOmitEmptyIndent
opStructFieldBoolOmitEmptyIndent
opStructEnd
opStructEndIndent
)
func (t opType) String() string {
@ -179,19 +292,36 @@ func (t opType) String() string {
return "INTERFACE"
case opPtr:
return "PTR"
case opSliceHead:
return "SLICE_HEAD"
case opSliceElem:
return "SLICE_ELEM"
case opSliceEnd:
return "SLICE_END"
case opSliceHeadIndent:
return "SLICE_HEAD_INDENT"
case opSliceElemIndent:
return "SLICE_ELEM_INDENT"
case opSliceEndIndent:
return "SLICE_END_INDENT"
case opArrayHead:
return "ARRAY_HEAD"
case opArrayElem:
return "ARRAY_ELEM"
case opArrayEnd:
return "ARRAY_END"
case opArrayHeadIndent:
return "ARRAY_HEAD_INDENT"
case opArrayElemIndent:
return "ARRAY_ELEM_INDENT"
case opArrayEndIndent:
return "ARRAY_END_INDENT"
case opMapHead:
return "MAP_HEAD"
case opMapKey:
return "MAP_KEY"
@ -200,6 +330,15 @@ func (t opType) String() string {
case opMapEnd:
return "MAP_END"
case opMapHeadIndent:
return "MAP_HEAD_INDENT"
case opMapKeyIndent:
return "MAP_KEY_INDENT"
case opMapValueIndent:
return "MAP_VALUE_INDENT"
case opMapEndIndent:
return "MAP_END_INDENT"
case opStructFieldHead:
return "STRUCT_FIELD_HEAD"
case opStructFieldHeadInt:
@ -231,6 +370,37 @@ func (t opType) String() string {
case opStructFieldHeadBool:
return "STRUCT_FIELD_HEAD_BOOL"
case opStructFieldHeadIndent:
return "STRUCT_FIELD_HEAD_INDENT"
case opStructFieldHeadIntIndent:
return "STRUCT_FIELD_HEAD_INT_INDENT"
case opStructFieldHeadInt8Indent:
return "STRUCT_FIELD_HEAD_INT8_INDENT"
case opStructFieldHeadInt16Indent:
return "STRUCT_FIELD_HEAD_INT16_INDENT"
case opStructFieldHeadInt32Indent:
return "STRUCT_FIELD_HEAD_INT32_INDENT"
case opStructFieldHeadInt64Indent:
return "STRUCT_FIELD_HEAD_INT64_INDENT"
case opStructFieldHeadUintIndent:
return "STRUCT_FIELD_HEAD_UINT_INDENT"
case opStructFieldHeadUint8Indent:
return "STRUCT_FIELD_HEAD_UINT8_INDENT"
case opStructFieldHeadUint16Indent:
return "STRUCT_FIELD_HEAD_UINT16_INDENT"
case opStructFieldHeadUint32Indent:
return "STRUCT_FIELD_HEAD_UINT32_INDENT"
case opStructFieldHeadUint64Indent:
return "STRUCT_FIELD_HEAD_UINT64_INDENT"
case opStructFieldHeadFloat32Indent:
return "STRUCT_FIELD_HEAD_FLOAT32_INDENT"
case opStructFieldHeadFloat64Indent:
return "STRUCT_FIELD_HEAD_FLOAT64_INDENT"
case opStructFieldHeadStringIndent:
return "STRUCT_FIELD_HEAD_STRING_INDENT"
case opStructFieldHeadBoolIndent:
return "STRUCT_FIELD_HEAD_BOOL_INDENT"
case opStructFieldHeadOmitEmpty:
return "STRUCT_FIELD_HEAD_OMIT_EMPTY"
case opStructFieldHeadIntOmitEmpty:
@ -262,6 +432,37 @@ func (t opType) String() string {
case opStructFieldHeadBoolOmitEmpty:
return "STRUCT_FIELD_HEAD_BOOL_OMIT_EMPTY"
case opStructFieldHeadOmitEmptyIndent:
return "STRUCT_FIELD_HEAD_OMIT_EMPTY_INDENT"
case opStructFieldHeadIntOmitEmptyIndent:
return "STRUCT_FIELD_HEAD_INT_OMIT_EMPTY_INDENT"
case opStructFieldHeadInt8OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_INT8_OMIT_EMPTY_INDENT"
case opStructFieldHeadInt16OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_INT16_OMIT_EMPTY_INDENT"
case opStructFieldHeadInt32OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_INT32_OMIT_EMPTY_INDENT"
case opStructFieldHeadInt64OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_INT64_OMIT_EMPTY_INDENT"
case opStructFieldHeadUintOmitEmptyIndent:
return "STRUCT_FIELD_HEAD_UINT_OMIT_EMPTY_INDENT"
case opStructFieldHeadUint8OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_UINT8_OMIT_EMPTY_INDENT"
case opStructFieldHeadUint16OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_UINT16_OMIT_EMPTY_INDENT"
case opStructFieldHeadUint32OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_UINT32_OMIT_EMPTY_INDENT"
case opStructFieldHeadUint64OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_UINT64_OMIT_EMPTY_INDENT"
case opStructFieldHeadFloat32OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_FLOAT32_OMIT_EMPTY_INDENT"
case opStructFieldHeadFloat64OmitEmptyIndent:
return "STRUCT_FIELD_HEAD_FLOAT64_OMIT_EMPTY_INDENT"
case opStructFieldHeadStringOmitEmptyIndent:
return "STRUCT_FIELD_HEAD_STRING_OMIT_EMPTY_INDENT"
case opStructFieldHeadBoolOmitEmptyIndent:
return "STRUCT_FIELD_HEAD_BOOL_OMIT_EMPTY_INDENT"
case opStructFieldPtrHead:
return "STRUCT_FIELD_PTR_HEAD"
case opStructFieldPtrHeadInt:
@ -293,6 +494,37 @@ func (t opType) String() string {
case opStructFieldPtrHeadBool:
return "STRUCT_FIELD_PTR_HEAD_BOOL"
case opStructFieldPtrHeadIndent:
return "STRUCT_FIELD_PTR_HEAD_INDENT"
case opStructFieldPtrHeadIntIndent:
return "STRUCT_FIELD_PTR_HEAD_INT_INDENT"
case opStructFieldPtrHeadInt8Indent:
return "STRUCT_FIELD_PTR_HEAD_INT8_INDENT"
case opStructFieldPtrHeadInt16Indent:
return "STRUCT_FIELD_PTR_HEAD_INT16_INDENT"
case opStructFieldPtrHeadInt32Indent:
return "STRUCT_FIELD_PTR_HEAD_INT32_INDENT"
case opStructFieldPtrHeadInt64Indent:
return "STRUCT_FIELD_PTR_HEAD_INT64_INDENT"
case opStructFieldPtrHeadUintIndent:
return "STRUCT_FIELD_PTR_HEAD_UINT_INDENT"
case opStructFieldPtrHeadUint8Indent:
return "STRUCT_FIELD_PTR_HEAD_UINT8_INDENT"
case opStructFieldPtrHeadUint16Indent:
return "STRUCT_FIELD_PTR_HEAD_UINT16_INDENT"
case opStructFieldPtrHeadUint32Indent:
return "STRUCT_FIELD_PTR_HEAD_UINT32_INDENT"
case opStructFieldPtrHeadUint64Indent:
return "STRUCT_FIELD_PTR_HEAD_UINT64_INDENT"
case opStructFieldPtrHeadFloat32Indent:
return "STRUCT_FIELD_PTR_HEAD_FLOAT32_INDENT"
case opStructFieldPtrHeadFloat64Indent:
return "STRUCT_FIELD_PTR_HEAD_FLOAT64_INDENT"
case opStructFieldPtrHeadStringIndent:
return "STRUCT_FIELD_PTR_HEAD_STRING_INDENT"
case opStructFieldPtrHeadBoolIndent:
return "STRUCT_FIELD_PTR_HEAD_BOOL_INDENT"
case opStructFieldPtrHeadOmitEmpty:
return "STRUCT_FIELD_PTR_HEAD_OMIT_EMPTY"
case opStructFieldPtrHeadIntOmitEmpty:
@ -324,6 +556,37 @@ func (t opType) String() string {
case opStructFieldPtrHeadBoolOmitEmpty:
return "STRUCT_FIELD_PTR_HEAD_BOOL_OMIT_EMPTY"
case opStructFieldPtrHeadOmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadIntOmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_INT_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadInt8OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_INT8_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadInt16OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_INT16_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadInt32OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_INT32_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadInt64OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_INT64_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadUintOmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_UINT_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadUint8OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_UINT8_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadUint16OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_UINT16_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadUint32OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_UINT32_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadUint64OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_UINT64_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadFloat32OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_FLOAT32_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadFloat64OmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_FLOAT64_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadStringOmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_STRING_OMIT_EMPTY_INDENT"
case opStructFieldPtrHeadBoolOmitEmptyIndent:
return "STRUCT_FIELD_PTR_HEAD_BOOL_OMIT_EMPTY_INDENT"
case opStructField:
return "STRUCT_FIELD"
case opStructFieldInt:
@ -355,6 +618,37 @@ func (t opType) String() string {
case opStructFieldBool:
return "STRUCT_FIELD_BOOL"
case opStructFieldIndent:
return "STRUCT_FIELD_INDENT"
case opStructFieldIntIndent:
return "STRUCT_FIELD_INT_INDENT"
case opStructFieldInt8Indent:
return "STRUCT_FIELD_INT8_INDENT"
case opStructFieldInt16Indent:
return "STRUCT_FIELD_INT16_INDENT"
case opStructFieldInt32Indent:
return "STRUCT_FIELD_INT32_INDENT"
case opStructFieldInt64Indent:
return "STRUCT_FIELD_INT64_INDENT"
case opStructFieldUintIndent:
return "STRUCT_FIELD_UINT_INDENT"
case opStructFieldUint8Indent:
return "STRUCT_FIELD_UINT8_INDENT"
case opStructFieldUint16Indent:
return "STRUCT_FIELD_UINT16_INDENT"
case opStructFieldUint32Indent:
return "STRUCT_FIELD_UINT32_INDENT"
case opStructFieldUint64Indent:
return "STRUCT_FIELD_UINT64_INDENT"
case opStructFieldFloat32Indent:
return "STRUCT_FIELD_FLOAT32_INDENT"
case opStructFieldFloat64Indent:
return "STRUCT_FIELD_FLOAT64_INDENT"
case opStructFieldStringIndent:
return "STRUCT_FIELD_STRING_INDENT"
case opStructFieldBoolIndent:
return "STRUCT_FIELD_BOOL_INDENT"
case opStructFieldOmitEmpty:
return "STRUCT_FIELD_OMIT_EMPTY"
case opStructFieldIntOmitEmpty:
@ -386,35 +680,71 @@ func (t opType) String() string {
case opStructFieldBoolOmitEmpty:
return "STRUCT_FIELD_BOOL_OMIT_EMPTY"
case opStructFieldOmitEmptyIndent:
return "STRUCT_FIELD_OMIT_EMPTY_INDENT"
case opStructFieldIntOmitEmptyIndent:
return "STRUCT_FIELD_INT_OMIT_EMPTY_INDENT"
case opStructFieldInt8OmitEmptyIndent:
return "STRUCT_FIELD_INT8_OMIT_EMPTY_INDENT"
case opStructFieldInt16OmitEmptyIndent:
return "STRUCT_FIELD_INT16_OMIT_EMPTY_INDENT"
case opStructFieldInt32OmitEmptyIndent:
return "STRUCT_FIELD_INT32_OMIT_EMPTY_INDENT"
case opStructFieldInt64OmitEmptyIndent:
return "STRUCT_FIELD_INT64_OMIT_EMPTY_INDENT"
case opStructFieldUintOmitEmptyIndent:
return "STRUCT_FIELD_UINT_OMIT_EMPTY_INDENT"
case opStructFieldUint8OmitEmptyIndent:
return "STRUCT_FIELD_UINT8_OMIT_EMPTY_INDENT"
case opStructFieldUint16OmitEmptyIndent:
return "STRUCT_FIELD_UINT16_OMIT_EMPTY_INDENT"
case opStructFieldUint32OmitEmptyIndent:
return "STRUCT_FIELD_UINT32_OMIT_EMPTY_INDENT"
case opStructFieldUint64OmitEmptyIndent:
return "STRUCT_FIELD_UINT64_OMIT_EMPTY_INDENT"
case opStructFieldFloat32OmitEmptyIndent:
return "STRUCT_FIELD_FLOAT32_OMIT_EMPTY_INDENT"
case opStructFieldFloat64OmitEmptyIndent:
return "STRUCT_FIELD_FLOAT64_OMIT_EMPTY_INDENT"
case opStructFieldStringOmitEmptyIndent:
return "STRUCT_FIELD_STRING_OMIT_EMPTY_INDENT"
case opStructFieldBoolOmitEmptyIndent:
return "STRUCT_FIELD_BOOL_OMIT_EMPTY_INDENT"
case opStructEnd:
return "STRUCT_END"
case opStructEndIndent:
return "STRUCT_END_INDENT"
}
return ""
}
type opcodeHeader struct {
op opType
typ *rtype
ptr uintptr
next *opcode
op opType
typ *rtype
ptr uintptr
indent int
next *opcode
}
type opcode struct {
*opcodeHeader
}
func newOpCode(op opType, typ *rtype, next *opcode) *opcode {
func newOpCode(op opType, typ *rtype, indent int, next *opcode) *opcode {
return &opcode{
opcodeHeader: &opcodeHeader{
op: op,
typ: typ,
next: next,
op: op,
typ: typ,
indent: indent,
next: next,
},
}
}
func newEndOp() *opcode {
return newOpCode(opEnd, nil, nil)
func newEndOp(indent int) *opcode {
return newOpCode(opEnd, nil, indent, nil)
}
func (c *opcode) beforeLastCode() *opcode {
@ -422,11 +752,11 @@ func (c *opcode) beforeLastCode() *opcode {
for {
var nextCode *opcode
switch code.op {
case opArrayElem:
case opArrayElem, opArrayElemIndent:
nextCode = code.toArrayElemCode().end
case opSliceElem:
case opSliceElem, opSliceElemIndent:
nextCode = code.toSliceElemCode().end
case opMapKey:
case opMapKey, opMapKeyIndent:
nextCode = code.toMapKeyCode().end
default:
nextCode = code.next
@ -442,13 +772,14 @@ func (c *opcode) beforeLastCode() *opcode {
func (c *opcode) dump() string {
codes := []string{}
for code := c; code.op != opEnd; {
codes = append(codes, fmt.Sprintf("%s", code.op))
indent := strings.Repeat(" ", code.indent)
codes = append(codes, fmt.Sprintf("%s%s", indent, code.op))
switch code.op {
case opArrayElem:
case opArrayElem, opArrayElemIndent:
code = code.toArrayElemCode().end
case opSliceElem:
case opSliceElem, opSliceElemIndent:
code = code.toSliceElemCode().end
case opMapKey:
case opMapKey, opMapKeyIndent:
code = code.toMapKeyCode().end
default:
code = code.next
@ -495,10 +826,11 @@ type sliceHeaderCode struct {
end *opcode
}
func newSliceHeaderCode() *sliceHeaderCode {
func newSliceHeaderCode(indent int) *sliceHeaderCode {
return &sliceHeaderCode{
opcodeHeader: &opcodeHeader{
op: opSliceHead,
op: opSliceHead,
indent: indent,
},
}
}
@ -525,10 +857,11 @@ type arrayHeaderCode struct {
end *opcode
}
func newArrayHeaderCode(alen int) *arrayHeaderCode {
func newArrayHeaderCode(indent, alen int) *arrayHeaderCode {
return &arrayHeaderCode{
opcodeHeader: &opcodeHeader{
op: opArrayHead,
op: opArrayHead,
indent: indent,
},
len: uintptr(alen),
}
@ -580,27 +913,30 @@ func (c *mapValueCode) set(iter unsafe.Pointer) {
c.iter = iter
}
func newMapHeaderCode(typ *rtype) *mapHeaderCode {
func newMapHeaderCode(typ *rtype, indent int) *mapHeaderCode {
return &mapHeaderCode{
opcodeHeader: &opcodeHeader{
op: opMapHead,
typ: typ,
op: opMapHead,
typ: typ,
indent: indent,
},
}
}
func newMapKeyCode() *mapKeyCode {
func newMapKeyCode(indent int) *mapKeyCode {
return &mapKeyCode{
opcodeHeader: &opcodeHeader{
op: opMapKey,
op: opMapKey,
indent: indent,
},
}
}
func newMapValueCode() *mapValueCode {
func newMapValueCode(indent int) *mapValueCode {
return &mapValueCode{
opcodeHeader: &opcodeHeader{
op: opMapValue,
op: opMapValue,
indent: indent,
},
}
}

View File

@ -6,7 +6,7 @@ import (
"github.com/goccy/go-json"
)
func Test_Encoder(t *testing.T) {
func Test_Marshal(t *testing.T) {
t.Run("int", func(t *testing.T) {
bytes, err := json.Marshal(-10)
assertErr(t, err)
@ -205,3 +205,77 @@ func Test_Encoder(t *testing.T) {
})
})
}
func Test_MarshalIndent(t *testing.T) {
prefix := "-"
indent := "\t"
t.Run("struct", func(t *testing.T) {
bytes, err := json.MarshalIndent(struct {
A int `json:"a"`
B uint `json:"b"`
C string `json:"c"`
D int `json:"-"` // ignore field
a int `json:"aa"` // private field
}{
A: -1,
B: 1,
C: "hello world",
}, prefix, indent)
assertErr(t, err)
result := "{\n-\t\"a\": -1,\n-\t\"b\": 1,\n-\t\"c\": \"hello world\"\n-}"
assertEq(t, "struct", result, string(bytes))
})
t.Run("slice", func(t *testing.T) {
t.Run("[]int", func(t *testing.T) {
bytes, err := json.MarshalIndent([]int{1, 2, 3, 4}, prefix, indent)
assertErr(t, err)
result := "[\n-\t1,\n-\t2,\n-\t3,\n-\t4\n-]"
assertEq(t, "[]int", result, string(bytes))
})
t.Run("[]interface{}", func(t *testing.T) {
bytes, err := json.MarshalIndent([]interface{}{1, 2.1, "hello"}, prefix, indent)
assertErr(t, err)
result := "[\n-\t1,\n-\t2.1,\n-\t\"hello\"\n-]"
assertEq(t, "[]interface{}", result, string(bytes))
})
})
t.Run("array", func(t *testing.T) {
bytes, err := json.MarshalIndent([4]int{1, 2, 3, 4}, prefix, indent)
assertErr(t, err)
result := "[\n-\t1,\n-\t2,\n-\t3,\n-\t4\n-]"
assertEq(t, "array", result, string(bytes))
})
t.Run("map", func(t *testing.T) {
t.Run("map[string]int", func(t *testing.T) {
bytes, err := json.MarshalIndent(map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
}, prefix, indent)
assertErr(t, err)
result := "{\n-\t\"a\": 1,\n-\t\"b\": 2,\n-\t\"c\": 3,\n-\t\"d\": 4\n-}"
assertEq(t, "map", len(result), len(string(bytes)))
})
t.Run("map[string]interface{}", func(t *testing.T) {
type T struct {
E int
F int
}
v := map[string]interface{}{
"a": 1,
"b": 2.1,
"c": &T{
E: 10,
F: 11,
},
"d": 4,
}
bytes, err := json.MarshalIndent(v, prefix, indent)
assertErr(t, err)
result := "{\n-\t\"a\": 1,\n-\t\"b\": 2.1,\n-\t\"c\": {\n-\t\t\"E\": 10,\n-\t\t\"F\": 11\n-\t},\n-\t\"d\": 4\n-}"
assertEq(t, "map[string]interface{}", len(result), len(string(bytes)))
})
})
}

File diff suppressed because it is too large Load Diff

14
json.go
View File

@ -5,6 +5,20 @@ import "bytes"
func Marshal(v interface{}) ([]byte, error) {
var b *bytes.Buffer
enc := NewEncoder(b)
enc.SetIndent("", "")
bytes, err := enc.encodeForMarshal(v)
if err != nil {
enc.release()
return nil, err
}
enc.release()
return bytes, nil
}
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
var b *bytes.Buffer
enc := NewEncoder(b)
enc.SetIndent(prefix, indent)
bytes, err := enc.encodeForMarshal(v)
if err != nil {
enc.release()