mirror of https://github.com/goccy/go-json.git
Add Compact/Indent/HTMLEscape/Valid
This commit is contained in:
parent
1d52cdb354
commit
ee13701278
22
README.md
22
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
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))
|
||||
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()
|
||||
|
|
69
json.go
69
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 <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