diff options
Diffstat (limited to 'src/encoding/json/decode.go')
| -rw-r--r-- | src/encoding/json/decode.go | 40 |
1 files changed, 22 insertions, 18 deletions
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index b43484692e..6fa2ea4e0c 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -177,8 +177,7 @@ func (d *decodeState) unmarshal(v interface{}) error { d.scanWhile(scanSkipSpace) // We decode rv not rv.Elem because the Unmarshaler interface // test must be applied at the top level of the value. - err := d.value(rv) - if err != nil { + if err := d.value(rv); err != nil { return d.addErrorContext(err) } return d.savedError @@ -525,6 +524,7 @@ func (d *decodeState) array(v reflect.Value) error { return nil } v = pv + initialSliceCap := 0 // Check type of target. switch v.Kind() { @@ -541,8 +541,9 @@ func (d *decodeState) array(v reflect.Value) error { d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)}) d.skip() return nil - case reflect.Array, reflect.Slice: - break + case reflect.Slice: + initialSliceCap = v.Cap() + case reflect.Array: } i := 0 @@ -553,7 +554,6 @@ func (d *decodeState) array(v reflect.Value) error { break } - // Get element of array, growing if necessary. if v.Kind() == reflect.Slice { // Grow slice if necessary if i >= v.Cap() { @@ -569,19 +569,22 @@ func (d *decodeState) array(v reflect.Value) error { v.SetLen(i + 1) } } - + var into reflect.Value if i < v.Len() { - // Decode into element. - if err := d.value(v.Index(i)); err != nil { - return err - } - } else { - // Ran out of fixed array: skip. - if err := d.value(reflect.Value{}); err != nil { - return err + into = v.Index(i) + if i < initialSliceCap { + // Reusing an element from the slice's original + // backing array; zero it before decoding. + into.Set(reflect.Zero(v.Type().Elem())) } } i++ + // Note that we decode the value even if we ran past the end of + // the fixed array. In that case, we decode into an empty value + // and do nothing with it. + if err := d.value(into); err != nil { + return err + } // Next token must be , or ]. if d.opcode == scanSkipSpace { @@ -597,16 +600,17 @@ func (d *decodeState) array(v reflect.Value) error { if i < v.Len() { if v.Kind() == reflect.Array { - // Array. Zero the rest. - z := reflect.Zero(v.Type().Elem()) + // Zero the remaining elements. + zero := reflect.Zero(v.Type().Elem()) for ; i < v.Len(); i++ { - v.Index(i).Set(z) + v.Index(i).Set(zero) } } else { v.SetLen(i) } } - if i == 0 && v.Kind() == reflect.Slice { + if v.Kind() == reflect.Slice && v.IsNil() { + // Don't allow the resulting slice to be nil. v.Set(reflect.MakeSlice(v.Type(), 0, 0)) } return nil |
