mirror of https://github.com/goccy/go-json.git
Merge pull request #18 from goccy/feature/support-all-apis
Add Compact/Indent/HTMLEscape/Valid
This commit is contained in:
commit
18e30e0106
22
README.md
22
README.md
|
@ -83,27 +83,7 @@ Currently supported all types
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
- [ ] `Compact`
|
Implements All APIs
|
||||||
- [ ] `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`
|
|
||||||
|
|
||||||
### Error
|
### Error
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ var floatTable = [256]bool{
|
||||||
'.': true,
|
'.': true,
|
||||||
'e': true,
|
'e': true,
|
||||||
'E': true,
|
'E': true,
|
||||||
|
'+': true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func floatBytes(s *stream) []byte {
|
func floatBytes(s *stream) []byte {
|
||||||
|
@ -79,8 +80,7 @@ func (d *floatDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, erro
|
||||||
start := cursor
|
start := cursor
|
||||||
cursor++
|
cursor++
|
||||||
for ; cursor < buflen; cursor++ {
|
for ; cursor < buflen; cursor++ {
|
||||||
tk := int(buf[cursor])
|
if floatTable[buf[cursor]] {
|
||||||
if (int('0') <= tk && tk <= int('9')) || tk == '.' || tk == 'e' || tk == 'E' {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
|
@ -65,7 +65,13 @@ func (d *mapDecoder) decodeStream(s *stream, p uintptr) error {
|
||||||
default:
|
default:
|
||||||
return errExpected("{ character for map value", s.totalOffset())
|
return errExpected("{ character for map value", s.totalOffset())
|
||||||
}
|
}
|
||||||
|
s.skipWhiteSpace()
|
||||||
mapValue := makemap(d.mapType, 0)
|
mapValue := makemap(d.mapType, 0)
|
||||||
|
if s.buf[s.cursor+1] == '}' {
|
||||||
|
*(*unsafe.Pointer)(unsafe.Pointer(p)) = mapValue
|
||||||
|
s.cursor++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
s.cursor++
|
s.cursor++
|
||||||
var key interface{}
|
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)
|
return 0, errExpected("{ character for map value", cursor)
|
||||||
}
|
}
|
||||||
cursor++
|
cursor++
|
||||||
|
cursor = skipWhiteSpace(buf, cursor)
|
||||||
mapValue := makemap(d.mapType, 0)
|
mapValue := makemap(d.mapType, 0)
|
||||||
|
if buf[cursor] == '}' {
|
||||||
|
*(*unsafe.Pointer)(unsafe.Pointer(p)) = mapValue
|
||||||
|
cursor++
|
||||||
|
return cursor, nil
|
||||||
|
}
|
||||||
for ; cursor < buflen; cursor++ {
|
for ; cursor < buflen; cursor++ {
|
||||||
var key interface{}
|
var key interface{}
|
||||||
keyCursor, err := d.setKey(buf, cursor, &key)
|
keyCursor, err := d.setKey(buf, cursor, &key)
|
||||||
|
|
|
@ -72,12 +72,22 @@ func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case '[':
|
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
|
idx := 0
|
||||||
slice := d.newSlice()
|
slice := d.newSlice()
|
||||||
cap := slice.cap
|
cap := slice.cap
|
||||||
data := slice.data
|
data := slice.data
|
||||||
for {
|
for {
|
||||||
s.cursor++
|
|
||||||
if cap <= idx {
|
if cap <= idx {
|
||||||
src := reflect.SliceHeader{Data: uintptr(data), Len: idx, Cap: cap}
|
src := reflect.SliceHeader{Data: uintptr(data), Len: idx, Cap: cap}
|
||||||
cap *= 2
|
cap *= 2
|
||||||
|
@ -112,7 +122,6 @@ func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error {
|
||||||
return nil
|
return nil
|
||||||
case ',':
|
case ',':
|
||||||
idx++
|
idx++
|
||||||
continue
|
|
||||||
case nul:
|
case nul:
|
||||||
if s.read() {
|
if s.read() {
|
||||||
goto RETRY
|
goto RETRY
|
||||||
|
@ -127,6 +136,7 @@ func (d *sliceDecoder) decodeStream(s *stream, p uintptr) error {
|
||||||
d.releaseSlice(slice)
|
d.releaseSlice(slice)
|
||||||
goto ERROR
|
goto ERROR
|
||||||
}
|
}
|
||||||
|
s.cursor++
|
||||||
}
|
}
|
||||||
case nul:
|
case nul:
|
||||||
if s.read() {
|
if s.read() {
|
||||||
|
@ -162,12 +172,22 @@ func (d *sliceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error
|
||||||
cursor += 4
|
cursor += 4
|
||||||
return cursor, nil
|
return cursor, nil
|
||||||
case '[':
|
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
|
idx := 0
|
||||||
slice := d.newSlice()
|
slice := d.newSlice()
|
||||||
cap := slice.cap
|
cap := slice.cap
|
||||||
data := slice.data
|
data := slice.data
|
||||||
for {
|
for {
|
||||||
cursor++
|
|
||||||
if cap <= idx {
|
if cap <= idx {
|
||||||
src := reflect.SliceHeader{Data: uintptr(data), Len: idx, Cap: cap}
|
src := reflect.SliceHeader{Data: uintptr(data), Len: idx, Cap: cap}
|
||||||
cap *= 2
|
cap *= 2
|
||||||
|
@ -203,13 +223,13 @@ func (d *sliceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error
|
||||||
return cursor, nil
|
return cursor, nil
|
||||||
case ',':
|
case ',':
|
||||||
idx++
|
idx++
|
||||||
continue
|
|
||||||
default:
|
default:
|
||||||
slice.cap = cap
|
slice.cap = cap
|
||||||
slice.data = data
|
slice.data = data
|
||||||
d.releaseSlice(slice)
|
d.releaseSlice(slice)
|
||||||
return 0, errInvalidCharacter(buf[cursor], "slice", cursor)
|
return 0, errInvalidCharacter(buf[cursor], "slice", cursor)
|
||||||
}
|
}
|
||||||
|
cursor++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,14 @@ func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) {
|
||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
typ = typ.Elem()
|
typ = typ.Elem()
|
||||||
}
|
}
|
||||||
|
root := true
|
||||||
if typ.Kind() == reflect.Map {
|
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) {
|
if typ.Implements(marshalJSONType) {
|
||||||
return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil
|
return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil
|
||||||
} else if typ.Implements(marshalTextType) {
|
} else if typ.Implements(marshalTextType) {
|
||||||
|
@ -30,15 +31,17 @@ func (e *Encoder) compile(typ *rtype, withIndent bool) (*opcode, error) {
|
||||||
}
|
}
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return e.compilePtr(typ, withIndent)
|
return e.compilePtr(typ, root, withIndent)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
return e.compileSlice(typ, withIndent)
|
return e.compileSlice(typ, root, withIndent)
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
return e.compileArray(typ, withIndent)
|
return e.compileArray(typ, root, withIndent)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
return e.compileMap(typ, true, withIndent)
|
return e.compileMap(typ, true, root, withIndent)
|
||||||
case reflect.Struct:
|
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:
|
case reflect.Int:
|
||||||
return e.compileInt(typ)
|
return e.compileInt(typ)
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
|
@ -69,8 +72,6 @@ func (e *Encoder) compile(typ *rtype, withIndent bool) (*opcode, error) {
|
||||||
return e.compileString(typ)
|
return e.compileString(typ)
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return e.compileBool(typ)
|
return e.compileBool(typ)
|
||||||
case reflect.Interface:
|
|
||||||
return e.compileInterface(typ)
|
|
||||||
}
|
}
|
||||||
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
|
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
|
||||||
}
|
}
|
||||||
|
@ -204,8 +205,8 @@ func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compilePtr(typ *rtype, withIndent bool) (*opcode, error) {
|
func (e *Encoder) compilePtr(typ *rtype, root, withIndent bool) (*opcode, error) {
|
||||||
code, err := e.compile(typ.Elem(), withIndent)
|
code, err := e.compile(typ.Elem(), root, withIndent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
return newOpCode(opBool, typ, e.indent, newEndOp(e.indent)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInterface(typ *rtype) (*opcode, error) {
|
func (e *Encoder) compileInterface(typ *rtype, root bool) (*opcode, error) {
|
||||||
return newOpCode(opInterface, typ, e.indent, newEndOp(e.indent)), nil
|
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()
|
elem := typ.Elem()
|
||||||
size := elem.Size()
|
size := elem.Size()
|
||||||
|
|
||||||
e.indent++
|
e.indent++
|
||||||
code, err := e.compile(elem, withIndent)
|
code, err := e.compile(elem, false, withIndent)
|
||||||
e.indent--
|
e.indent--
|
||||||
|
|
||||||
if err != nil {
|
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))
|
end := newOpCode(opSliceEnd, nil, e.indent, newEndOp(e.indent))
|
||||||
if withIndent {
|
if withIndent {
|
||||||
|
if root {
|
||||||
|
header.op = opRootSliceHeadIndent
|
||||||
|
elemCode.op = opRootSliceElemIndent
|
||||||
|
} else {
|
||||||
header.op = opSliceHeadIndent
|
header.op = opSliceHeadIndent
|
||||||
elemCode.op = opSliceElemIndent
|
elemCode.op = opSliceElemIndent
|
||||||
|
}
|
||||||
end.op = opSliceEndIndent
|
end.op = opSliceEndIndent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,13 +326,13 @@ func (e *Encoder) compileSlice(typ *rtype, withIndent bool) (*opcode, error) {
|
||||||
return (*opcode)(unsafe.Pointer(header)), nil
|
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()
|
elem := typ.Elem()
|
||||||
alen := typ.Len()
|
alen := typ.Len()
|
||||||
size := elem.Size()
|
size := elem.Size()
|
||||||
|
|
||||||
e.indent++
|
e.indent++
|
||||||
code, err := e.compile(elem, withIndent)
|
code, err := e.compile(elem, false, withIndent)
|
||||||
e.indent--
|
e.indent--
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -369,18 +383,18 @@ func mapiternext(it unsafe.Pointer)
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func maplen(m unsafe.Pointer) int
|
func maplen(m unsafe.Pointer) int
|
||||||
|
|
||||||
func (e *Encoder) compileMap(typ *rtype, 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
|
// header => code => value => code => key => code => value => code => end
|
||||||
// ^ |
|
// ^ |
|
||||||
// |_______________________|
|
// |_______________________|
|
||||||
e.indent++
|
e.indent++
|
||||||
keyType := typ.Key()
|
keyType := typ.Key()
|
||||||
keyCode, err := e.compile(keyType, withIndent)
|
keyCode, err := e.compile(keyType, false, withIndent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
valueType := typ.Elem()
|
valueType := typ.Elem()
|
||||||
valueCode, err := e.compile(valueType, withIndent)
|
valueCode, err := e.compile(valueType, false, withIndent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -397,11 +411,19 @@ func (e *Encoder) compileMap(typ *rtype, withLoad, withIndent bool) (*opcode, er
|
||||||
|
|
||||||
if withIndent {
|
if withIndent {
|
||||||
if header.op == opMapHead {
|
if header.op == opMapHead {
|
||||||
|
if root {
|
||||||
|
header.op = opRootMapHeadIndent
|
||||||
|
} else {
|
||||||
header.op = opMapHeadIndent
|
header.op = opMapHeadIndent
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
header.op = opMapHeadLoadIndent
|
header.op = opMapHeadLoadIndent
|
||||||
}
|
}
|
||||||
|
if root {
|
||||||
|
key.op = opRootMapKeyIndent
|
||||||
|
} else {
|
||||||
key.op = opMapKeyIndent
|
key.op = opMapKeyIndent
|
||||||
|
}
|
||||||
value.op = opMapValueIndent
|
value.op = opMapValueIndent
|
||||||
end.op = opMapEndIndent
|
end.op = opMapEndIndent
|
||||||
}
|
}
|
||||||
|
@ -724,7 +746,7 @@ func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) o
|
||||||
return opStructField
|
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
|
// header => code => structField => code => end
|
||||||
// ^ |
|
// ^ |
|
||||||
// |__________|
|
// |__________|
|
||||||
|
@ -754,7 +776,7 @@ func (e *Encoder) compileStruct(typ *rtype, withIndent bool) (*opcode, error) {
|
||||||
isOmitEmpty = opts[1] == "omitempty"
|
isOmitEmpty = opts[1] == "omitempty"
|
||||||
}
|
}
|
||||||
fieldType := type2rtype(field.Type)
|
fieldType := type2rtype(field.Type)
|
||||||
valueCode, err := e.compile(fieldType, withIndent)
|
valueCode, err := e.compile(fieldType, false, withIndent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,9 @@ const (
|
||||||
opSliceEnd
|
opSliceEnd
|
||||||
|
|
||||||
opSliceHeadIndent
|
opSliceHeadIndent
|
||||||
|
opRootSliceHeadIndent
|
||||||
opSliceElemIndent
|
opSliceElemIndent
|
||||||
|
opRootSliceElemIndent
|
||||||
opSliceEndIndent
|
opSliceEndIndent
|
||||||
|
|
||||||
opArrayHead
|
opArrayHead
|
||||||
|
@ -50,12 +52,14 @@ const (
|
||||||
opMapHeadLoad
|
opMapHeadLoad
|
||||||
opMapKey
|
opMapKey
|
||||||
opMapValue
|
opMapValue
|
||||||
opMapEnd
|
|
||||||
|
|
||||||
opMapHeadIndent
|
opMapHeadIndent
|
||||||
|
opRootMapHeadIndent
|
||||||
opMapHeadLoadIndent
|
opMapHeadLoadIndent
|
||||||
opMapKeyIndent
|
opMapKeyIndent
|
||||||
|
opRootMapKeyIndent
|
||||||
opMapValueIndent
|
opMapValueIndent
|
||||||
|
opMapEnd
|
||||||
opMapEndIndent
|
opMapEndIndent
|
||||||
|
|
||||||
// StructFieldHead
|
// StructFieldHead
|
||||||
|
@ -310,8 +314,12 @@ func (t opType) String() string {
|
||||||
|
|
||||||
case opSliceHeadIndent:
|
case opSliceHeadIndent:
|
||||||
return "SLICE_HEAD_INDENT"
|
return "SLICE_HEAD_INDENT"
|
||||||
|
case opRootSliceHeadIndent:
|
||||||
|
return "ROOT_SLICE_HEAD_INDENT"
|
||||||
case opSliceElemIndent:
|
case opSliceElemIndent:
|
||||||
return "SLICE_ELEM_INDENT"
|
return "SLICE_ELEM_INDENT"
|
||||||
|
case opRootSliceElemIndent:
|
||||||
|
return "ROOT_SLICE_ELEM_INDENT"
|
||||||
case opSliceEndIndent:
|
case opSliceEndIndent:
|
||||||
return "SLICE_END_INDENT"
|
return "SLICE_END_INDENT"
|
||||||
|
|
||||||
|
@ -341,10 +349,14 @@ func (t opType) String() string {
|
||||||
|
|
||||||
case opMapHeadIndent:
|
case opMapHeadIndent:
|
||||||
return "MAP_HEAD_INDENT"
|
return "MAP_HEAD_INDENT"
|
||||||
|
case opRootMapHeadIndent:
|
||||||
|
return "ROOT_MAP_HEAD_INDENT"
|
||||||
case opMapHeadLoadIndent:
|
case opMapHeadLoadIndent:
|
||||||
return "MAP_HEAD_LOAD_INDENT"
|
return "MAP_HEAD_LOAD_INDENT"
|
||||||
case opMapKeyIndent:
|
case opMapKeyIndent:
|
||||||
return "MAP_KEY_INDENT"
|
return "MAP_KEY_INDENT"
|
||||||
|
case opRootMapKeyIndent:
|
||||||
|
return "ROOT_MAP_KEY_INDENT"
|
||||||
case opMapValueIndent:
|
case opMapValueIndent:
|
||||||
return "MAP_VALUE_INDENT"
|
return "MAP_VALUE_INDENT"
|
||||||
case opMapEndIndent:
|
case opMapEndIndent:
|
||||||
|
@ -780,9 +792,9 @@ func (c *opcode) beforeLastCode() *opcode {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
case opArrayElem, opArrayElemIndent:
|
case opArrayElem, opArrayElemIndent:
|
||||||
nextCode = code.toArrayElemCode().end
|
nextCode = code.toArrayElemCode().end
|
||||||
case opSliceElem, opSliceElemIndent:
|
case opSliceElem, opSliceElemIndent, opRootSliceElemIndent:
|
||||||
nextCode = code.toSliceElemCode().end
|
nextCode = code.toSliceElemCode().end
|
||||||
case opMapKey, opMapKeyIndent:
|
case opMapKey, opMapKeyIndent, opRootMapKeyIndent:
|
||||||
nextCode = code.toMapKeyCode().end
|
nextCode = code.toMapKeyCode().end
|
||||||
default:
|
default:
|
||||||
nextCode = code.next
|
nextCode = code.next
|
||||||
|
@ -809,13 +821,13 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
|
||||||
code = c.toArrayHeaderCode().copy(codeMap)
|
code = c.toArrayHeaderCode().copy(codeMap)
|
||||||
case opArrayElem, opArrayElemIndent:
|
case opArrayElem, opArrayElemIndent:
|
||||||
code = c.toArrayElemCode().copy(codeMap)
|
code = c.toArrayElemCode().copy(codeMap)
|
||||||
case opSliceHead, opSliceHeadIndent:
|
case opSliceHead, opSliceHeadIndent, opRootSliceHeadIndent:
|
||||||
code = c.toSliceHeaderCode().copy(codeMap)
|
code = c.toSliceHeaderCode().copy(codeMap)
|
||||||
case opSliceElem, opSliceElemIndent:
|
case opSliceElem, opSliceElemIndent, opRootSliceElemIndent:
|
||||||
code = c.toSliceElemCode().copy(codeMap)
|
code = c.toSliceElemCode().copy(codeMap)
|
||||||
case opMapHead, opMapHeadLoad, opMapHeadIndent, opMapHeadLoadIndent:
|
case opMapHead, opMapHeadLoad, opMapHeadIndent, opMapHeadLoadIndent, opRootMapHeadIndent:
|
||||||
code = c.toMapHeadCode().copy(codeMap)
|
code = c.toMapHeadCode().copy(codeMap)
|
||||||
case opMapKey, opMapKeyIndent:
|
case opMapKey, opMapKeyIndent, opRootMapKeyIndent:
|
||||||
code = c.toMapKeyCode().copy(codeMap)
|
code = c.toMapKeyCode().copy(codeMap)
|
||||||
case opMapValue, opMapValueIndent:
|
case opMapValue, opMapValueIndent:
|
||||||
code = c.toMapValueCode().copy(codeMap)
|
code = c.toMapValueCode().copy(codeMap)
|
||||||
|
@ -1017,9 +1029,9 @@ func (c *opcode) dump() string {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
case opArrayElem, opArrayElemIndent:
|
case opArrayElem, opArrayElemIndent:
|
||||||
code = code.toArrayElemCode().end
|
code = code.toArrayElemCode().end
|
||||||
case opSliceElem, opSliceElemIndent:
|
case opSliceElem, opSliceElemIndent, opRootSliceElemIndent:
|
||||||
code = code.toSliceElemCode().end
|
code = code.toSliceElemCode().end
|
||||||
case opMapKey, opMapKeyIndent:
|
case opMapKey, opMapKeyIndent, opRootMapKeyIndent:
|
||||||
code = code.toMapKeyCode().end
|
code = code.toMapKeyCode().end
|
||||||
default:
|
default:
|
||||||
code = code.next
|
code = code.next
|
||||||
|
@ -1060,6 +1072,10 @@ func (c *opcode) toMapValueCode() *mapValueCode {
|
||||||
return (*mapValueCode)(unsafe.Pointer(c))
|
return (*mapValueCode)(unsafe.Pointer(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *opcode) toInterfaceCode() *interfaceCode {
|
||||||
|
return (*interfaceCode)(unsafe.Pointer(c))
|
||||||
|
}
|
||||||
|
|
||||||
type sliceHeaderCode struct {
|
type sliceHeaderCode struct {
|
||||||
*opcodeHeader
|
*opcodeHeader
|
||||||
elem *sliceElemCode
|
elem *sliceElemCode
|
||||||
|
@ -1285,6 +1301,27 @@ func (c *mapKeyCode) set(len int, iter unsafe.Pointer) {
|
||||||
c.iter = iter
|
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 {
|
type mapValueCode struct {
|
||||||
*opcodeHeader
|
*opcodeHeader
|
||||||
iter unsafe.Pointer
|
iter unsafe.Pointer
|
||||||
|
|
130
encode_vm.go
130
encode_vm.go
|
@ -65,19 +65,26 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
e.encodeBool(e.ptrToBool(code.ptr))
|
e.encodeBool(e.ptrToBool(code.ptr))
|
||||||
code = code.next
|
code = code.next
|
||||||
case opInterface:
|
case opInterface:
|
||||||
ptr := code.ptr
|
ifaceCode := code.toInterfaceCode()
|
||||||
|
ptr := ifaceCode.ptr
|
||||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||||
typ: code.typ,
|
typ: ifaceCode.typ,
|
||||||
ptr: unsafe.Pointer(ptr),
|
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))
|
header := (*interfaceHeader)(unsafe.Pointer(&vv))
|
||||||
typ := header.typ
|
typ := header.typ
|
||||||
if typ.Kind() == reflect.Ptr {
|
if typ.Kind() == reflect.Ptr {
|
||||||
typ = typ.Elem()
|
typ = typ.Elem()
|
||||||
}
|
}
|
||||||
e.indent = code.indent
|
e.indent = ifaceCode.indent
|
||||||
c, err := e.compile(typ, e.enabledIndent)
|
c, err := e.compile(typ, ifaceCode.root, e.enabledIndent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -153,17 +160,37 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
e.encodeNull()
|
e.encodeNull()
|
||||||
code = headerCode.end.next
|
code = headerCode.end.next
|
||||||
} else {
|
} else {
|
||||||
e.encodeBytes([]byte{'[', '\n'})
|
|
||||||
header := (*reflect.SliceHeader)(unsafe.Pointer(p))
|
header := (*reflect.SliceHeader)(unsafe.Pointer(p))
|
||||||
headerCode.elem.set(header)
|
headerCode.elem.set(header)
|
||||||
if header.Len > 0 {
|
if header.Len > 0 {
|
||||||
|
e.encodeBytes([]byte{'[', '\n'})
|
||||||
e.encodeIndent(code.indent + 1)
|
e.encodeIndent(code.indent + 1)
|
||||||
code = code.next
|
code = code.next
|
||||||
code.ptr = header.Data
|
code.ptr = header.Data
|
||||||
} else {
|
} else {
|
||||||
e.encodeByte('\n')
|
|
||||||
e.encodeIndent(code.indent)
|
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
|
code = headerCode.end.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,6 +208,20 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
e.encodeBytes([]byte{']', '\n'})
|
e.encodeBytes([]byte{']', '\n'})
|
||||||
code = c.end.next
|
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:
|
case opArrayHead:
|
||||||
p := code.ptr
|
p := code.ptr
|
||||||
headerCode := code.toArrayHeaderCode()
|
headerCode := code.toArrayHeaderCode()
|
||||||
|
@ -287,7 +328,6 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
code = mapHeadCode.end.next
|
code = mapHeadCode.end.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case opMapKey:
|
case opMapKey:
|
||||||
c := code.toMapKeyCode()
|
c := code.toMapKeyCode()
|
||||||
c.idx++
|
c.idx++
|
||||||
|
@ -315,9 +355,9 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
e.encodeNull()
|
e.encodeNull()
|
||||||
code = mapHeadCode.end.next
|
code = mapHeadCode.end.next
|
||||||
} else {
|
} else {
|
||||||
e.encodeBytes([]byte{'{', '\n'})
|
|
||||||
mlen := maplen(unsafe.Pointer(ptr))
|
mlen := maplen(unsafe.Pointer(ptr))
|
||||||
if mlen > 0 {
|
if mlen > 0 {
|
||||||
|
e.encodeBytes([]byte{'{', '\n'})
|
||||||
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||||
mapHeadCode.key.set(mlen, iter)
|
mapHeadCode.key.set(mlen, iter)
|
||||||
mapHeadCode.value.set(iter)
|
mapHeadCode.value.set(iter)
|
||||||
|
@ -326,9 +366,58 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
code = code.next
|
code = code.next
|
||||||
e.encodeIndent(code.indent)
|
e.encodeIndent(code.indent)
|
||||||
} else {
|
} else {
|
||||||
e.encodeByte('\n')
|
e.encodeIndent(code.indent)
|
||||||
e.encodeIndent(code.indent - 1)
|
e.encodeBytes([]byte{'{', '}', '\n'})
|
||||||
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
|
code = mapHeadCode.end.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,6 +436,21 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
e.encodeBytes([]byte{'}', '\n'})
|
e.encodeBytes([]byte{'}', '\n'})
|
||||||
code = c.end.next
|
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:
|
case opMapValueIndent:
|
||||||
e.encodeBytes([]byte{':', ' '})
|
e.encodeBytes([]byte{':', ' '})
|
||||||
c := code.toMapValueCode()
|
c := code.toMapValueCode()
|
||||||
|
|
69
json.go
69
json.go
|
@ -296,6 +296,10 @@ func (n Number) Int64() (int64, error) {
|
||||||
return strconv.ParseInt(string(n), 10, 64)
|
return strconv.ParseInt(string(n), 10, 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n Number) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
// RawMessage is a raw encoded JSON value.
|
// RawMessage is a raw encoded JSON value.
|
||||||
// It implements Marshaler and Unmarshaler and can
|
// It implements Marshaler and Unmarshaler and can
|
||||||
// be used to delay JSON decoding or precompute a JSON encoding.
|
// 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...)
|
*m = append((*m)[0:0], data...)
|
||||||
return nil
|
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 <script> tags.
|
||||||
|
// For historical reasons, web browsers don't honor standard HTML
|
||||||
|
// escaping within <script> tags, so an alternative JSON encoding must
|
||||||
|
// be used.
|
||||||
|
func HTMLEscape(dst *bytes.Buffer, src []byte) {
|
||||||
|
var v interface{}
|
||||||
|
dec := NewDecoder(bytes.NewBuffer(src))
|
||||||
|
dec.UseNumber()
|
||||||
|
if err := dec.Decode(&v); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enc := NewEncoder(dst)
|
||||||
|
enc.SetEscapeHTML(true)
|
||||||
|
enc.Encode(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid reports whether data is a valid JSON encoding.
|
||||||
|
func Valid(data []byte) bool {
|
||||||
|
var v interface{}
|
||||||
|
if err := Unmarshal(data, &v); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
package json_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/goccy/go-json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validTests = []struct {
|
||||||
|
data string
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{`foo`, false},
|
||||||
|
{`}{`, false},
|
||||||
|
{`{]`, false},
|
||||||
|
{`{}`, true},
|
||||||
|
{`{"foo":"bar"}`, true},
|
||||||
|
{`{"foo":"bar","bar":{"baz":["qux"]}}`, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValid(t *testing.T) {
|
||||||
|
for _, tt := range validTests {
|
||||||
|
if ok := json.Valid([]byte(tt.data)); ok != tt.ok {
|
||||||
|
t.Errorf("Valid(%#q) = %v, want %v", tt.data, ok, tt.ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type example struct {
|
||||||
|
compact string
|
||||||
|
indent string
|
||||||
|
}
|
||||||
|
|
||||||
|
var examples = []example{
|
||||||
|
{`1`, `1`},
|
||||||
|
{`{}`, `{}`},
|
||||||
|
{`[]`, `[]`},
|
||||||
|
{`{"":2}`, "{\n\t\"\": 2\n}"},
|
||||||
|
{`[3]`, "[\n\t3\n]"},
|
||||||
|
{`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"},
|
||||||
|
{`{"x":1}`, "{\n\t\"x\": 1\n}"},
|
||||||
|
{ex1, ex1i},
|
||||||
|
{"{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"}, // See golang.org/issue/34070
|
||||||
|
}
|
||||||
|
|
||||||
|
var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]`
|
||||||
|
|
||||||
|
var ex1i = `[
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
"x",
|
||||||
|
1,
|
||||||
|
1.5,
|
||||||
|
0,
|
||||||
|
-5e+2
|
||||||
|
]`
|
||||||
|
|
||||||
|
func TestCompact(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, tt := range examples {
|
||||||
|
buf.Reset()
|
||||||
|
t.Log("src = ", tt.compact)
|
||||||
|
if err := json.Compact(&buf, []byte(tt.compact)); err != nil {
|
||||||
|
t.Errorf("Compact(%#q): %v", tt.compact, err)
|
||||||
|
} else if s := buf.String(); s != tt.compact {
|
||||||
|
t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
if err := json.Compact(&buf, []byte(tt.indent)); err != nil {
|
||||||
|
t.Errorf("Compact(%#q): %v", tt.indent, err)
|
||||||
|
continue
|
||||||
|
} else if s := buf.String(); s != tt.compact {
|
||||||
|
t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompactSeparators(t *testing.T) {
|
||||||
|
// U+2028 and U+2029 should be escaped inside strings.
|
||||||
|
// They should not appear outside strings.
|
||||||
|
tests := []struct {
|
||||||
|
in, compact string
|
||||||
|
}{
|
||||||
|
{"{\"\u2028\": 1}", "{\"\u2028\":1}"},
|
||||||
|
{"{\"\u2029\" :2}", "{\"\u2029\":2}"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := json.Compact(&buf, []byte(tt.in)); err != nil {
|
||||||
|
t.Errorf("Compact(%q): %v", tt.in, err)
|
||||||
|
} else if s := buf.String(); s != tt.compact {
|
||||||
|
t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndent(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, tt := range examples {
|
||||||
|
buf.Reset()
|
||||||
|
if err := json.Indent(&buf, []byte(tt.indent), "", "\t"); err != nil {
|
||||||
|
t.Errorf("Indent(%#q): %v", tt.indent, err)
|
||||||
|
} else if s := buf.String(); s != tt.indent {
|
||||||
|
t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
if err := json.Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
|
||||||
|
t.Errorf("Indent(%#q): %v", tt.compact, err)
|
||||||
|
continue
|
||||||
|
} else if s := buf.String(); s != tt.indent {
|
||||||
|
t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue