From 3d7267abc87da631de8844d7ec9906f36670089f Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Sat, 2 May 2020 23:35:41 +0900 Subject: [PATCH] Add MarshalIndent --- encode.go | 122 +++-- encode_compile.go | 648 +++++++++++++++++------- encode_opcode.go | 392 +++++++++++++-- encode_test.go | 76 ++- encode_vm.go | 1210 ++++++++++++++++++++++++++++++++++++++++++++- json.go | 14 + 6 files changed, 2210 insertions(+), 252 deletions(-) diff --git a/encode.go b/encode.go index b3e523e..728e322 100644 --- a/encode.go +++ b/encode.go @@ -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)...) } diff --git a/encode_compile.go b/encode_compile.go index a63920d..b528c61 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -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 } diff --git a/encode_opcode.go b/encode_opcode.go index 9561339..0070724 100644 --- a/encode_opcode.go +++ b/encode_opcode.go @@ -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, }, } } diff --git a/encode_test.go b/encode_test.go index bd46fee..03366f8 100644 --- a/encode_test.go +++ b/encode_test.go @@ -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))) + }) + }) +} diff --git a/encode_vm.go b/encode_vm.go index a70dbc8..f071d11 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -66,7 +66,8 @@ func (e *Encoder) run(code *opcode) error { if typ.Kind() == reflect.Ptr { typ = typ.Elem() } - c, err := e.compile(typ) + e.indent = code.indent + c, err := e.compile(typ, e.enabledIndent) if err != nil { return err } @@ -102,6 +103,42 @@ func (e *Encoder) run(code *opcode) error { e.encodeByte(']') code = c.end.next } + case opSliceHeadIndent: + p := code.ptr + headerCode := code.toSliceHeaderCode() + if p == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = headerCode.end.next + } else { + e.encodeBytes([]byte{'[', '\n'}) + header := (*reflect.SliceHeader)(unsafe.Pointer(p)) + headerCode.elem.set(header) + if header.Len > 0 { + e.encodeIndent(code.indent + 1) + code = code.next + code.ptr = header.Data + } else { + e.encodeByte('\n') + e.encodeIndent(code.indent) + e.encodeBytes([]byte{']', '\n'}) + code = headerCode.end.next + } + } + case opSliceElemIndent: + c := code.toSliceElemCode() + c.idx++ + if c.idx < c.len { + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(code.indent + 1) + code = code.next + code.ptr = c.data + c.idx*c.size + } else { + e.encodeByte('\n') + e.encodeIndent(code.indent) + e.encodeBytes([]byte{']', '\n'}) + code = c.end.next + } case opArrayHead: p := code.ptr headerCode := code.toArrayHeaderCode() @@ -130,6 +167,40 @@ func (e *Encoder) run(code *opcode) error { e.encodeByte(']') code = c.end.next } + case opArrayHeadIndent: + p := code.ptr + headerCode := code.toArrayHeaderCode() + if p == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = headerCode.end.next + } else { + e.encodeBytes([]byte{'[', '\n'}) + if headerCode.len > 0 { + e.encodeIndent(code.indent + 1) + code = code.next + code.ptr = p + headerCode.elem.ptr = p + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{']', '\n'}) + code = headerCode.end.next + } + } + case opArrayElemIndent: + c := code.toArrayElemCode() + c.idx++ + if c.idx < c.len { + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(code.indent + 1) + code = code.next + code.ptr = c.ptr + c.idx*c.size + } else { + e.encodeByte('\n') + e.encodeIndent(code.indent) + e.encodeBytes([]byte{']', '\n'}) + code = c.end.next + } case opMapHead: ptr := code.ptr mapHeadCode := code.toMapHeadCode() @@ -170,6 +241,53 @@ func (e *Encoder) run(code *opcode) error { c.next.ptr = uintptr(value) mapiternext(c.iter) code = c.next + case opMapHeadIndent: + ptr := code.ptr + mapHeadCode := code.toMapHeadCode() + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = mapHeadCode.end.next + } else { + e.encodeBytes([]byte{'{', '\n'}) + mlen := maplen(unsafe.Pointer(ptr)) + if mlen > 0 { + iter := mapiterinit(code.typ, unsafe.Pointer(ptr)) + mapHeadCode.key.set(mlen, iter) + mapHeadCode.value.set(iter) + key := mapiterkey(iter) + code.next.ptr = uintptr(key) + code = code.next + e.encodeIndent(code.indent) + } else { + e.encodeByte('\n') + e.encodeIndent(code.indent - 1) + e.encodeBytes([]byte{'}', '\n'}) + code = mapHeadCode.end.next + } + } + case opMapKeyIndent: + c := code.toMapKeyCode() + c.idx++ + if c.idx < c.len { + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(code.indent) + key := mapiterkey(c.iter) + c.next.ptr = uintptr(key) + code = c.next + } else { + e.encodeByte('\n') + e.encodeIndent(code.indent - 1) + e.encodeBytes([]byte{'}', '\n'}) + code = c.end.next + } + case opMapValueIndent: + e.encodeBytes([]byte{':', ' '}) + c := code.toMapValueCode() + value := mapitervalue(c.iter) + c.next.ptr = uintptr(value) + mapiternext(c.iter) + code = c.next case opStructFieldPtrHead: if code.ptr != 0 { code.ptr = e.ptrToPtr(code.ptr) @@ -413,6 +531,306 @@ func (e *Encoder) run(code *opcode) error { code = field.next } + case opStructFieldPtrHeadIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + code = field.next + code.ptr = field.ptr + field.offset + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadIntIndent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadIntIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt(e.ptrToInt(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt8Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt8Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt8(e.ptrToInt8(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt16Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt16Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt16(e.ptrToInt16(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt32Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt32Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt32(e.ptrToInt32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt64Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt64Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt64(e.ptrToInt64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUintIndent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUintIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint(e.ptrToUint(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint8Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint8Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint8(e.ptrToUint8(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint16Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint16Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint16(e.ptrToUint16(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint32Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint32Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint32(e.ptrToUint32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint64Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint64Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint64(e.ptrToUint64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadFloat32Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadFloat32Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeFloat32(e.ptrToFloat32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadFloat64Indent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadFloat64Indent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeFloat64(e.ptrToFloat64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadStringIndent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadStringIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeEscapedString(e.ptrToString(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadBoolIndent: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadBoolIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeBool(e.ptrToBool(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } case opStructFieldPtrHeadOmitEmpty: if code.ptr != 0 { code.ptr = e.ptrToPtr(code.ptr) @@ -425,11 +843,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') p := ptr + field.offset if p == 0 || *(*uintptr)(unsafe.Pointer(p)) == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) code = field.next code.ptr = p @@ -448,11 +868,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToInt(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeInt(v) code = field.next @@ -471,11 +893,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToInt8(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeInt8(v) code = field.next @@ -494,11 +918,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToInt16(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeInt16(v) code = field.next @@ -517,11 +943,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToInt32(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeInt32(v) code = field.next @@ -540,11 +968,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToInt64(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeInt64(v) code = field.next @@ -563,11 +993,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToUint(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeUint(v) code = field.next @@ -586,11 +1018,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToUint8(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeUint8(v) code = field.next @@ -609,11 +1043,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToUint16(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeUint16(v) code = field.next @@ -632,11 +1068,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToUint32(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeUint32(v) code = field.next @@ -655,11 +1093,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToUint64(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeUint64(v) code = field.next @@ -678,11 +1118,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToFloat32(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeFloat32(v) code = field.next @@ -701,11 +1143,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToFloat64(ptr + field.offset) if v == 0 { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeFloat64(v) code = field.next @@ -724,11 +1168,13 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToString(ptr + field.offset) if v == "" { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeEscapedString(v) code = field.next @@ -747,17 +1193,425 @@ func (e *Encoder) run(code *opcode) error { e.encodeString("null") code = field.end.next } else { + e.encodeIndent(code.indent) e.encodeByte('{') v := e.ptrToBool(ptr + field.offset) if !v { code = field.nextField } else { + e.encodeIndent(code.indent + 1) e.encodeBytes(field.key) e.encodeBool(v) code = field.next } field.nextField.ptr = field.ptr } + + case opStructFieldPtrHeadOmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadOmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + p := ptr + field.offset + if p == 0 || *(*uintptr)(unsafe.Pointer(p)) == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + code = field.next + code.ptr = p + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadIntOmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadIntOmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToInt(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadInt8OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadInt8OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToInt8(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt8(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadInt16OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadInt16OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToInt16(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt16(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadInt32OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadInt32OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToInt32(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt32(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadInt64OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadInt64OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToInt64(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeInt64(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadUintOmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadUintOmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToUint(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadUint8OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadUint8OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToUint8(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint8(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadUint16OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadUint16OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToUint16(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint16(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadUint32OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadUint32OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToUint32(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint32(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadUint64OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadUint64OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToUint64(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeUint64(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadFloat32OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadFloat32OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToFloat32(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeFloat32(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadFloat64OmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadFloat64OmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToFloat64(ptr + field.offset) + if v == 0 { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeFloat64(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadStringOmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadStringOmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToString(ptr + field.offset) + if v == "" { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeEscapedString(v) + code = field.next + } + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadBoolOmitEmptyIndent: + if code.ptr != 0 { + code.ptr = e.ptrToPtr(code.ptr) + } + fallthrough + case opStructFieldHeadBoolOmitEmptyIndent: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeString("null") + code = field.end.next + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '\n'}) + v := e.ptrToBool(ptr + field.offset) + if !v { + code = field.nextField + } else { + e.encodeIndent(code.indent + 1) + e.encodeBytes(field.key) + e.encodeByte(' ') + e.encodeBool(v) + code = field.next + } + field.nextField.ptr = field.ptr + } case opStructField: e.encodeByte(',') c := code.toStructFieldCode() @@ -863,6 +1717,142 @@ func (e *Encoder) run(code *opcode) error { e.encodeBytes(c.key) e.encodeBool(e.ptrToBool(c.ptr + c.offset)) code = code.next + + case opStructFieldIndent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + code = code.next + code.ptr = c.ptr + c.offset + c.nextField.ptr = c.ptr + case opStructFieldIntIndent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt(e.ptrToInt(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldInt8Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt8(e.ptrToInt8(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldInt16Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt16(e.ptrToInt16(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldInt32Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt32(e.ptrToInt32(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldInt64Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt64(e.ptrToInt64(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldUintIndent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint(e.ptrToUint(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldUint8Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint8(e.ptrToUint8(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldUint16Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint16(e.ptrToUint16(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldUint32Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint32(e.ptrToUint32(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldUint64Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint64(e.ptrToUint64(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldFloat32Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeFloat32(e.ptrToFloat32(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldFloat64Indent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeFloat64(e.ptrToFloat64(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldStringIndent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeEscapedString(e.ptrToString(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr + case opStructFieldBoolIndent: + c := code.toStructFieldCode() + e.encodeBytes([]byte{',', '\n'}) + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeBool(e.ptrToBool(c.ptr + c.offset)) + code = code.next + c.nextField.ptr = c.ptr case opStructFieldOmitEmpty: c := code.toStructFieldCode() p := c.ptr + c.offset @@ -1045,9 +2035,227 @@ func (e *Encoder) run(code *opcode) error { } code = code.next code.ptr = c.ptr + + case opStructFieldOmitEmptyIndent: + c := code.toStructFieldCode() + p := c.ptr + c.offset + if p == 0 || *(*uintptr)(unsafe.Pointer(p)) == 0 { + code = c.nextField + } else { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + code = code.next + code.ptr = p + } + c.nextField.ptr = c.ptr + case opStructFieldIntOmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToInt(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldInt8OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToInt8(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt8(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldInt16OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToInt16(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt16(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldInt32OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToInt32(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt32(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldInt64OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToInt64(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeInt64(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldUintOmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToUint(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldUint8OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToUint8(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint8(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldUint16OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToUint16(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint16(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldUint32OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToUint32(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint32(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldUint64OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToUint64(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeUint64(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldFloat32OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToFloat32(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeFloat32(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldFloat64OmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToFloat64(c.ptr + c.offset) + if v != 0 { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeFloat64(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldStringOmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToString(c.ptr + c.offset) + if v != "" { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeEscapedString(v) + } + code = code.next + code.ptr = c.ptr + case opStructFieldBoolOmitEmptyIndent: + c := code.toStructFieldCode() + v := e.ptrToBool(c.ptr + c.offset) + if v { + if e.buf[len(e.buf)-2] != '{' { + e.encodeBytes([]byte{',', '\n'}) + } + e.encodeIndent(c.indent) + e.encodeBytes(c.key) + e.encodeByte(' ') + e.encodeBool(v) + } + code = code.next + code.ptr = c.ptr case opStructEnd: e.encodeByte('}') code = code.next + case opStructEndIndent: + e.encodeByte('\n') + e.encodeIndent(code.indent) + e.encodeByte('}') + code = code.next case opEnd: goto END } diff --git a/json.go b/json.go index 0f1098f..b4d4623 100644 --- a/json.go +++ b/json.go @@ -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()