aboutsummaryrefslogtreecommitdiff
path: root/src/encoding
diff options
context:
space:
mode:
Diffstat (limited to 'src/encoding')
-rw-r--r--src/encoding/asn1/asn1.go12
-rw-r--r--src/encoding/asn1/asn1_test.go5
-rw-r--r--src/encoding/binary/binary.go77
-rw-r--r--src/encoding/binary/binary_test.go23
-rw-r--r--src/encoding/gob/decode.go13
-rw-r--r--src/encoding/gob/decoder.go9
-rw-r--r--src/encoding/gob/encode.go75
-rw-r--r--src/encoding/gob/encoder.go4
-rw-r--r--src/encoding/gob/encoder_test.go22
-rw-r--r--src/encoding/gob/type.go76
-rw-r--r--src/encoding/json/decode.go6
-rw-r--r--src/encoding/json/decode_test.go12
12 files changed, 213 insertions, 121 deletions
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index b06aec3e40..3aeb3dcc4b 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -640,15 +640,19 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
// when it sees a string, so if we see a different string type on the
// wire, we change the universal type to match.
if universalTag == tagPrintableString {
- switch t.tag {
- case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
- universalTag = t.tag
+ if params.tag == nil {
+ switch t.tag {
+ case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
+ universalTag = t.tag
+ }
+ } else if params.stringType != 0 {
+ universalTag = params.stringType
}
}
// Special case for time: UTCTime and GeneralizedTime both map to the
// Go type time.Time.
- if universalTag == tagUTCTime && t.tag == tagGeneralizedTime {
+ if universalTag == tagUTCTime && params.tag == nil && t.tag == tagGeneralizedTime {
universalTag = tagGeneralizedTime
}
diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go
index b553f78e0a..b94d59d369 100644
--- a/src/encoding/asn1/asn1_test.go
+++ b/src/encoding/asn1/asn1_test.go
@@ -392,6 +392,10 @@ type TestContextSpecificTags2 struct {
B int
}
+type TestContextSpecificTags3 struct {
+ S string `asn1:"tag:1,utf8"`
+}
+
type TestElementsAfterString struct {
S string
A, B int
@@ -420,6 +424,7 @@ var unmarshalTestData = []struct {
{[]byte{0x04, 0x04, 1, 2, 3, 4}, &RawValue{0, 4, false, []byte{1, 2, 3, 4}, []byte{4, 4, 1, 2, 3, 4}}},
{[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}},
{[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}},
+ {[]byte{0x30, 0x03, 0x81, 0x01, '@'}, &TestContextSpecificTags3{"@"}},
{[]byte{0x01, 0x01, 0x00}, newBool(false)},
{[]byte{0x01, 0x01, 0xff}, newBool(true)},
{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},
diff --git a/src/encoding/binary/binary.go b/src/encoding/binary/binary.go
index a5694876ac..466bf97c97 100644
--- a/src/encoding/binary/binary.go
+++ b/src/encoding/binary/binary.go
@@ -10,9 +10,10 @@
// type (int8, uint8, int16, float32, complex64, ...)
// or an array or struct containing only fixed-size values.
//
-// Varints are a method of encoding integers using one or more bytes;
-// numbers with smaller absolute value take a smaller number of bytes.
-// For a specification, see http://code.google.com/apis/protocolbuffers/docs/encoding.html.
+// The varint functions encode and decode single integer values using
+// a variable-length encoding; smaller values require fewer bytes.
+// For a specification, see
+// http://code.google.com/apis/protocolbuffers/docs/encoding.html.
//
// This package favors simplicity over efficiency. Clients that require
// high-performance serialization, especially for large data structures,
@@ -199,18 +200,17 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
}
// Fallback to reflect-based decoding.
- var v reflect.Value
- switch d := reflect.ValueOf(data); d.Kind() {
+ v := reflect.ValueOf(data)
+ size := -1
+ switch v.Kind() {
case reflect.Ptr:
- v = d.Elem()
+ v = v.Elem()
+ size = dataSize(v)
case reflect.Slice:
- v = d
- default:
- return errors.New("binary.Read: invalid type " + d.Type().String())
+ size = dataSize(v)
}
- size, err := dataSize(v)
- if err != nil {
- return errors.New("binary.Read: " + err.Error())
+ if size < 0 {
+ return errors.New("binary.Read: invalid type " + reflect.TypeOf(data).String())
}
d := &decoder{order: order, buf: make([]byte, size)}
if _, err := io.ReadFull(r, d.buf); err != nil {
@@ -323,68 +323,64 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error {
// Fallback to reflect-based encoding.
v := reflect.Indirect(reflect.ValueOf(data))
- size, err := dataSize(v)
- if err != nil {
- return errors.New("binary.Write: " + err.Error())
+ size := dataSize(v)
+ if size < 0 {
+ return errors.New("binary.Write: invalid type " + reflect.TypeOf(data).String())
}
buf := make([]byte, size)
e := &encoder{order: order, buf: buf}
e.value(v)
- _, err = w.Write(buf)
+ _, err := w.Write(buf)
return err
}
// Size returns how many bytes Write would generate to encode the value v, which
// must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
+// If v is neither of these, Size returns -1.
func Size(v interface{}) int {
- n, err := dataSize(reflect.Indirect(reflect.ValueOf(v)))
- if err != nil {
- return -1
- }
- return n
+ return dataSize(reflect.Indirect(reflect.ValueOf(v)))
}
// dataSize returns the number of bytes the actual data represented by v occupies in memory.
// For compound structures, it sums the sizes of the elements. Thus, for instance, for a slice
// it returns the length of the slice times the element size and does not count the memory
-// occupied by the header.
-func dataSize(v reflect.Value) (int, error) {
+// occupied by the header. If the type of v is not acceptable, dataSize returns -1.
+func dataSize(v reflect.Value) int {
if v.Kind() == reflect.Slice {
- elem, err := sizeof(v.Type().Elem())
- if err != nil {
- return 0, err
+ if s := sizeof(v.Type().Elem()); s >= 0 {
+ return s * v.Len()
}
- return v.Len() * elem, nil
+ return -1
}
return sizeof(v.Type())
}
-func sizeof(t reflect.Type) (int, error) {
+// sizeof returns the size >= 0 of variables for the given type or -1 if the type is not acceptable.
+func sizeof(t reflect.Type) int {
switch t.Kind() {
case reflect.Array:
- n, err := sizeof(t.Elem())
- if err != nil {
- return 0, err
+ if s := sizeof(t.Elem()); s >= 0 {
+ return s * t.Len()
}
- return t.Len() * n, nil
case reflect.Struct:
sum := 0
for i, n := 0, t.NumField(); i < n; i++ {
- s, err := sizeof(t.Field(i).Type)
- if err != nil {
- return 0, err
+ s := sizeof(t.Field(i).Type)
+ if s < 0 {
+ return -1
}
sum += s
}
- return sum, nil
+ return sum
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
- return int(t.Size()), nil
+ return int(t.Size())
}
- return 0, errors.New("invalid type " + t.String())
+
+ return -1
}
type coder struct {
@@ -594,12 +590,11 @@ func (e *encoder) value(v reflect.Value) {
}
func (d *decoder) skip(v reflect.Value) {
- n, _ := dataSize(v)
- d.buf = d.buf[n:]
+ d.buf = d.buf[dataSize(v):]
}
func (e *encoder) skip(v reflect.Value) {
- n, _ := dataSize(v)
+ n := dataSize(v)
for i := range e.buf[0:n] {
e.buf[i] = 0
}
diff --git a/src/encoding/binary/binary_test.go b/src/encoding/binary/binary_test.go
index c80c90383a..8ee595fa47 100644
--- a/src/encoding/binary/binary_test.go
+++ b/src/encoding/binary/binary_test.go
@@ -289,6 +289,26 @@ func TestUnexportedRead(t *testing.T) {
Read(&buf, LittleEndian, &u2)
}
+func TestReadErrorMsg(t *testing.T) {
+ var buf bytes.Buffer
+ read := func(data interface{}) {
+ err := Read(&buf, LittleEndian, data)
+ want := "binary.Read: invalid type " + reflect.TypeOf(data).String()
+ if err == nil {
+ t.Errorf("%T: got no error; want %q", data, want)
+ return
+ }
+ if got := err.Error(); got != want {
+ t.Errorf("%T: got %q; want %q", data, got, want)
+ }
+ }
+ read(0)
+ s := new(struct{})
+ read(&s)
+ p := &s
+ read(&p)
+}
+
type byteSliceReader struct {
remain []byte
}
@@ -315,8 +335,7 @@ func BenchmarkReadStruct(b *testing.B) {
bsr := &byteSliceReader{}
var buf bytes.Buffer
Write(&buf, BigEndian, &s)
- n, _ := dataSize(reflect.ValueOf(s))
- b.SetBytes(int64(n))
+ b.SetBytes(int64(dataSize(reflect.ValueOf(s))))
t := s
b.ResetTimer()
for i := 0; i < b.N; i++ {
diff --git a/src/encoding/gob/decode.go b/src/encoding/gob/decode.go
index 2367650c8b..6a9213fb3c 100644
--- a/src/encoding/gob/decode.go
+++ b/src/encoding/gob/decode.go
@@ -312,6 +312,9 @@ func decUint8Slice(i *decInstr, state *decoderState, value reflect.Value) {
if n > state.b.Len() {
errorf("%s data too long for buffer: %d", value.Type(), n)
}
+ if n > tooBig {
+ errorf("byte slice too big: %d", n)
+ }
if value.Cap() < n {
value.Set(reflect.MakeSlice(value.Type(), n, n))
} else {
@@ -533,14 +536,18 @@ func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) {
// Slices are encoded as an unsigned length followed by the elements.
func (dec *Decoder) decodeSlice(state *decoderState, value reflect.Value, elemOp decOp, ovfl error) {
u := state.decodeUint()
+ typ := value.Type()
+ size := uint64(typ.Elem().Size())
+ nBytes := u * size
n := int(u)
- if n < 0 || uint64(n) != u {
+ // Take care with overflow in this calculation.
+ if n < 0 || uint64(n) != u || nBytes > tooBig || (size > 0 && nBytes/size != u) {
// We don't check n against buffer length here because if it's a slice
// of interfaces, there will be buffer reloads.
- errorf("length of %s is negative (%d bytes)", value.Type(), u)
+ errorf("%s slice too big: %d elements of %d bytes", typ.Elem(), u, size)
}
if value.Cap() < n {
- value.Set(reflect.MakeSlice(value.Type(), n, n))
+ value.Set(reflect.MakeSlice(typ, n, n))
} else {
value.Set(value.Slice(0, n))
}
diff --git a/src/encoding/gob/decoder.go b/src/encoding/gob/decoder.go
index 3a769ec125..dcad7a0e48 100644
--- a/src/encoding/gob/decoder.go
+++ b/src/encoding/gob/decoder.go
@@ -13,6 +13,11 @@ import (
"sync"
)
+// tooBig provides a sanity check for sizes; used in several places.
+// Upper limit of 1GB, allowing room to grow a little without overflow.
+// TODO: make this adjustable?
+const tooBig = 1 << 30
+
// A Decoder manages the receipt of type and data information read from the
// remote side of a connection.
type Decoder struct {
@@ -75,9 +80,7 @@ func (dec *Decoder) recvMessage() bool {
dec.err = err
return false
}
- // Upper limit of 1GB, allowing room to grow a little without overflow.
- // TODO: We might want more control over this limit.
- if nbytes >= 1<<30 {
+ if nbytes >= tooBig {
dec.err = errBadCount
return false
}
diff --git a/src/encoding/gob/encode.go b/src/encoding/gob/encode.go
index 5d35db20e6..b7bf8b0022 100644
--- a/src/encoding/gob/encode.go
+++ b/src/encoding/gob/encode.go
@@ -470,7 +470,7 @@ var encOpTable = [...]encOp{
// encOpFor returns (a pointer to) the encoding op for the base type under rt and
// the indirection count to reach it.
-func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) {
+func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp, building map[*typeInfo]bool) (*encOp, int) {
ut := userType(rt)
// If the type implements GobEncoder, we handle it without further processing.
if ut.externalEnc != 0 {
@@ -498,7 +498,7 @@ func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int)
break
}
// Slices have a header; we decode it to find the underlying array.
- elemOp, elemIndir := encOpFor(t.Elem(), inProgress)
+ elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building)
op = func(i *encInstr, state *encoderState, slice reflect.Value) {
if !state.sendZero && slice.Len() == 0 {
return
@@ -508,14 +508,14 @@ func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int)
}
case reflect.Array:
// True arrays have size in the type.
- elemOp, elemIndir := encOpFor(t.Elem(), inProgress)
+ elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building)
op = func(i *encInstr, state *encoderState, array reflect.Value) {
state.update(i)
state.enc.encodeArray(state.b, array, *elemOp, elemIndir, array.Len())
}
case reflect.Map:
- keyOp, keyIndir := encOpFor(t.Key(), inProgress)
- elemOp, elemIndir := encOpFor(t.Elem(), inProgress)
+ keyOp, keyIndir := encOpFor(t.Key(), inProgress, building)
+ elemOp, elemIndir := encOpFor(t.Elem(), inProgress, building)
op = func(i *encInstr, state *encoderState, mv reflect.Value) {
// We send zero-length (but non-nil) maps because the
// receiver might want to use the map. (Maps don't use append.)
@@ -527,12 +527,13 @@ func encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int)
}
case reflect.Struct:
// Generate a closure that calls out to the engine for the nested type.
- getEncEngine(userType(typ))
+ getEncEngine(userType(typ), building)
info := mustGetTypeInfo(typ)
op = func(i *encInstr, state *encoderState, sv reflect.Value) {
state.update(i)
// indirect through info to delay evaluation for recursive structs
- state.enc.encodeStruct(state.b, info.encoder, sv)
+ enc := info.encoder.Load().(*encEngine)
+ state.enc.encodeStruct(state.b, enc, sv)
}
case reflect.Interface:
op = func(i *encInstr, state *encoderState, iv reflect.Value) {
@@ -579,7 +580,7 @@ func gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) {
}
// compileEnc returns the engine to compile the type.
-func compileEnc(ut *userTypeInfo) *encEngine {
+func compileEnc(ut *userTypeInfo, building map[*typeInfo]bool) *encEngine {
srt := ut.base
engine := new(encEngine)
seen := make(map[reflect.Type]*encOp)
@@ -593,7 +594,7 @@ func compileEnc(ut *userTypeInfo) *encEngine {
if !isSent(&f) {
continue
}
- op, indir := encOpFor(f.Type, seen)
+ op, indir := encOpFor(f.Type, seen, building)
engine.instr = append(engine.instr, encInstr{*op, wireFieldNum, f.Index, indir})
wireFieldNum++
}
@@ -603,49 +604,47 @@ func compileEnc(ut *userTypeInfo) *encEngine {
engine.instr = append(engine.instr, encInstr{encStructTerminator, 0, nil, 0})
} else {
engine.instr = make([]encInstr, 1)
- op, indir := encOpFor(rt, seen)
+ op, indir := encOpFor(rt, seen, building)
engine.instr[0] = encInstr{*op, singletonField, nil, indir}
}
return engine
}
// getEncEngine returns the engine to compile the type.
-// typeLock must be held (or we're in initialization and guaranteed single-threaded).
-func getEncEngine(ut *userTypeInfo) *encEngine {
- info, err1 := getTypeInfo(ut)
- if err1 != nil {
- error_(err1)
+func getEncEngine(ut *userTypeInfo, building map[*typeInfo]bool) *encEngine {
+ info, err := getTypeInfo(ut)
+ if err != nil {
+ error_(err)
}
- if info.encoder == nil {
- // Assign the encEngine now, so recursive types work correctly. But...
- info.encoder = new(encEngine)
- // ... if we fail to complete building the engine, don't cache the half-built machine.
- // Doing this here means we won't cache a type that is itself OK but
- // that contains a nested type that won't compile. The result is consistent
- // error behavior when Encode is called multiple times on the top-level type.
- ok := false
- defer func() {
- if !ok {
- info.encoder = nil
- }
- }()
- info.encoder = compileEnc(ut)
- ok = true
+ enc, ok := info.encoder.Load().(*encEngine)
+ if !ok {
+ enc = buildEncEngine(info, ut, building)
}
- return info.encoder
+ return enc
}
-// lockAndGetEncEngine is a function that locks and compiles.
-// This lets us hold the lock only while compiling, not when encoding.
-func lockAndGetEncEngine(ut *userTypeInfo) *encEngine {
- typeLock.Lock()
- defer typeLock.Unlock()
- return getEncEngine(ut)
+func buildEncEngine(info *typeInfo, ut *userTypeInfo, building map[*typeInfo]bool) *encEngine {
+ // Check for recursive types.
+ if building != nil && building[info] {
+ return nil
+ }
+ info.encInit.Lock()
+ defer info.encInit.Unlock()
+ enc, ok := info.encoder.Load().(*encEngine)
+ if !ok {
+ if building == nil {
+ building = make(map[*typeInfo]bool)
+ }
+ building[info] = true
+ enc = compileEnc(ut, building)
+ info.encoder.Store(enc)
+ }
+ return enc
}
func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInfo) {
defer catchError(&enc.err)
- engine := lockAndGetEncEngine(ut)
+ engine := getEncEngine(ut, nil)
indir := ut.indir
if ut.externalEnc != 0 {
indir = int(ut.encIndir)
diff --git a/src/encoding/gob/encoder.go b/src/encoding/gob/encoder.go
index a3301c3bd3..4b5dc16c79 100644
--- a/src/encoding/gob/encoder.go
+++ b/src/encoding/gob/encoder.go
@@ -88,9 +88,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp
if _, alreadySent := enc.sent[actual]; alreadySent {
return false
}
- typeLock.Lock()
info, err := getTypeInfo(ut)
- typeLock.Unlock()
if err != nil {
enc.setError(err)
return
@@ -191,9 +189,7 @@ func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *use
// a singleton basic type (int, []byte etc.) at top level. We don't
// need to send the type info but we do need to update enc.sent.
if !sent {
- typeLock.Lock()
info, err := getTypeInfo(ut)
- typeLock.Unlock()
if err != nil {
enc.setError(err)
return
diff --git a/src/encoding/gob/encoder_test.go b/src/encoding/gob/encoder_test.go
index 376df82f15..0ea4c0ec8e 100644
--- a/src/encoding/gob/encoder_test.go
+++ b/src/encoding/gob/encoder_test.go
@@ -932,3 +932,25 @@ func Test29ElementSlice(t *testing.T) {
return
}
}
+
+// Don't crash, just give error when allocating a huge slice.
+// Issue 8084.
+func TestErrorForHugeSlice(t *testing.T) {
+ // Encode an int slice.
+ buf := new(bytes.Buffer)
+ slice := []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+ err := NewEncoder(buf).Encode(slice)
+ if err != nil {
+ t.Fatal("encode:", err)
+ }
+ // Reach into the buffer and smash the count to make the encoded slice very long.
+ buf.Bytes()[buf.Len()-len(slice)-1] = 0xfa
+ // Decode and see error.
+ err = NewDecoder(buf).Decode(&slice)
+ if err == nil {
+ t.Fatal("decode: no error")
+ }
+ if !strings.Contains(err.Error(), "slice too big") {
+ t.Fatal("decode: expected slice too big error, got %s", err.Error())
+ }
+}
diff --git a/src/encoding/gob/type.go b/src/encoding/gob/type.go
index cad1452795..a49b71a867 100644
--- a/src/encoding/gob/type.go
+++ b/src/encoding/gob/type.go
@@ -11,6 +11,7 @@ import (
"os"
"reflect"
"sync"
+ "sync/atomic"
"unicode"
"unicode/utf8"
)
@@ -681,29 +682,51 @@ func (w *wireType) string() string {
type typeInfo struct {
id typeId
- encoder *encEngine
+ encInit sync.Mutex // protects creation of encoder
+ encoder atomic.Value // *encEngine
wire *wireType
}
-var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock
+// typeInfoMap is an atomic pointer to map[reflect.Type]*typeInfo.
+// It's updated copy-on-write. Readers just do an atomic load
+// to get the current version of the map. Writers make a full copy of
+// the map and atomically update the pointer to point to the new map.
+// Under heavy read contention, this is significantly faster than a map
+// protected by a mutex.
+var typeInfoMap atomic.Value
+
+func lookupTypeInfo(rt reflect.Type) *typeInfo {
+ m, _ := typeInfoMap.Load().(map[reflect.Type]*typeInfo)
+ return m[rt]
+}
-// typeLock must be held.
func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) {
rt := ut.base
if ut.externalEnc != 0 {
// We want the user type, not the base type.
rt = ut.user
}
- info, ok := typeInfoMap[rt]
- if ok {
+ if info := lookupTypeInfo(rt); info != nil {
return info, nil
}
- info = new(typeInfo)
+ return buildTypeInfo(ut, rt)
+}
+
+// buildTypeInfo constructs the type information for the type
+// and stores it in the type info map.
+func buildTypeInfo(ut *userTypeInfo, rt reflect.Type) (*typeInfo, error) {
+ typeLock.Lock()
+ defer typeLock.Unlock()
+
+ if info := lookupTypeInfo(rt); info != nil {
+ return info, nil
+ }
+
gt, err := getBaseType(rt.Name(), rt)
if err != nil {
return nil, err
}
- info.id = gt.id()
+ info := &typeInfo{id: gt.id()}
if ut.externalEnc != 0 {
userType, err := getType(rt.Name(), ut, rt)
@@ -719,25 +742,32 @@ func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) {
case xText:
info.wire = &wireType{TextMarshalerT: gt}
}
- typeInfoMap[ut.user] = info
- return info, nil
+ rt = ut.user
+ } else {
+ t := info.id.gobType()
+ switch typ := rt; typ.Kind() {
+ case reflect.Array:
+ info.wire = &wireType{ArrayT: t.(*arrayType)}
+ case reflect.Map:
+ info.wire = &wireType{MapT: t.(*mapType)}
+ case reflect.Slice:
+ // []byte == []uint8 is a special case handled separately
+ if typ.Elem().Kind() != reflect.Uint8 {
+ info.wire = &wireType{SliceT: t.(*sliceType)}
+ }
+ case reflect.Struct:
+ info.wire = &wireType{StructT: t.(*structType)}
+ }
}
- t := info.id.gobType()
- switch typ := rt; typ.Kind() {
- case reflect.Array:
- info.wire = &wireType{ArrayT: t.(*arrayType)}
- case reflect.Map:
- info.wire = &wireType{MapT: t.(*mapType)}
- case reflect.Slice:
- // []byte == []uint8 is a special case handled separately
- if typ.Elem().Kind() != reflect.Uint8 {
- info.wire = &wireType{SliceT: t.(*sliceType)}
- }
- case reflect.Struct:
- info.wire = &wireType{StructT: t.(*structType)}
+ // Create new map with old contents plus new entry.
+ newm := make(map[reflect.Type]*typeInfo)
+ m, _ := typeInfoMap.Load().(map[reflect.Type]*typeInfo)
+ for k, v := range m {
+ newm[k] = v
}
- typeInfoMap[rt] = info
+ newm[rt] = info
+ typeInfoMap.Store(newm)
return info, nil
}
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index af1c908ad7..67ec37388f 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -445,7 +445,7 @@ func (d *decodeState) array(v reflect.Value) {
}
// object consumes an object from d.data[d.off-1:], decoding into the value v.
-// the first byte of the object ('{') has been read already.
+// the first byte ('{') of the object has been read already.
func (d *decodeState) object(v reflect.Value) {
// Check for unmarshaler.
u, ut, pv := d.indirect(v, false)
@@ -478,7 +478,9 @@ func (d *decodeState) object(v reflect.Value) {
t := v.Type()
if t.Key().Kind() != reflect.String {
d.saveError(&UnmarshalTypeError{"object", v.Type()})
- break
+ d.off--
+ d.next() // skip over { } in input
+ return
}
if v.IsNil() {
v.Set(reflect.MakeMap(t))
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 238a87fd66..d95657d729 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -406,6 +406,13 @@ var unmarshalTests = []unmarshalTest{
ptr: new(string),
out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
},
+
+ // issue 8305
+ {
+ in: `{"2009-11-10T23:00:00Z": "hello world"}`,
+ ptr: &map[time.Time]string{},
+ err: &UnmarshalTypeError{"object", reflect.TypeOf(map[time.Time]string{})},
+ },
}
func TestMarshal(t *testing.T) {
@@ -514,6 +521,7 @@ func TestUnmarshal(t *testing.T) {
if tt.ptr == nil {
continue
}
+
// v = new(right-type)
v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
dec := NewDecoder(bytes.NewReader(in))
@@ -521,7 +529,9 @@ func TestUnmarshal(t *testing.T) {
dec.UseNumber()
}
if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) {
- t.Errorf("#%d: %v want %v", i, err, tt.err)
+ t.Errorf("#%d: %v, want %v", i, err, tt.err)
+ continue
+ } else if err != nil {
continue
}
if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {