Merge pull request #18 from goccy/feature/support-all-apis

Add Compact/Indent/HTMLEscape/Valid
This commit is contained in:
Masaaki Goshima 2020-08-12 16:57:50 +09:00 committed by GitHub
commit 18e30e0106
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 438 additions and 76 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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++
} }
} }
} }

View File

@ -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 {
header.op = opSliceHeadIndent if root {
elemCode.op = opSliceElemIndent header.op = opRootSliceHeadIndent
elemCode.op = opRootSliceElemIndent
} else {
header.op = opSliceHeadIndent
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 {
header.op = opMapHeadIndent if root {
header.op = opRootMapHeadIndent
} else {
header.op = opMapHeadIndent
}
} else { } else {
header.op = opMapHeadLoadIndent header.op = opMapHeadLoadIndent
} }
key.op = opMapKeyIndent if root {
key.op = opRootMapKeyIndent
} else {
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
} }

View File

@ -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

View File

@ -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
View File

@ -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
}

118
json_test.go Normal file
View File

@ -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)
}
}
}