From ee1370127869cd5f66e38c707b94896a49422d9f Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Wed, 12 Aug 2020 16:54:15 +0900 Subject: [PATCH] Add Compact/Indent/HTMLEscape/Valid --- README.md | 22 +------- decode_float.go | 4 +- decode_map.go | 12 +++++ decode_slice.go | 28 ++++++++-- encode_compile.go | 76 +++++++++++++++++---------- encode_opcode.go | 55 ++++++++++++++++---- encode_vm.go | 130 +++++++++++++++++++++++++++++++++++++++++----- json.go | 69 ++++++++++++++++++++++++ json_test.go | 118 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 438 insertions(+), 76 deletions(-) create mode 100644 json_test.go diff --git a/README.md b/README.md index 934f108..b70abf8 100644 --- a/README.md +++ b/README.md @@ -83,27 +83,7 @@ Currently supported all types ## API -- [ ] `Compact` -- [ ] `HTMLEscape` -- [ ] `Indent` -- [x] `Marshal` -- [x] `MarshalIndent` -- [x] `Unmarshal` -- [ ] `Valid` -- [x] `NewDecoder` -- [x] `(*Decoder).Buffered` -- [x] `(*Decoder).Decode` -- [x] `(*Decoder).DisallowUnknownFields` -- [x] `(*Decoder).InputOffset` -- [x] `(*Decoder).More` -- [x] `(*Decoder).Token` -- [x] `(*Decoder).UseNumber` -- [x] `Delim` -- [x] `(Delim).String` -- [x] `NewEncoder` -- [x] `(*Encoder).Encode` -- [x] `(*Encoder).SetEscapeHTML` -- [x] `(*Encoder).SetIndent` +Implements All APIs ### Error diff --git a/decode_float.go b/decode_float.go index 4043964..5420a50 100644 --- a/decode_float.go +++ b/decode_float.go @@ -27,6 +27,7 @@ var floatTable = [256]bool{ '.': true, 'e': true, 'E': true, + '+': true, } func floatBytes(s *stream) []byte { @@ -79,8 +80,7 @@ func (d *floatDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, erro start := cursor cursor++ for ; cursor < buflen; cursor++ { - tk := int(buf[cursor]) - if (int('0') <= tk && tk <= int('9')) || tk == '.' || tk == 'e' || tk == 'E' { + if floatTable[buf[cursor]] { continue } break diff --git a/decode_map.go b/decode_map.go index 876eb11..8d1bd7d 100644 --- a/decode_map.go +++ b/decode_map.go @@ -65,7 +65,13 @@ func (d *mapDecoder) decodeStream(s *stream, p uintptr) error { default: return errExpected("{ character for map value", s.totalOffset()) } + s.skipWhiteSpace() mapValue := makemap(d.mapType, 0) + if s.buf[s.cursor+1] == '}' { + *(*unsafe.Pointer)(unsafe.Pointer(p)) = mapValue + s.cursor++ + return nil + } for { s.cursor++ var key interface{} @@ -131,7 +137,13 @@ func (d *mapDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error) return 0, errExpected("{ character for map value", cursor) } cursor++ + cursor = skipWhiteSpace(buf, cursor) mapValue := makemap(d.mapType, 0) + if buf[cursor] == '}' { + *(*unsafe.Pointer)(unsafe.Pointer(p)) = mapValue + cursor++ + return cursor, nil + } for ; cursor < buflen; cursor++ { var key interface{} keyCursor, err := d.setKey(buf, cursor, &key) diff --git a/decode_slice.go b/decode_slice.go index dbe3576..a0bfba3 100644 --- a/decode_slice.go +++ b/decode_slice.go @@ -72,12 +72,22 @@ func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error { } return nil case '[': + s.cursor++ + s.skipWhiteSpace() + if s.char() == ']' { + *(*reflect.SliceHeader)(unsafe.Pointer(p)) = reflect.SliceHeader{ + Data: uintptr(newArray(d.elemType, 0)), + Len: 0, + Cap: 0, + } + s.cursor++ + return nil + } idx := 0 slice := d.newSlice() cap := slice.cap data := slice.data for { - s.cursor++ if cap <= idx { src := reflect.SliceHeader{Data: uintptr(data), Len: idx, Cap: cap} cap *= 2 @@ -112,7 +122,6 @@ func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error { return nil case ',': idx++ - continue case nul: if s.read() { goto RETRY @@ -127,6 +136,7 @@ func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error { d.releaseSlice(slice) goto ERROR } + s.cursor++ } case nul: if s.read() { @@ -162,12 +172,22 @@ func (d *sliceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error cursor += 4 return cursor, nil case '[': + cursor++ + cursor = skipWhiteSpace(buf, cursor) + if buf[cursor] == ']' { + *(*reflect.SliceHeader)(unsafe.Pointer(p)) = reflect.SliceHeader{ + Data: uintptr(newArray(d.elemType, 0)), + Len: 0, + Cap: 0, + } + cursor++ + return cursor, nil + } idx := 0 slice := d.newSlice() cap := slice.cap data := slice.data for { - cursor++ if cap <= idx { src := reflect.SliceHeader{Data: uintptr(data), Len: idx, Cap: cap} cap *= 2 @@ -203,13 +223,13 @@ func (d *sliceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error return cursor, nil case ',': idx++ - continue default: slice.cap = cap slice.data = data d.releaseSlice(slice) return 0, errInvalidCharacter(buf[cursor], "slice", cursor) } + cursor++ } } } diff --git a/encode_compile.go b/encode_compile.go index a95d696..1f86e8d 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -16,13 +16,14 @@ func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) { if typ.Kind() == reflect.Ptr { typ = typ.Elem() } + root := true if typ.Kind() == reflect.Map { - return e.compileMap(typ, false, withIndent) + return e.compileMap(typ, false, root, withIndent) } - return e.compile(typ, withIndent) + return e.compile(typ, root, withIndent) } -func (e *Encoder) compile(typ *rtype, withIndent bool) (*opcode, error) { +func (e *Encoder) compile(typ *rtype, root, withIndent bool) (*opcode, error) { if typ.Implements(marshalJSONType) { return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil } else if typ.Implements(marshalTextType) { @@ -30,15 +31,17 @@ func (e *Encoder) compile(typ *rtype, withIndent bool) (*opcode, error) { } switch typ.Kind() { case reflect.Ptr: - return e.compilePtr(typ, withIndent) + return e.compilePtr(typ, root, withIndent) case reflect.Slice: - return e.compileSlice(typ, withIndent) + return e.compileSlice(typ, root, withIndent) case reflect.Array: - return e.compileArray(typ, withIndent) + return e.compileArray(typ, root, withIndent) case reflect.Map: - return e.compileMap(typ, true, withIndent) + return e.compileMap(typ, true, root, withIndent) case reflect.Struct: - return e.compileStruct(typ, withIndent) + return e.compileStruct(typ, root, withIndent) + case reflect.Interface: + return e.compileInterface(typ, root) case reflect.Int: return e.compileInt(typ) case reflect.Int8: @@ -69,8 +72,6 @@ func (e *Encoder) compile(typ *rtype, withIndent bool) (*opcode, error) { return e.compileString(typ) case reflect.Bool: return e.compileBool(typ) - case reflect.Interface: - return e.compileInterface(typ) } return nil, &UnsupportedTypeError{Type: rtype2type(typ)} } @@ -204,8 +205,8 @@ func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode { return code } -func (e *Encoder) compilePtr(typ *rtype, withIndent bool) (*opcode, error) { - code, err := e.compile(typ.Elem(), withIndent) +func (e *Encoder) compilePtr(typ *rtype, root, withIndent bool) (*opcode, error) { + code, err := e.compile(typ.Elem(), root, withIndent) if err != nil { return nil, err } @@ -268,16 +269,24 @@ func (e *Encoder) compileBool(typ *rtype) (*opcode, error) { return newOpCode(opBool, typ, e.indent, newEndOp(e.indent)), nil } -func (e *Encoder) compileInterface(typ *rtype) (*opcode, error) { - return newOpCode(opInterface, typ, e.indent, newEndOp(e.indent)), nil +func (e *Encoder) compileInterface(typ *rtype, root bool) (*opcode, error) { + return (*opcode)(unsafe.Pointer(&interfaceCode{ + opcodeHeader: &opcodeHeader{ + op: opInterface, + typ: typ, + indent: e.indent, + next: newEndOp(e.indent), + }, + root: root, + })), nil } -func (e *Encoder) compileSlice(typ *rtype, withIndent bool) (*opcode, error) { +func (e *Encoder) compileSlice(typ *rtype, root, withIndent bool) (*opcode, error) { elem := typ.Elem() size := elem.Size() e.indent++ - code, err := e.compile(elem, withIndent) + code, err := e.compile(elem, false, withIndent) e.indent-- if err != nil { @@ -298,8 +307,13 @@ func (e *Encoder) compileSlice(typ *rtype, withIndent bool) (*opcode, error) { } end := newOpCode(opSliceEnd, nil, e.indent, newEndOp(e.indent)) if withIndent { - header.op = opSliceHeadIndent - elemCode.op = opSliceElemIndent + if root { + header.op = opRootSliceHeadIndent + elemCode.op = opRootSliceElemIndent + } else { + header.op = opSliceHeadIndent + elemCode.op = opSliceElemIndent + } end.op = opSliceEndIndent } @@ -312,13 +326,13 @@ func (e *Encoder) compileSlice(typ *rtype, withIndent bool) (*opcode, error) { return (*opcode)(unsafe.Pointer(header)), nil } -func (e *Encoder) compileArray(typ *rtype, withIndent bool) (*opcode, error) { +func (e *Encoder) compileArray(typ *rtype, root, withIndent bool) (*opcode, error) { elem := typ.Elem() alen := typ.Len() size := elem.Size() e.indent++ - code, err := e.compile(elem, withIndent) + code, err := e.compile(elem, false, withIndent) e.indent-- if err != nil { @@ -369,18 +383,18 @@ func mapiternext(it unsafe.Pointer) //go:noescape func maplen(m unsafe.Pointer) int -func (e *Encoder) compileMap(typ *rtype, withLoad, withIndent bool) (*opcode, error) { +func (e *Encoder) compileMap(typ *rtype, withLoad, root, withIndent bool) (*opcode, error) { // header => code => value => code => key => code => value => code => end // ^ | // |_______________________| e.indent++ keyType := typ.Key() - keyCode, err := e.compile(keyType, withIndent) + keyCode, err := e.compile(keyType, false, withIndent) if err != nil { return nil, err } valueType := typ.Elem() - valueCode, err := e.compile(valueType, withIndent) + valueCode, err := e.compile(valueType, false, withIndent) if err != nil { return nil, err } @@ -397,11 +411,19 @@ func (e *Encoder) compileMap(typ *rtype, withLoad, withIndent bool) (*opcode, er if withIndent { if header.op == opMapHead { - header.op = opMapHeadIndent + if root { + header.op = opRootMapHeadIndent + } else { + header.op = opMapHeadIndent + } } else { header.op = opMapHeadLoadIndent } - key.op = opMapKeyIndent + if root { + key.op = opRootMapKeyIndent + } else { + key.op = opMapKeyIndent + } value.op = opMapValueIndent end.op = opMapEndIndent } @@ -724,7 +746,7 @@ func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) o return opStructField } -func (e *Encoder) compileStruct(typ *rtype, withIndent bool) (*opcode, error) { +func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, error) { // header => code => structField => code => end // ^ | // |__________| @@ -754,7 +776,7 @@ func (e *Encoder) compileStruct(typ *rtype, withIndent bool) (*opcode, error) { isOmitEmpty = opts[1] == "omitempty" } fieldType := type2rtype(field.Type) - valueCode, err := e.compile(fieldType, withIndent) + valueCode, err := e.compile(fieldType, false, withIndent) if err != nil { return nil, err } diff --git a/encode_opcode.go b/encode_opcode.go index 900bc16..7db664b 100644 --- a/encode_opcode.go +++ b/encode_opcode.go @@ -35,7 +35,9 @@ const ( opSliceEnd opSliceHeadIndent + opRootSliceHeadIndent opSliceElemIndent + opRootSliceElemIndent opSliceEndIndent opArrayHead @@ -50,12 +52,14 @@ const ( opMapHeadLoad opMapKey opMapValue - opMapEnd opMapHeadIndent + opRootMapHeadIndent opMapHeadLoadIndent opMapKeyIndent + opRootMapKeyIndent opMapValueIndent + opMapEnd opMapEndIndent // StructFieldHead @@ -310,8 +314,12 @@ func (t opType) String() string { case opSliceHeadIndent: return "SLICE_HEAD_INDENT" + case opRootSliceHeadIndent: + return "ROOT_SLICE_HEAD_INDENT" case opSliceElemIndent: return "SLICE_ELEM_INDENT" + case opRootSliceElemIndent: + return "ROOT_SLICE_ELEM_INDENT" case opSliceEndIndent: return "SLICE_END_INDENT" @@ -341,10 +349,14 @@ func (t opType) String() string { case opMapHeadIndent: return "MAP_HEAD_INDENT" + case opRootMapHeadIndent: + return "ROOT_MAP_HEAD_INDENT" case opMapHeadLoadIndent: return "MAP_HEAD_LOAD_INDENT" case opMapKeyIndent: return "MAP_KEY_INDENT" + case opRootMapKeyIndent: + return "ROOT_MAP_KEY_INDENT" case opMapValueIndent: return "MAP_VALUE_INDENT" case opMapEndIndent: @@ -780,9 +792,9 @@ func (c *opcode) beforeLastCode() *opcode { switch code.op { case opArrayElem, opArrayElemIndent: nextCode = code.toArrayElemCode().end - case opSliceElem, opSliceElemIndent: + case opSliceElem, opSliceElemIndent, opRootSliceElemIndent: nextCode = code.toSliceElemCode().end - case opMapKey, opMapKeyIndent: + case opMapKey, opMapKeyIndent, opRootMapKeyIndent: nextCode = code.toMapKeyCode().end default: nextCode = code.next @@ -809,13 +821,13 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode { code = c.toArrayHeaderCode().copy(codeMap) case opArrayElem, opArrayElemIndent: code = c.toArrayElemCode().copy(codeMap) - case opSliceHead, opSliceHeadIndent: + case opSliceHead, opSliceHeadIndent, opRootSliceHeadIndent: code = c.toSliceHeaderCode().copy(codeMap) - case opSliceElem, opSliceElemIndent: + case opSliceElem, opSliceElemIndent, opRootSliceElemIndent: code = c.toSliceElemCode().copy(codeMap) - case opMapHead, opMapHeadLoad, opMapHeadIndent, opMapHeadLoadIndent: + case opMapHead, opMapHeadLoad, opMapHeadIndent, opMapHeadLoadIndent, opRootMapHeadIndent: code = c.toMapHeadCode().copy(codeMap) - case opMapKey, opMapKeyIndent: + case opMapKey, opMapKeyIndent, opRootMapKeyIndent: code = c.toMapKeyCode().copy(codeMap) case opMapValue, opMapValueIndent: code = c.toMapValueCode().copy(codeMap) @@ -1017,9 +1029,9 @@ func (c *opcode) dump() string { switch code.op { case opArrayElem, opArrayElemIndent: code = code.toArrayElemCode().end - case opSliceElem, opSliceElemIndent: + case opSliceElem, opSliceElemIndent, opRootSliceElemIndent: code = code.toSliceElemCode().end - case opMapKey, opMapKeyIndent: + case opMapKey, opMapKeyIndent, opRootMapKeyIndent: code = code.toMapKeyCode().end default: code = code.next @@ -1060,6 +1072,10 @@ func (c *opcode) toMapValueCode() *mapValueCode { return (*mapValueCode)(unsafe.Pointer(c)) } +func (c *opcode) toInterfaceCode() *interfaceCode { + return (*interfaceCode)(unsafe.Pointer(c)) +} + type sliceHeaderCode struct { *opcodeHeader elem *sliceElemCode @@ -1285,6 +1301,27 @@ func (c *mapKeyCode) set(len int, iter unsafe.Pointer) { c.iter = iter } +type interfaceCode struct { + *opcodeHeader + root bool +} + +func (c *interfaceCode) copy(codeMap map[uintptr]*opcode) *opcode { + if c == nil { + return nil + } + addr := uintptr(unsafe.Pointer(c)) + if code, exists := codeMap[addr]; exists { + return code + } + iface := &interfaceCode{} + code := (*opcode)(unsafe.Pointer(iface)) + codeMap[addr] = code + + iface.opcodeHeader = c.opcodeHeader.copy(codeMap) + return code +} + type mapValueCode struct { *opcodeHeader iter unsafe.Pointer diff --git a/encode_vm.go b/encode_vm.go index 804d49a..8fa39ca 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -65,19 +65,26 @@ func (e *Encoder) run(code *opcode) error { e.encodeBool(e.ptrToBool(code.ptr)) code = code.next case opInterface: - ptr := code.ptr + ifaceCode := code.toInterfaceCode() + ptr := ifaceCode.ptr v := *(*interface{})(unsafe.Pointer(&interfaceHeader{ - typ: code.typ, + typ: ifaceCode.typ, ptr: unsafe.Pointer(ptr), })) - vv := reflect.ValueOf(v).Interface() + rv := reflect.ValueOf(v) + if rv.IsNil() { + e.encodeNull() + code = ifaceCode.next + break + } + vv := rv.Interface() header := (*interfaceHeader)(unsafe.Pointer(&vv)) typ := header.typ if typ.Kind() == reflect.Ptr { typ = typ.Elem() } - e.indent = code.indent - c, err := e.compile(typ, e.enabledIndent) + e.indent = ifaceCode.indent + c, err := e.compile(typ, ifaceCode.root, e.enabledIndent) if err != nil { return err } @@ -153,17 +160,37 @@ func (e *Encoder) run(code *opcode) error { e.encodeNull() code = headerCode.end.next } else { - e.encodeBytes([]byte{'[', '\n'}) header := (*reflect.SliceHeader)(unsafe.Pointer(p)) headerCode.elem.set(header) if header.Len > 0 { + e.encodeBytes([]byte{'[', '\n'}) e.encodeIndent(code.indent + 1) code = code.next code.ptr = header.Data } else { - e.encodeByte('\n') e.encodeIndent(code.indent) - e.encodeBytes([]byte{']', '\n'}) + e.encodeBytes([]byte{'[', ']', '\n'}) + code = headerCode.end.next + } + } + case opRootSliceHeadIndent: + p := code.ptr + headerCode := code.toSliceHeaderCode() + if p == 0 { + e.encodeIndent(code.indent) + e.encodeNull() + code = headerCode.end.next + } else { + header := (*reflect.SliceHeader)(unsafe.Pointer(p)) + headerCode.elem.set(header) + if header.Len > 0 { + e.encodeBytes([]byte{'[', '\n'}) + e.encodeIndent(code.indent + 1) + code = code.next + code.ptr = header.Data + } else { + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'[', ']'}) code = headerCode.end.next } } @@ -181,6 +208,20 @@ func (e *Encoder) run(code *opcode) error { e.encodeBytes([]byte{']', '\n'}) code = c.end.next } + case opRootSliceElemIndent: + 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{']'}) + code = c.end.next + } case opArrayHead: p := code.ptr headerCode := code.toArrayHeaderCode() @@ -287,7 +328,6 @@ func (e *Encoder) run(code *opcode) error { code = mapHeadCode.end.next } } - case opMapKey: c := code.toMapKeyCode() c.idx++ @@ -315,9 +355,9 @@ func (e *Encoder) run(code *opcode) error { e.encodeNull() code = mapHeadCode.end.next } else { - e.encodeBytes([]byte{'{', '\n'}) mlen := maplen(unsafe.Pointer(ptr)) if mlen > 0 { + e.encodeBytes([]byte{'{', '\n'}) iter := mapiterinit(code.typ, unsafe.Pointer(ptr)) mapHeadCode.key.set(mlen, iter) mapHeadCode.value.set(iter) @@ -326,9 +366,58 @@ func (e *Encoder) run(code *opcode) error { code = code.next e.encodeIndent(code.indent) } else { - e.encodeByte('\n') - e.encodeIndent(code.indent - 1) - e.encodeBytes([]byte{'}', '\n'}) + e.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '}', '\n'}) + code = mapHeadCode.end.next + } + } + case opMapHeadLoadIndent: + ptr := code.ptr + mapHeadCode := code.toMapHeadCode() + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeNull() + code = mapHeadCode.end.next + } else { + // load pointer + ptr = uintptr(*(*unsafe.Pointer)(unsafe.Pointer(ptr))) + mlen := maplen(unsafe.Pointer(ptr)) + if mlen > 0 { + e.encodeBytes([]byte{'{', '\n'}) + 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.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '}', '\n'}) + code = mapHeadCode.end.next + } + } + case opRootMapHeadIndent: + ptr := code.ptr + mapHeadCode := code.toMapHeadCode() + if ptr == 0 { + e.encodeIndent(code.indent) + e.encodeNull() + code = mapHeadCode.end.next + } else { + mlen := maplen(unsafe.Pointer(ptr)) + if mlen > 0 { + e.encodeBytes([]byte{'{', '\n'}) + 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.encodeIndent(code.indent) + e.encodeBytes([]byte{'{', '}'}) code = mapHeadCode.end.next } } @@ -347,6 +436,21 @@ func (e *Encoder) run(code *opcode) error { e.encodeBytes([]byte{'}', '\n'}) code = c.end.next } + case opRootMapKeyIndent: + 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{'}'}) + code = c.end.next + } case opMapValueIndent: e.encodeBytes([]byte{':', ' '}) c := code.toMapValueCode() diff --git a/json.go b/json.go index adc88b5..9af4282 100644 --- a/json.go +++ b/json.go @@ -296,6 +296,10 @@ func (n Number) Int64() (int64, error) { return strconv.ParseInt(string(n), 10, 64) } +func (n Number) MarshalJSON() ([]byte, error) { + return []byte(n), nil +} + // RawMessage is a raw encoded JSON value. // It implements Marshaler and Unmarshaler and can // be used to delay JSON decoding or precompute a JSON encoding. @@ -317,3 +321,68 @@ func (m *RawMessage) UnmarshalJSON(data []byte) error { *m = append((*m)[0:0], data...) return nil } + +// Compact appends to dst the JSON-encoded src with +// insignificant space characters elided. +func Compact(dst *bytes.Buffer, src []byte) error { + var v interface{} + dec := NewDecoder(bytes.NewBuffer(src)) + dec.UseNumber() + if err := dec.Decode(&v); err != nil { + return err + } + enc := NewEncoder(dst) + enc.SetEscapeHTML(false) + return enc.Encode(v) +} + +// Indent appends to dst an indented form of the JSON-encoded src. +// Each element in a JSON object or array begins on a new, +// indented line beginning with prefix followed by one or more +// copies of indent according to the indentation nesting. +// The data appended to dst does not begin with the prefix nor +// any indentation, to make it easier to embed inside other formatted JSON data. +// Although leading space characters (space, tab, carriage return, newline) +// at the beginning of src are dropped, trailing space characters +// at the end of src are preserved and copied to dst. +// For example, if src has no trailing spaces, neither will dst; +// if src ends in a trailing newline, so will dst. +func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { + var v interface{} + dec := NewDecoder(bytes.NewBuffer(src)) + dec.UseNumber() + if err := dec.Decode(&v); err != nil { + return err + } + enc := NewEncoder(dst) + enc.SetEscapeHTML(false) + enc.SetIndent(prefix, indent) + return enc.Encode(v) +} + +// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 +// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 +// so that the JSON will be safe to embed inside HTML