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

116
encode.go
View File

@ -1,6 +1,7 @@
package json package json
import ( import (
"bytes"
"io" "io"
"reflect" "reflect"
"strconv" "strconv"
@ -13,6 +14,10 @@ type Encoder struct {
w io.Writer w io.Writer
buf []byte buf []byte
pool sync.Pool pool sync.Pool
enabledIndent bool
prefix []byte
indentStr []byte
indent int
} }
const ( const (
@ -23,14 +28,19 @@ type opcodeMap struct {
sync.Map 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 { if v, ok := m.Load(k); ok {
return v.(*opcode) return v.(*opcodeSet)
} }
return nil return nil
} }
func (m *opcodeMap) Set(k *rtype, op *opcode) { func (m *opcodeMap) set(k *rtype, op *opcodeSet) {
m.Store(k, op) m.Store(k, op)
} }
@ -55,6 +65,7 @@ func init() {
func NewEncoder(w io.Writer) *Encoder { func NewEncoder(w io.Writer) *Encoder {
enc := encPool.Get().(*Encoder) enc := encPool.Get().(*Encoder)
enc.w = w enc.w = w
enc.indent = 0
enc.reset() enc.reset()
return enc return enc
} }
@ -72,15 +83,6 @@ func (e *Encoder) Encode(v interface{}) error {
return nil 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. // 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. // 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). // 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. // Calling SetIndent("", "") disables indentation.
func (e *Encoder) SetIndent(prefix, indent string) { 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() { func (e *Encoder) release() {
@ -104,6 +112,63 @@ func (e *Encoder) reset() {
e.buf = e.buf[:0] 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) { func (e *Encoder) encodeInt(v int) {
e.encodeInt64(int64(v)) e.encodeInt64(int64(v))
} }
@ -169,26 +234,7 @@ func (e *Encoder) encodeByte(b byte) {
e.buf = append(e.buf, b) e.buf = append(e.buf, b)
} }
func (e *Encoder) encode(v interface{}) error { func (e *Encoder) encodeIndent(indent int) {
header := (*interfaceHeader)(unsafe.Pointer(&v)) e.buf = append(e.buf, e.prefix...)
typ := header.typ e.buf = append(e.buf, bytes.Repeat(e.indentStr, indent)...)
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)
} }

View File

@ -9,18 +9,18 @@ import (
"golang.org/x/xerrors" "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() { switch typ.Kind() {
case reflect.Ptr: case reflect.Ptr:
return e.compilePtr(typ) return e.compilePtr(typ, withIndent)
case reflect.Slice: case reflect.Slice:
return e.compileSlice(typ) return e.compileSlice(typ, withIndent)
case reflect.Array: case reflect.Array:
return e.compileArray(typ) return e.compileArray(typ, withIndent)
case reflect.Map: case reflect.Map:
return e.compileMap(typ) return e.compileMap(typ, withIndent)
case reflect.Struct: case reflect.Struct:
return e.compileStruct(typ) return e.compileStruct(typ, withIndent)
case reflect.Int: case reflect.Int:
return e.compileInt(typ) return e.compileInt(typ)
case reflect.Int8: case reflect.Int8:
@ -119,14 +119,75 @@ func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
code.op = opStructFieldPtrHeadStringOmitEmpty code.op = opStructFieldPtrHeadStringOmitEmpty
case opStructFieldHeadBoolOmitEmpty: case opStructFieldHeadBoolOmitEmpty:
code.op = opStructFieldPtrHeadBoolOmitEmpty 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: default:
return newOpCode(opPtr, typ, code) return newOpCode(opPtr, typ, e.indent, code)
} }
return code return code
} }
func (e *Encoder) compilePtr(typ *rtype) (*opcode, error) { func (e *Encoder) compilePtr(typ *rtype, withIndent bool) (*opcode, error) {
code, err := e.compile(typ.Elem()) code, err := e.compile(typ.Elem(), withIndent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -134,69 +195,73 @@ func (e *Encoder) compilePtr(typ *rtype) (*opcode, error) {
} }
func (e *Encoder) compileInt(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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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() elem := typ.Elem()
size := elem.Size() size := elem.Size()
code, err := e.compile(elem)
e.indent++
code, err := e.compile(elem, withIndent)
e.indent--
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -205,9 +270,20 @@ func (e *Encoder) compileSlice(typ *rtype) (*opcode, error) {
// ^ | // ^ |
// |________| // |________|
header := newSliceHeaderCode() header := newSliceHeaderCode(e.indent)
elemCode := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElem}, size: size} elemCode := &sliceElemCode{
end := newOpCode(opSliceEnd, nil, newEndOp()) 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.elem = elemCode
header.end = end header.end = end
@ -218,11 +294,15 @@ func (e *Encoder) compileSlice(typ *rtype) (*opcode, error) {
return (*opcode)(unsafe.Pointer(header)), nil 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() elem := typ.Elem()
alen := typ.Len() alen := typ.Len()
size := elem.Size() size := elem.Size()
code, err := e.compile(elem)
e.indent++
code, err := e.compile(elem, withIndent)
e.indent--
if err != nil { if err != nil {
return nil, err 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{ elemCode := &arrayElemCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opArrayElem, op: opArrayElem,
@ -238,7 +318,13 @@ func (e *Encoder) compileArray(typ *rtype) (*opcode, error) {
len: uintptr(alen), len: uintptr(alen),
size: size, 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.elem = elemCode
header.end = end header.end = end
@ -265,26 +351,38 @@ func mapiternext(it unsafe.Pointer)
//go:noescape //go:noescape
func maplen(m unsafe.Pointer) int 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 // header => code => value => code => key => code => value => code => end
// ^ | // ^ |
// |_______________________| // |_______________________|
e.indent++
keyType := typ.Key() keyType := typ.Key()
keyCode, err := e.compile(keyType) keyCode, err := e.compile(keyType, withIndent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
valueType := typ.Elem() valueType := typ.Elem()
valueCode, err := e.compile(valueType) valueCode, err := e.compile(valueType, withIndent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
header := newMapHeaderCode(typ)
key := newMapKeyCode() key := newMapKeyCode(e.indent)
value := newMapValueCode() value := newMapValueCode(e.indent)
e.indent--
header := newMapHeaderCode(typ, e.indent)
header.key = key header.key = key
header.value = value 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 header.next = keyCode
keyCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(value)) keyCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(value))
@ -314,7 +412,297 @@ func (e *Encoder) isIgnoredStructField(field reflect.StructField) bool {
return false 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 // header => code => structField => code => end
// ^ | // ^ |
// |__________| // |__________|
@ -325,6 +713,7 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
code *opcode code *opcode
prevField *structFieldCode prevField *structFieldCode
) )
e.indent++
for i := 0; i < fieldNum; i++ { for i := 0; i < fieldNum; i++ {
field := typ.Field(i) field := typ.Field(i)
if e.isIgnoredStructField(field) { if e.isIgnoredStructField(field) {
@ -343,7 +732,7 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
isOmitEmpty = opts[1] == "omitempty" isOmitEmpty = opts[1] == "omitempty"
} }
fieldType := type2rtype(field.Type) fieldType := type2rtype(field.Type)
valueCode, err := e.compile(fieldType) valueCode, err := e.compile(fieldType, withIndent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -352,162 +741,49 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
typ: fieldType, typ: fieldType,
next: valueCode, next: valueCode,
indent: e.indent,
}, },
key: []byte(key), key: []byte(key),
offset: field.Offset, offset: field.Offset,
} }
if fieldIdx == 0 { if fieldIdx == 0 {
fieldCode.indent--
head = fieldCode head = fieldCode
code = (*opcode)(unsafe.Pointer(fieldCode)) code = (*opcode)(unsafe.Pointer(fieldCode))
prevField = fieldCode prevField = fieldCode
if isOmitEmpty { op := e.optimizeStructHeader(valueCode.op, isOmitEmpty, withIndent)
fieldCode.op = opStructFieldHeadOmitEmpty fieldCode.op = op
switch valueCode.op { switch op {
case opInt: case opStructFieldHead,
fieldCode.op = opStructFieldHeadIntOmitEmpty opStructFieldHeadOmitEmpty,
case opInt8: opStructFieldHeadIndent,
fieldCode.op = opStructFieldHeadInt8OmitEmpty opStructFieldHeadOmitEmptyIndent:
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() code = valueCode.beforeLastCode()
} }
} else { } 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()
}
}
} else {
fieldCode.op = opStructField
code.next = (*opcode)(unsafe.Pointer(fieldCode)) code.next = (*opcode)(unsafe.Pointer(fieldCode))
prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode)) prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode))
prevField = fieldCode prevField = fieldCode
code = (*opcode)(unsafe.Pointer(fieldCode)) code = (*opcode)(unsafe.Pointer(fieldCode))
if isOmitEmpty { op := e.optimizeStructField(valueCode.op, isOmitEmpty, withIndent)
fieldCode.op = opStructFieldOmitEmpty fieldCode.op = op
switch valueCode.op { switch op {
case opInt: case opStructField,
fieldCode.op = opStructFieldIntOmitEmpty opStructFieldOmitEmpty,
case opInt8: opStructFieldIndent,
fieldCode.op = opStructFieldInt8OmitEmpty opStructFieldOmitEmptyIndent:
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() 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()
}
}
} }
fieldIdx++ 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 { if prevField != nil && prevField.nextField == nil {
prevField.nextField = structEndCode prevField.nextField = structEndCode
@ -519,13 +795,17 @@ func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) {
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opStructFieldHead, op: opStructFieldHead,
typ: typ, typ: typ,
indent: e.indent,
}, },
nextField: structEndCode, nextField: structEndCode,
} }
if withIndent {
head.op = opStructFieldHeadIndent
}
code = (*opcode)(unsafe.Pointer(head)) code = (*opcode)(unsafe.Pointer(head))
} }
head.end = structEndCode head.end = structEndCode
code.next = structEndCode code.next = structEndCode
structEndCode.next = newEndOp() structEndCode.next = newEndOp(e.indent)
return (*opcode)(unsafe.Pointer(head)), nil return (*opcode)(unsafe.Pointer(head)), nil
} }

View File

@ -27,17 +27,33 @@ const (
opBool opBool
opInterface opInterface
opPtr opPtr
opSliceHead opSliceHead
opSliceElem opSliceElem
opSliceEnd opSliceEnd
opSliceHeadIndent
opSliceElemIndent
opSliceEndIndent
opArrayHead opArrayHead
opArrayElem opArrayElem
opArrayEnd opArrayEnd
opArrayHeadIndent
opArrayElemIndent
opArrayEndIndent
opMapHead opMapHead
opMapKey opMapKey
opMapValue opMapValue
opMapEnd opMapEnd
opMapHeadIndent
opMapKeyIndent
opMapValueIndent
opMapEndIndent
// StructFieldHead // StructFieldHead
opStructFieldHead opStructFieldHead
opStructFieldHeadInt opStructFieldHeadInt
@ -55,6 +71,22 @@ const (
opStructFieldHeadString opStructFieldHeadString
opStructFieldHeadBool opStructFieldHeadBool
opStructFieldHeadIndent
opStructFieldHeadIntIndent
opStructFieldHeadInt8Indent
opStructFieldHeadInt16Indent
opStructFieldHeadInt32Indent
opStructFieldHeadInt64Indent
opStructFieldHeadUintIndent
opStructFieldHeadUint8Indent
opStructFieldHeadUint16Indent
opStructFieldHeadUint32Indent
opStructFieldHeadUint64Indent
opStructFieldHeadFloat32Indent
opStructFieldHeadFloat64Indent
opStructFieldHeadStringIndent
opStructFieldHeadBoolIndent
// StructFieldHead with omitempty // StructFieldHead with omitempty
opStructFieldHeadOmitEmpty opStructFieldHeadOmitEmpty
opStructFieldHeadIntOmitEmpty opStructFieldHeadIntOmitEmpty
@ -72,6 +104,22 @@ const (
opStructFieldHeadStringOmitEmpty opStructFieldHeadStringOmitEmpty
opStructFieldHeadBoolOmitEmpty opStructFieldHeadBoolOmitEmpty
opStructFieldHeadOmitEmptyIndent
opStructFieldHeadIntOmitEmptyIndent
opStructFieldHeadInt8OmitEmptyIndent
opStructFieldHeadInt16OmitEmptyIndent
opStructFieldHeadInt32OmitEmptyIndent
opStructFieldHeadInt64OmitEmptyIndent
opStructFieldHeadUintOmitEmptyIndent
opStructFieldHeadUint8OmitEmptyIndent
opStructFieldHeadUint16OmitEmptyIndent
opStructFieldHeadUint32OmitEmptyIndent
opStructFieldHeadUint64OmitEmptyIndent
opStructFieldHeadFloat32OmitEmptyIndent
opStructFieldHeadFloat64OmitEmptyIndent
opStructFieldHeadStringOmitEmptyIndent
opStructFieldHeadBoolOmitEmptyIndent
// StructFieldHead for pointer structure // StructFieldHead for pointer structure
opStructFieldPtrHead opStructFieldPtrHead
opStructFieldPtrHeadInt opStructFieldPtrHeadInt
@ -89,6 +137,22 @@ const (
opStructFieldPtrHeadString opStructFieldPtrHeadString
opStructFieldPtrHeadBool opStructFieldPtrHeadBool
opStructFieldPtrHeadIndent
opStructFieldPtrHeadIntIndent
opStructFieldPtrHeadInt8Indent
opStructFieldPtrHeadInt16Indent
opStructFieldPtrHeadInt32Indent
opStructFieldPtrHeadInt64Indent
opStructFieldPtrHeadUintIndent
opStructFieldPtrHeadUint8Indent
opStructFieldPtrHeadUint16Indent
opStructFieldPtrHeadUint32Indent
opStructFieldPtrHeadUint64Indent
opStructFieldPtrHeadFloat32Indent
opStructFieldPtrHeadFloat64Indent
opStructFieldPtrHeadStringIndent
opStructFieldPtrHeadBoolIndent
// StructFieldPtrHead with omitempty // StructFieldPtrHead with omitempty
opStructFieldPtrHeadOmitEmpty opStructFieldPtrHeadOmitEmpty
opStructFieldPtrHeadIntOmitEmpty opStructFieldPtrHeadIntOmitEmpty
@ -106,6 +170,22 @@ const (
opStructFieldPtrHeadStringOmitEmpty opStructFieldPtrHeadStringOmitEmpty
opStructFieldPtrHeadBoolOmitEmpty opStructFieldPtrHeadBoolOmitEmpty
opStructFieldPtrHeadOmitEmptyIndent
opStructFieldPtrHeadIntOmitEmptyIndent
opStructFieldPtrHeadInt8OmitEmptyIndent
opStructFieldPtrHeadInt16OmitEmptyIndent
opStructFieldPtrHeadInt32OmitEmptyIndent
opStructFieldPtrHeadInt64OmitEmptyIndent
opStructFieldPtrHeadUintOmitEmptyIndent
opStructFieldPtrHeadUint8OmitEmptyIndent
opStructFieldPtrHeadUint16OmitEmptyIndent
opStructFieldPtrHeadUint32OmitEmptyIndent
opStructFieldPtrHeadUint64OmitEmptyIndent
opStructFieldPtrHeadFloat32OmitEmptyIndent
opStructFieldPtrHeadFloat64OmitEmptyIndent
opStructFieldPtrHeadStringOmitEmptyIndent
opStructFieldPtrHeadBoolOmitEmptyIndent
// StructField // StructField
opStructField opStructField
opStructFieldInt opStructFieldInt
@ -123,6 +203,22 @@ const (
opStructFieldString opStructFieldString
opStructFieldBool opStructFieldBool
opStructFieldIndent
opStructFieldIntIndent
opStructFieldInt8Indent
opStructFieldInt16Indent
opStructFieldInt32Indent
opStructFieldInt64Indent
opStructFieldUintIndent
opStructFieldUint8Indent
opStructFieldUint16Indent
opStructFieldUint32Indent
opStructFieldUint64Indent
opStructFieldFloat32Indent
opStructFieldFloat64Indent
opStructFieldStringIndent
opStructFieldBoolIndent
// StructField with omitempty // StructField with omitempty
opStructFieldOmitEmpty opStructFieldOmitEmpty
opStructFieldIntOmitEmpty opStructFieldIntOmitEmpty
@ -140,7 +236,24 @@ const (
opStructFieldStringOmitEmpty opStructFieldStringOmitEmpty
opStructFieldBoolOmitEmpty opStructFieldBoolOmitEmpty
opStructFieldOmitEmptyIndent
opStructFieldIntOmitEmptyIndent
opStructFieldInt8OmitEmptyIndent
opStructFieldInt16OmitEmptyIndent
opStructFieldInt32OmitEmptyIndent
opStructFieldInt64OmitEmptyIndent
opStructFieldUintOmitEmptyIndent
opStructFieldUint8OmitEmptyIndent
opStructFieldUint16OmitEmptyIndent
opStructFieldUint32OmitEmptyIndent
opStructFieldUint64OmitEmptyIndent
opStructFieldFloat32OmitEmptyIndent
opStructFieldFloat64OmitEmptyIndent
opStructFieldStringOmitEmptyIndent
opStructFieldBoolOmitEmptyIndent
opStructEnd opStructEnd
opStructEndIndent
) )
func (t opType) String() string { func (t opType) String() string {
@ -179,19 +292,36 @@ func (t opType) String() string {
return "INTERFACE" return "INTERFACE"
case opPtr: case opPtr:
return "PTR" return "PTR"
case opSliceHead: case opSliceHead:
return "SLICE_HEAD" return "SLICE_HEAD"
case opSliceElem: case opSliceElem:
return "SLICE_ELEM" return "SLICE_ELEM"
case opSliceEnd: case opSliceEnd:
return "SLICE_END" return "SLICE_END"
case opSliceHeadIndent:
return "SLICE_HEAD_INDENT"
case opSliceElemIndent:
return "SLICE_ELEM_INDENT"
case opSliceEndIndent:
return "SLICE_END_INDENT"
case opArrayHead: case opArrayHead:
return "ARRAY_HEAD" return "ARRAY_HEAD"
case opArrayElem: case opArrayElem:
return "ARRAY_ELEM" return "ARRAY_ELEM"
case opArrayEnd: case opArrayEnd:
return "ARRAY_END" return "ARRAY_END"
case opArrayHeadIndent:
return "ARRAY_HEAD_INDENT"
case opArrayElemIndent:
return "ARRAY_ELEM_INDENT"
case opArrayEndIndent:
return "ARRAY_END_INDENT"
case opMapHead: case opMapHead:
return "MAP_HEAD" return "MAP_HEAD"
case opMapKey: case opMapKey:
return "MAP_KEY" return "MAP_KEY"
@ -200,6 +330,15 @@ func (t opType) String() string {
case opMapEnd: case opMapEnd:
return "MAP_END" 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: case opStructFieldHead:
return "STRUCT_FIELD_HEAD" return "STRUCT_FIELD_HEAD"
case opStructFieldHeadInt: case opStructFieldHeadInt:
@ -231,6 +370,37 @@ func (t opType) String() string {
case opStructFieldHeadBool: case opStructFieldHeadBool:
return "STRUCT_FIELD_HEAD_BOOL" 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: case opStructFieldHeadOmitEmpty:
return "STRUCT_FIELD_HEAD_OMIT_EMPTY" return "STRUCT_FIELD_HEAD_OMIT_EMPTY"
case opStructFieldHeadIntOmitEmpty: case opStructFieldHeadIntOmitEmpty:
@ -262,6 +432,37 @@ func (t opType) String() string {
case opStructFieldHeadBoolOmitEmpty: case opStructFieldHeadBoolOmitEmpty:
return "STRUCT_FIELD_HEAD_BOOL_OMIT_EMPTY" 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: case opStructFieldPtrHead:
return "STRUCT_FIELD_PTR_HEAD" return "STRUCT_FIELD_PTR_HEAD"
case opStructFieldPtrHeadInt: case opStructFieldPtrHeadInt:
@ -293,6 +494,37 @@ func (t opType) String() string {
case opStructFieldPtrHeadBool: case opStructFieldPtrHeadBool:
return "STRUCT_FIELD_PTR_HEAD_BOOL" 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: case opStructFieldPtrHeadOmitEmpty:
return "STRUCT_FIELD_PTR_HEAD_OMIT_EMPTY" return "STRUCT_FIELD_PTR_HEAD_OMIT_EMPTY"
case opStructFieldPtrHeadIntOmitEmpty: case opStructFieldPtrHeadIntOmitEmpty:
@ -324,6 +556,37 @@ func (t opType) String() string {
case opStructFieldPtrHeadBoolOmitEmpty: case opStructFieldPtrHeadBoolOmitEmpty:
return "STRUCT_FIELD_PTR_HEAD_BOOL_OMIT_EMPTY" 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: case opStructField:
return "STRUCT_FIELD" return "STRUCT_FIELD"
case opStructFieldInt: case opStructFieldInt:
@ -355,6 +618,37 @@ func (t opType) String() string {
case opStructFieldBool: case opStructFieldBool:
return "STRUCT_FIELD_BOOL" 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: case opStructFieldOmitEmpty:
return "STRUCT_FIELD_OMIT_EMPTY" return "STRUCT_FIELD_OMIT_EMPTY"
case opStructFieldIntOmitEmpty: case opStructFieldIntOmitEmpty:
@ -386,8 +680,42 @@ func (t opType) String() string {
case opStructFieldBoolOmitEmpty: case opStructFieldBoolOmitEmpty:
return "STRUCT_FIELD_BOOL_OMIT_EMPTY" 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: case opStructEnd:
return "STRUCT_END" return "STRUCT_END"
case opStructEndIndent:
return "STRUCT_END_INDENT"
} }
return "" return ""
} }
@ -396,6 +724,7 @@ type opcodeHeader struct {
op opType op opType
typ *rtype typ *rtype
ptr uintptr ptr uintptr
indent int
next *opcode next *opcode
} }
@ -403,18 +732,19 @@ type opcode struct {
*opcodeHeader *opcodeHeader
} }
func newOpCode(op opType, typ *rtype, next *opcode) *opcode { func newOpCode(op opType, typ *rtype, indent int, next *opcode) *opcode {
return &opcode{ return &opcode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: op, op: op,
typ: typ, typ: typ,
indent: indent,
next: next, next: next,
}, },
} }
} }
func newEndOp() *opcode { func newEndOp(indent int) *opcode {
return newOpCode(opEnd, nil, nil) return newOpCode(opEnd, nil, indent, nil)
} }
func (c *opcode) beforeLastCode() *opcode { func (c *opcode) beforeLastCode() *opcode {
@ -422,11 +752,11 @@ func (c *opcode) beforeLastCode() *opcode {
for { for {
var nextCode *opcode var nextCode *opcode
switch code.op { switch code.op {
case opArrayElem: case opArrayElem, opArrayElemIndent:
nextCode = code.toArrayElemCode().end nextCode = code.toArrayElemCode().end
case opSliceElem: case opSliceElem, opSliceElemIndent:
nextCode = code.toSliceElemCode().end nextCode = code.toSliceElemCode().end
case opMapKey: case opMapKey, opMapKeyIndent:
nextCode = code.toMapKeyCode().end nextCode = code.toMapKeyCode().end
default: default:
nextCode = code.next nextCode = code.next
@ -442,13 +772,14 @@ func (c *opcode) beforeLastCode() *opcode {
func (c *opcode) dump() string { func (c *opcode) dump() string {
codes := []string{} codes := []string{}
for code := c; code.op != opEnd; { 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 { switch code.op {
case opArrayElem: case opArrayElem, opArrayElemIndent:
code = code.toArrayElemCode().end code = code.toArrayElemCode().end
case opSliceElem: case opSliceElem, opSliceElemIndent:
code = code.toSliceElemCode().end code = code.toSliceElemCode().end
case opMapKey: case opMapKey, opMapKeyIndent:
code = code.toMapKeyCode().end code = code.toMapKeyCode().end
default: default:
code = code.next code = code.next
@ -495,10 +826,11 @@ type sliceHeaderCode struct {
end *opcode end *opcode
} }
func newSliceHeaderCode() *sliceHeaderCode { func newSliceHeaderCode(indent int) *sliceHeaderCode {
return &sliceHeaderCode{ return &sliceHeaderCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opSliceHead, op: opSliceHead,
indent: indent,
}, },
} }
} }
@ -525,10 +857,11 @@ type arrayHeaderCode struct {
end *opcode end *opcode
} }
func newArrayHeaderCode(alen int) *arrayHeaderCode { func newArrayHeaderCode(indent, alen int) *arrayHeaderCode {
return &arrayHeaderCode{ return &arrayHeaderCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opArrayHead, op: opArrayHead,
indent: indent,
}, },
len: uintptr(alen), len: uintptr(alen),
} }
@ -580,27 +913,30 @@ func (c *mapValueCode) set(iter unsafe.Pointer) {
c.iter = iter c.iter = iter
} }
func newMapHeaderCode(typ *rtype) *mapHeaderCode { func newMapHeaderCode(typ *rtype, indent int) *mapHeaderCode {
return &mapHeaderCode{ return &mapHeaderCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opMapHead, op: opMapHead,
typ: typ, typ: typ,
indent: indent,
}, },
} }
} }
func newMapKeyCode() *mapKeyCode { func newMapKeyCode(indent int) *mapKeyCode {
return &mapKeyCode{ return &mapKeyCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opMapKey, op: opMapKey,
indent: indent,
}, },
} }
} }
func newMapValueCode() *mapValueCode { func newMapValueCode(indent int) *mapValueCode {
return &mapValueCode{ return &mapValueCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opMapValue, op: opMapValue,
indent: indent,
}, },
} }
} }

View File

@ -6,7 +6,7 @@ import (
"github.com/goccy/go-json" "github.com/goccy/go-json"
) )
func Test_Encoder(t *testing.T) { func Test_Marshal(t *testing.T) {
t.Run("int", func(t *testing.T) { t.Run("int", func(t *testing.T) {
bytes, err := json.Marshal(-10) bytes, err := json.Marshal(-10)
assertErr(t, err) 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) { func Marshal(v interface{}) ([]byte, error) {
var b *bytes.Buffer var b *bytes.Buffer
enc := NewEncoder(b) 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) bytes, err := enc.encodeForMarshal(v)
if err != nil { if err != nil {
enc.release() enc.release()