Merge pull request #14 from goccy/feature/support-json-number

Support json.Number
This commit is contained in:
Masaaki Goshima 2020-08-11 18:07:10 +09:00 committed by GitHub
commit 0943ec9fb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 16 deletions

View File

@ -258,5 +258,5 @@ func (d *Decoder) InputOffset() int64 {
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a // UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64. // Number instead of as a float64.
func (d *Decoder) UseNumber() { func (d *Decoder) UseNumber() {
d.s.useNumber = true
} }

View File

@ -11,7 +11,20 @@ type interfaceDecoder struct {
} }
func newInterfaceDecoder(typ *rtype) *interfaceDecoder { func newInterfaceDecoder(typ *rtype) *interfaceDecoder {
return &interfaceDecoder{typ: typ} return &interfaceDecoder{
typ: typ,
}
}
func (d *interfaceDecoder) numDecoder(s *stream) decoder {
if s.useNumber {
return newNumberDecoder(func(p uintptr, v Number) {
*(*interface{})(unsafe.Pointer(p)) = v
})
}
return newFloatDecoder(func(p uintptr, v float64) {
*(*interface{})(unsafe.Pointer(p)) = v
})
} }
var ( var (
@ -28,7 +41,11 @@ func (d *interfaceDecoder) decodeStream(s *stream, p uintptr) error {
var v map[interface{}]interface{} var v map[interface{}]interface{}
ptr := unsafe.Pointer(&v) ptr := unsafe.Pointer(&v)
d.dummy = ptr d.dummy = ptr
dec := newMapDecoder(interfaceMapType, newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ)) dec := newMapDecoder(
interfaceMapType,
newInterfaceDecoder(d.typ),
newInterfaceDecoder(d.typ),
)
if err := dec.decodeStream(s, uintptr(ptr)); err != nil { if err := dec.decodeStream(s, uintptr(ptr)); err != nil {
return err return err
} }
@ -38,16 +55,18 @@ func (d *interfaceDecoder) decodeStream(s *stream, p uintptr) error {
var v []interface{} var v []interface{}
ptr := unsafe.Pointer(&v) ptr := unsafe.Pointer(&v)
d.dummy = ptr // escape ptr d.dummy = ptr // escape ptr
dec := newSliceDecoder(newInterfaceDecoder(d.typ), d.typ, d.typ.Size()) dec := newSliceDecoder(
newInterfaceDecoder(d.typ),
d.typ,
d.typ.Size(),
)
if err := dec.decodeStream(s, uintptr(ptr)); err != nil { if err := dec.decodeStream(s, uintptr(ptr)); err != nil {
return err return err
} }
*(*interface{})(unsafe.Pointer(p)) = v *(*interface{})(unsafe.Pointer(p)) = v
return nil return nil
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return newFloatDecoder(func(p uintptr, v float64) { return d.numDecoder(s).decodeStream(s, p)
*(*interface{})(unsafe.Pointer(p)) = v
}).decodeStream(s, p)
case '"': case '"':
s.cursor++ s.cursor++
start := s.cursor start := s.cursor
@ -104,7 +123,11 @@ func (d *interfaceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, e
var v map[interface{}]interface{} var v map[interface{}]interface{}
ptr := unsafe.Pointer(&v) ptr := unsafe.Pointer(&v)
d.dummy = ptr d.dummy = ptr
dec := newMapDecoder(interfaceMapType, newInterfaceDecoder(d.typ), newInterfaceDecoder(d.typ)) dec := newMapDecoder(
interfaceMapType,
newInterfaceDecoder(d.typ),
newInterfaceDecoder(d.typ),
)
cursor, err := dec.decode(buf, cursor, uintptr(ptr)) cursor, err := dec.decode(buf, cursor, uintptr(ptr))
if err != nil { if err != nil {
return 0, err return 0, err
@ -115,7 +138,11 @@ func (d *interfaceDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, e
var v []interface{} var v []interface{}
ptr := unsafe.Pointer(&v) ptr := unsafe.Pointer(&v)
d.dummy = ptr // escape ptr d.dummy = ptr // escape ptr
dec := newSliceDecoder(newInterfaceDecoder(d.typ), d.typ, d.typ.Size()) dec := newSliceDecoder(
newInterfaceDecoder(d.typ),
d.typ,
d.typ.Size(),
)
cursor, err := dec.decode(buf, cursor, uintptr(ptr)) cursor, err := dec.decode(buf, cursor, uintptr(ptr))
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -8,6 +8,7 @@ type mapDecoder struct {
mapType *rtype mapType *rtype
keyDecoder decoder keyDecoder decoder
valueDecoder decoder valueDecoder decoder
dummy *interfaceHeader
} }
func newMapDecoder(mapType *rtype, keyDec decoder, valueDec decoder) *mapDecoder { func newMapDecoder(mapType *rtype, keyDec decoder, valueDec decoder) *mapDecoder {
@ -37,11 +38,13 @@ func (d *mapDecoder) setValue(buf []byte, cursor int64, key interface{}) (int64,
func (d *mapDecoder) setKeyStream(s *stream, key interface{}) error { func (d *mapDecoder) setKeyStream(s *stream, key interface{}) error {
header := (*interfaceHeader)(unsafe.Pointer(&key)) header := (*interfaceHeader)(unsafe.Pointer(&key))
d.dummy = header
return d.keyDecoder.decodeStream(s, uintptr(header.ptr)) return d.keyDecoder.decodeStream(s, uintptr(header.ptr))
} }
func (d *mapDecoder) setValueStream(s *stream, key interface{}) error { func (d *mapDecoder) setValueStream(s *stream, key interface{}) error {
header := (*interfaceHeader)(unsafe.Pointer(&key)) header := (*interfaceHeader)(unsafe.Pointer(&key))
d.dummy = header
return d.valueDecoder.decodeStream(s, uintptr(header.ptr)) return d.valueDecoder.decodeStream(s, uintptr(header.ptr))
} }

38
decode_number.go Normal file
View File

@ -0,0 +1,38 @@
package json
import (
"unsafe"
)
type numberDecoder struct {
*floatDecoder
op func(uintptr, Number)
}
func newNumberDecoder(op func(uintptr, Number)) *numberDecoder {
return &numberDecoder{
floatDecoder: newFloatDecoder(nil),
op: op,
}
}
func (d *numberDecoder) decodeStream(s *stream, p uintptr) error {
bytes, err := d.floatDecoder.decodeStreamByte(s)
if err != nil {
return err
}
str := *(*string)(unsafe.Pointer(&bytes))
d.op(p, Number(str))
return nil
}
func (d *numberDecoder) decode(buf []byte, cursor int64, p uintptr) (int64, error) {
bytes, c, err := d.floatDecoder.decodeByte(buf, cursor)
if err != nil {
return 0, err
}
cursor = c
s := *(*string)(unsafe.Pointer(&bytes))
d.op(p, Number(s))
return cursor, nil
}

View File

@ -10,12 +10,13 @@ const (
) )
type stream struct { type stream struct {
buf []byte buf []byte
length int64 length int64
r io.Reader r io.Reader
offset int64 offset int64
cursor int64 cursor int64
allRead bool allRead bool
useNumber bool
} }
func (s *stream) buffered() io.Reader { func (s *stream) buffered() io.Reader {

View File

@ -217,6 +217,14 @@ func Test_Decoder(t *testing.T) {
}) })
} }
func Test_Decoder_UseNumber(t *testing.T) {
dec := json.NewDecoder(strings.NewReader(`{"a": 3.14}`))
dec.UseNumber()
var v map[string]interface{}
assertErr(t, dec.Decode(&v))
assertEq(t, "json.Number", "json.Number", fmt.Sprintf("%T", v["a"]))
}
type unmarshalJSON struct { type unmarshalJSON struct {
v int v int
} }

21
json.go
View File

@ -1,6 +1,9 @@
package json package json
import "bytes" import (
"bytes"
"strconv"
)
// Marshaler is the interface implemented by types that // Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON. // can marshal themselves into valid JSON.
@ -275,3 +278,19 @@ func UnmarshalNoEscape(data []byte, v interface{}) error {
// nil, for JSON null // nil, for JSON null
// //
type Token interface{} type Token interface{}
// A Number represents a JSON number literal.
type Number string
// String returns the literal text of the number.
func (n Number) String() string { return string(n) }
// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
return strconv.ParseFloat(string(n), 64)
}
// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}