Add decoder for Array or Slice type

This commit is contained in:
Masaaki Goshima 2020-04-25 19:55:05 +09:00
parent 146949a833
commit 71d6f845e5
4 changed files with 193 additions and 19 deletions

View File

@ -1,6 +1,7 @@
package json package json
import ( import (
"bytes"
"io" "io"
"reflect" "reflect"
"strings" "strings"
@ -8,25 +9,26 @@ import (
"unsafe" "unsafe"
) )
// A Token holds a value of one of these types:
//
// Delim, for the four JSON delimiters [ ] { }
// bool, for JSON booleans
// float64, for JSON numbers
// Number, for JSON numbers
// string, for JSON string literals
// nil, for JSON null
//
type Token interface{} type Token interface{}
type Delim rune type Delim rune
const (
stateNone int = iota
stateLiteral
stateObject
stateArray
)
type decoder interface { type decoder interface {
decode(*context, uintptr) error decode(*context, uintptr) error
} }
type Decoder struct { type Decoder struct {
r io.Reader r io.Reader
state int buffered func() io.Reader
value []byte
} }
var ( var (
@ -43,14 +45,18 @@ func init() {
} }
} }
// NewDecoder returns a new decoder that reads from r.
//
// The decoder introduces its own buffering and may
// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder { func NewDecoder(r io.Reader) *Decoder {
return &Decoder{ return &Decoder{r: r}
r: r,
}
} }
// Buffered returns a reader of the data remaining in the Decoder's
// buffer. The reader is valid until the next call to Decode.
func (d *Decoder) Buffered() io.Reader { func (d *Decoder) Buffered() io.Reader {
return d.r return d.buffered()
} }
func (d *Decoder) decode(src []byte, header *interfaceHeader) error { func (d *Decoder) decode(src []byte, header *interfaceHeader) error {
@ -92,6 +98,11 @@ func (d *Decoder) decodeForUnmarshalNoEscape(src []byte, v interface{}) error {
return d.decode(src, header) return d.decode(src, header)
} }
// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
//
// See the documentation for Unmarshal for details about
// the conversion of JSON into a Go value.
func (d *Decoder) Decode(v interface{}) error { func (d *Decoder) Decode(v interface{}) error {
header := (*interfaceHeader)(unsafe.Pointer(&v)) header := (*interfaceHeader)(unsafe.Pointer(&v))
typ := header.typ typ := header.typ
@ -113,6 +124,9 @@ func (d *Decoder) Decode(v interface{}) error {
ptr := uintptr(header.ptr) ptr := uintptr(header.ptr)
ctx := ctxPool.Get().(*context) ctx := ctxPool.Get().(*context)
defer ctxPool.Put(ctx) defer ctxPool.Put(ctx)
d.buffered = func() io.Reader {
return bytes.NewReader(ctx.buf[ctx.cursor:])
}
for { for {
buf := make([]byte, 1024) buf := make([]byte, 1024)
n, err := d.r.Read(buf) n, err := d.r.Read(buf)
@ -136,6 +150,10 @@ func (d *Decoder) compile(typ *rtype) (decoder, error) {
return d.compilePtr(typ) return d.compilePtr(typ)
case reflect.Struct: case reflect.Struct:
return d.compileStruct(typ) return d.compileStruct(typ)
case reflect.Slice:
return d.compileSlice(typ)
case reflect.Array:
return d.compileArray(typ)
case reflect.Int: case reflect.Int:
return d.compileInt() return d.compileInt()
case reflect.Int8: case reflect.Int8:
@ -256,6 +274,24 @@ func (d *Decoder) compileBool() (decoder, error) {
return newBoolDecoder(), nil return newBoolDecoder(), nil
} }
func (d *Decoder) compileSlice(typ *rtype) (decoder, error) {
elem := typ.Elem()
decoder, err := d.compile(elem)
if err != nil {
return nil, err
}
return newSliceDecoder(decoder, elem, elem.Size()), nil
}
func (d *Decoder) compileArray(typ *rtype) (decoder, error) {
elem := typ.Elem()
decoder, err := d.compile(elem)
if err != nil {
return nil, err
}
return newArrayDecoder(decoder, elem, typ.Len()), nil
}
func (d *Decoder) getTag(field reflect.StructField) string { func (d *Decoder) getTag(field reflect.StructField) string {
return field.Tag.Get("json") return field.Tag.Get("json")
} }
@ -300,6 +336,9 @@ func (d *Decoder) compileStruct(typ *rtype) (decoder, error) {
return newStructDecoder(fieldMap), nil return newStructDecoder(fieldMap), nil
} }
// DisallowUnknownFields causes the Decoder to return an error when the destination
// is a struct and the input contains object keys which do not match any
// non-ignored, exported fields in the destination.
func (d *Decoder) DisallowUnknownFields() { func (d *Decoder) DisallowUnknownFields() {
} }
@ -316,6 +355,8 @@ func (d *Decoder) Token() (Token, error) {
return nil, nil return nil, nil
} }
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64.
func (d *Decoder) UseNumber() { func (d *Decoder) UseNumber() {
} }

52
decode_array.go Normal file
View File

@ -0,0 +1,52 @@
package json
import (
"errors"
)
type arrayDecoder struct {
elemType *rtype
size uintptr
valueDecoder decoder
alen int
}
func newArrayDecoder(dec decoder, elemType *rtype, alen int) *arrayDecoder {
return &arrayDecoder{
valueDecoder: dec,
elemType: elemType,
size: elemType.Size(),
alen: alen,
}
}
func (d *arrayDecoder) decode(ctx *context, p uintptr) error {
buf := ctx.buf
buflen := ctx.buflen
cursor := ctx.cursor
for ; cursor < buflen; cursor++ {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
continue
case '[':
idx := 0
for {
ctx.cursor = cursor + 1
if err := d.valueDecoder.decode(ctx, p+uintptr(idx)*d.size); err != nil {
return err
}
cursor = ctx.skipWhiteSpace()
switch buf[cursor] {
case ']':
return nil
case ',':
idx++
continue
default:
return errors.New("syntax error array")
}
}
}
}
return errors.New("unexpected error array")
}

72
decode_slice.go Normal file
View File

@ -0,0 +1,72 @@
package json
import (
"errors"
"reflect"
"unsafe"
)
type sliceDecoder struct {
elemType *rtype
valueDecoder decoder
size uintptr
}
func newSliceDecoder(dec decoder, elemType *rtype, size uintptr) *sliceDecoder {
return &sliceDecoder{
valueDecoder: dec,
elemType: elemType,
size: size,
}
}
//go:linkname copySlice reflect.typedslicecopy
func copySlice(elemType *rtype, dst, src reflect.SliceHeader) int
//go:linkname newArray reflect.unsafe_NewArray
func newArray(*rtype, int) unsafe.Pointer
func (d *sliceDecoder) decode(ctx *context, p uintptr) error {
buf := ctx.buf
buflen := ctx.buflen
cursor := ctx.cursor
for ; cursor < buflen; cursor++ {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
continue
case '[':
idx := 0
cap := 2
data := uintptr(newArray(d.elemType, cap))
for {
ctx.cursor = cursor + 1
if cap <= idx {
src := reflect.SliceHeader{Data: data, Len: idx, Cap: cap}
cap *= 2
data = uintptr(newArray(d.elemType, cap))
dst := reflect.SliceHeader{Data: data, Len: idx, Cap: cap}
copySlice(d.elemType, dst, src)
}
if err := d.valueDecoder.decode(ctx, data+uintptr(idx)*d.size); err != nil {
return err
}
cursor = ctx.skipWhiteSpace()
switch buf[cursor] {
case ']':
*(*reflect.SliceHeader)(unsafe.Pointer(p)) = reflect.SliceHeader{
Data: data,
Len: idx + 1,
Cap: cap,
}
return nil
case ',':
idx++
continue
default:
return errors.New("syntax error slice")
}
}
}
}
return errors.New("unexpected error slice")
}

View File

@ -1,6 +1,7 @@
package json_test package json_test
import ( import (
"fmt"
"testing" "testing"
"github.com/goccy/go-json" "github.com/goccy/go-json"
@ -75,17 +76,25 @@ func Test_Decoder(t *testing.T) {
assertEq(t, "string", "hello", v) assertEq(t, "string", "hello", v)
}) })
t.Run("float32", func(t *testing.T) { t.Run("float32", func(t *testing.T) {
var v struct { var v float32
F float32 assertErr(t, json.Unmarshal([]byte(`3.14`), &v))
} assertEq(t, "float32", float32(3.14), v)
assertErr(t, json.Unmarshal([]byte(`{"f": 3.14}`), &v))
assertEq(t, "float32", float32(3.14), v.F)
}) })
t.Run("float64", func(t *testing.T) { t.Run("float64", func(t *testing.T) {
var v float64 var v float64
assertErr(t, json.Unmarshal([]byte(`3.14`), &v)) assertErr(t, json.Unmarshal([]byte(`3.14`), &v))
assertEq(t, "float64", float64(3.14), v) assertEq(t, "float64", float64(3.14), v)
}) })
t.Run("slice", func(t *testing.T) {
var v []int
assertErr(t, json.Unmarshal([]byte(` [ 1 , 2 , 3 , 4 ] `), &v))
assertEq(t, "slice", fmt.Sprint([]int{1, 2, 3, 4}), fmt.Sprint(v))
})
t.Run("array", func(t *testing.T) {
var v [4]int
assertErr(t, json.Unmarshal([]byte(` [ 1 , 2 , 3 , 4 ] `), &v))
assertEq(t, "array", fmt.Sprint([4]int{1, 2, 3, 4}), fmt.Sprint(v))
})
t.Run("struct", func(t *testing.T) { t.Run("struct", func(t *testing.T) {
type T struct { type T struct {
AA int `json:"aa"` AA int `json:"aa"`