Add support for array types

Arrays are useful to decode into to keep the allocations down
and thus the gc pressure low.

The code allows the json to contain truncated arrays without
raising an error, same as standard encoding/json. However excess
elements in the json is an error.
This commit is contained in:
Nicolas S. Dade
2016-09-22 01:51:36 -07:00
parent e978125a7e
commit bc1638cf78
5 changed files with 94 additions and 15 deletions
+41 -7
View File
@@ -123,6 +123,40 @@ func (g *Generator) genTypeDecoderNoCheck(t reflect.Type, out string, tags field
fmt.Fprintln(g.out, ws+"}")
}
case reflect.Array:
tmpVar := g.uniqueVarName()
iterVar := g.uniqueVarName()
elem := t.Elem()
if elem.Kind() == reflect.Uint8 {
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" copy("+out+"[:], in.Bytes())")
fmt.Fprintln(g.out, ws+"}")
} else {
length := t.Len()
fmt.Fprintln(g.out, ws+"if in.IsNull() {")
fmt.Fprintln(g.out, ws+" in.Skip()")
fmt.Fprintln(g.out, ws+"} else {")
fmt.Fprintln(g.out, ws+" in.Delim('[')")
fmt.Fprintln(g.out, ws+" "+iterVar+" := 0")
fmt.Fprintln(g.out, ws+" for !in.IsDelim(']') && "+iterVar+" < "+fmt.Sprint(length)+" {")
fmt.Fprintln(g.out, ws+" var "+tmpVar+" "+g.getType(elem))
g.genTypeDecoder(elem, tmpVar, tags, indent+2)
fmt.Fprintln(g.out, ws+" "+out+"["+iterVar+"] = "+tmpVar)
fmt.Fprintln(g.out, ws+" "+iterVar+"++")
fmt.Fprintln(g.out, ws+" in.WantComma()")
fmt.Fprintln(g.out, ws+" }")
fmt.Fprintln(g.out, ws+" in.Delim(']')")
fmt.Fprintln(g.out, ws+"}")
}
case reflect.Struct:
dec := g.getDecoderName(t)
g.addType(t)
@@ -287,16 +321,16 @@ func getStructFields(t reflect.Type) ([]reflect.StructField, error) {
func (g *Generator) genDecoder(t reflect.Type) error {
switch t.Kind() {
case reflect.Slice:
return g.genSliceDecoder(t)
case reflect.Slice, reflect.Array:
return g.genSliceArrayDecoder(t)
default:
return g.genStructDecoder(t)
}
}
func (g *Generator) genSliceDecoder(t reflect.Type) error {
if t.Kind() != reflect.Slice {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a slice type", t)
func (g *Generator) genSliceArrayDecoder(t reflect.Type) error {
if t.Kind() != reflect.Slice && t.Kind() != reflect.Array {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a slice or array type", t)
}
fname := g.getDecoderName(t)
@@ -378,8 +412,8 @@ func (g *Generator) genStructDecoder(t reflect.Type) error {
}
func (g *Generator) genStructUnmarshaller(t reflect.Type) error {
if t.Kind() != reflect.Struct && t.Kind() != reflect.Slice {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct/slice type", t)
if t.Kind() != reflect.Struct && t.Kind() != reflect.Slice && t.Kind() != reflect.Array {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct/slice/array type", t)
}
fname := g.getDecoderName(t)
+28 -8
View File
@@ -137,6 +137,26 @@ func (g *Generator) genTypeEncoderNoCheck(t reflect.Type, in string, tags fieldT
fmt.Fprintln(g.out, ws+"}")
}
case reflect.Array:
elem := t.Elem()
iVar := g.uniqueVarName()
vVar := g.uniqueVarName()
if t.Elem().Kind() == reflect.Uint8 {
fmt.Fprintln(g.out, ws+"out.Base64Bytes("+in+"[:])")
} else {
fmt.Fprintln(g.out, ws+"out.RawByte('[')")
fmt.Fprintln(g.out, ws+"for "+iVar+", "+vVar+" := range "+in+" {")
fmt.Fprintln(g.out, ws+" if "+iVar+" > 0 {")
fmt.Fprintln(g.out, ws+" out.RawByte(',')")
fmt.Fprintln(g.out, ws+" }")
g.genTypeEncoder(elem, vVar, tags, indent+1)
fmt.Fprintln(g.out, ws+"}")
fmt.Fprintln(g.out, ws+"out.RawByte(']')")
}
case reflect.Struct:
enc := g.getEncoderName(t)
g.addType(t)
@@ -195,7 +215,7 @@ func (g *Generator) notEmptyCheck(t reflect.Type, v string) string {
}
switch t.Kind() {
case reflect.Slice, reflect.Map:
case reflect.Slice, reflect.Map: // note: Array types don't have a useful empty value
return "len(" + v + ") != 0"
case reflect.Interface, reflect.Ptr:
return v + " != nil"
@@ -242,16 +262,16 @@ func (g *Generator) genStructFieldEncoder(t reflect.Type, f reflect.StructField)
func (g *Generator) genEncoder(t reflect.Type) error {
switch t.Kind() {
case reflect.Slice:
return g.genSliceEncoder(t)
case reflect.Slice, reflect.Array:
return g.genSliceArrayEncoder(t)
default:
return g.genStructEncoder(t)
}
}
func (g *Generator) genSliceEncoder(t reflect.Type) error {
if t.Kind() != reflect.Slice {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a slice type", t)
func (g *Generator) genSliceArrayEncoder(t reflect.Type) error {
if t.Kind() != reflect.Slice && t.Kind() != reflect.Array {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a slice or array type", t)
}
fname := g.getEncoderName(t)
@@ -296,8 +316,8 @@ func (g *Generator) genStructEncoder(t reflect.Type) error {
}
func (g *Generator) genStructMarshaller(t reflect.Type) error {
if t.Kind() != reflect.Struct && t.Kind() != reflect.Slice {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct/slice type", t)
if t.Kind() != reflect.Struct && t.Kind() != reflect.Slice && t.Kind() != reflect.Array {
return fmt.Errorf("cannot generate encoder/decoder for %v, not a struct/slice/array type", t)
}
fname := g.getEncoderName(t)
+3
View File
@@ -8,6 +8,7 @@ import (
"path"
"reflect"
"sort"
"strconv"
"strings"
"unicode"
)
@@ -233,6 +234,8 @@ func (g *Generator) getType(t reflect.Type) string {
return "*" + g.getType(t.Elem())
case reflect.Slice:
return "[]" + g.getType(t.Elem())
case reflect.Array:
return "[" + strconv.Itoa(t.Len()) + "]" + g.getType(t.Elem())
case reflect.Map:
return "map[" + g.getType(t.Key()) + "]" + g.getType(t.Elem())
}
+1
View File
@@ -31,6 +31,7 @@ var testCases = []struct {
{&unexportedStructValue, unexportedStructString},
{&excludedFieldValue, excludedFieldString},
{&sliceValue, sliceString},
{&arrayValue, arrayString},
{&mapsValue, mapsString},
{&deepNestValue, deepNestString},
{&IntsValue, IntsString},
+21
View File
@@ -445,6 +445,27 @@ var sliceString = `{` +
`"NilIntSlice":null` +
`}`
type Arrays struct {
ByteArray [3]byte
EmptyByteArray [0]byte
IntArray [5]int
EmptyIntArray [0]int
}
var arrayValue = Arrays{
ByteArray: [3]byte{'a', 'b', 'c'},
EmptyByteArray: [0]byte{},
IntArray: [5]int{1, 2, 3, 4, 5},
EmptyIntArray: [0]int{},
}
var arrayString = `{` +
`"ByteArray":"YWJj",` +
`"EmptyByteArray":"",` +
`"IntArray":[1,2,3,4,5],` +
`"EmptyIntArray":[]` +
`}`
type Str string
type Maps struct {