Преглед на файлове

codec: initial string interning support

This enables support for decoding a structure
containing a consistent set of mapping string keys.

To use, user must configure InternString option.

Handles which "could" use it, should use it. For example,
JsonHandle currently uses it for map keys if InternString option is enabled.
Ugorji Nwoke преди 10 години
родител
ревизия
4373325df6
променени са 4 файла, в които са добавени 81 реда и са изтрити 39 реда
  1. 39 3
      codec/decode.go
  2. 32 32
      codec/fast-path.generated.go
  3. 2 2
      codec/fast-path.go.tmpl
  4. 8 2
      codec/json.go

+ 39 - 3
codec/decode.go

@@ -139,8 +139,18 @@ type DecodeOptions struct {
 	// that formerly contained a number.
 	// that formerly contained a number.
 	//
 	//
 	// If true, we will decode into a new "blank" value, and set that in the interface.
 	// If true, we will decode into a new "blank" value, and set that in the interface.
-	// If true, we will decode into whatever is contained in the interface.
+	// If false, we will decode into whatever is contained in the interface.
 	InterfaceReset bool
 	InterfaceReset bool
+
+	// InternString controls interning of strings during decoding.
+	//
+	// Some handles, e.g. json, typically will read map keys as strings.
+	// If the set of keys are finite, it may help reduce allocation to
+	// look them up from a map (than to allocate them afresh).
+	//
+	// Note: Handles will be smart when using the intern functionality.
+	// So everything will not be interned.
+	InternString bool
 }
 }
 
 
 // ------------------------------------
 // ------------------------------------
@@ -953,7 +963,7 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
 			if ktypeId == intfTypId {
 			if ktypeId == intfTypId {
 				rvk = rvk.Elem()
 				rvk = rvk.Elem()
 				if rvk.Type() == uint8SliceTyp {
 				if rvk.Type() == uint8SliceTyp {
-					rvk = reflect.ValueOf(string(rvk.Bytes()))
+					rvk = reflect.ValueOf(d.string(rvk.Bytes()))
 				}
 				}
 			}
 			}
 			if mapGet {
 			if mapGet {
@@ -976,7 +986,7 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
 			if ktypeId == intfTypId {
 			if ktypeId == intfTypId {
 				rvk = rvk.Elem()
 				rvk = rvk.Elem()
 				if rvk.Type() == uint8SliceTyp {
 				if rvk.Type() == uint8SliceTyp {
-					rvk = reflect.ValueOf(string(rvk.Bytes()))
+					rvk = reflect.ValueOf(d.string(rvk.Bytes()))
 				}
 				}
 			}
 			}
 			if mapGet {
 			if mapGet {
@@ -1020,6 +1030,8 @@ type Decoder struct {
 
 
 	ri ioDecReader
 	ri ioDecReader
 	f  map[uintptr]*decFn
 	f  map[uintptr]*decFn
+	is map[string]string // used for interning strings
+
 	// _  uintptr // for alignment purposes, so next one starts from a cache line
 	// _  uintptr // for alignment purposes, so next one starts from a cache line
 
 
 	b [scratchByteArrayLen]byte
 	b [scratchByteArrayLen]byte
@@ -1040,6 +1052,9 @@ func NewDecoder(r io.Reader, h Handle) (d *Decoder) {
 		d.ri.br = &d.ri.bs
 		d.ri.br = &d.ri.bs
 	}
 	}
 	d.r = &d.ri
 	d.r = &d.ri
+	if d.h.InternString {
+		d.is = make(map[string]string, 32)
+	}
 	_, d.js = h.(*JsonHandle)
 	_, d.js = h.(*JsonHandle)
 	d.d = h.newDecDriver(d)
 	d.d = h.newDecDriver(d)
 	return
 	return
@@ -1053,6 +1068,9 @@ func NewDecoderBytes(in []byte, h Handle) (d *Decoder) {
 	d.rb.b = in
 	d.rb.b = in
 	d.rb.a = len(in)
 	d.rb.a = len(in)
 	d.r = &d.rb
 	d.r = &d.rb
+	if d.h.InternString {
+		d.is = make(map[string]string, 32)
+	}
 	_, d.js = h.(*JsonHandle)
 	_, d.js = h.(*JsonHandle)
 	d.d = h.newDecDriver(d)
 	d.d = h.newDecDriver(d)
 	// d.d = h.newDecDriver(decReaderT{true, &d.rb, &d.ri})
 	// d.d = h.newDecDriver(decReaderT{true, &d.rb, &d.ri})
@@ -1535,6 +1553,24 @@ func (d *Decoder) errorf(format string, params ...interface{}) {
 	panic(err)
 	panic(err)
 }
 }
 
 
+func (d *Decoder) string(v []byte) (s string) {
+	if d.is != nil {
+		s, ok := d.is[string(v)] // no allocation here.
+		if !ok {
+			s = string(v)
+			d.is[s] = s
+		}
+		return s
+	}
+	return string(v) // don't return stringView, as we need a real string here.
+}
+
+func (d *Decoder) intern(s string) {
+	if d.is != nil {
+		d.is[s] = s
+	}
+}
+
 // --------------------------------------------------
 // --------------------------------------------------
 
 
 // decSliceHelper assists when decoding into a slice, from a map or an array in the stream.
 // decSliceHelper assists when decoding into a slice, from a map or an array in the stream.

+ 32 - 32
codec/fast-path.generated.go

@@ -15329,7 +15329,7 @@ func (_ fastpathT) DecMapIntfIntfV(v map[interface{}]interface{}, checkNil bool,
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			if mapGet {
 			if mapGet {
 				mv = v[mk]
 				mv = v[mk]
@@ -15346,7 +15346,7 @@ func (_ fastpathT) DecMapIntfIntfV(v map[interface{}]interface{}, checkNil bool,
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			if mapGet {
 			if mapGet {
 				mv = v[mk]
 				mv = v[mk]
@@ -15406,7 +15406,7 @@ func (_ fastpathT) DecMapIntfStringV(v map[interface{}]string, checkNil bool, ca
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeString()
 			mv = dd.DecodeString()
 			if v != nil {
 			if v != nil {
@@ -15418,7 +15418,7 @@ func (_ fastpathT) DecMapIntfStringV(v map[interface{}]string, checkNil bool, ca
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeString()
 			mv = dd.DecodeString()
 			if v != nil {
 			if v != nil {
@@ -15473,7 +15473,7 @@ func (_ fastpathT) DecMapIntfUintV(v map[interface{}]uint, checkNil bool, canCha
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uint(dd.DecodeUint(uintBitsize))
 			mv = uint(dd.DecodeUint(uintBitsize))
 			if v != nil {
 			if v != nil {
@@ -15485,7 +15485,7 @@ func (_ fastpathT) DecMapIntfUintV(v map[interface{}]uint, checkNil bool, canCha
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uint(dd.DecodeUint(uintBitsize))
 			mv = uint(dd.DecodeUint(uintBitsize))
 			if v != nil {
 			if v != nil {
@@ -15540,7 +15540,7 @@ func (_ fastpathT) DecMapIntfUint8V(v map[interface{}]uint8, checkNil bool, canC
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uint8(dd.DecodeUint(8))
 			mv = uint8(dd.DecodeUint(8))
 			if v != nil {
 			if v != nil {
@@ -15552,7 +15552,7 @@ func (_ fastpathT) DecMapIntfUint8V(v map[interface{}]uint8, checkNil bool, canC
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uint8(dd.DecodeUint(8))
 			mv = uint8(dd.DecodeUint(8))
 			if v != nil {
 			if v != nil {
@@ -15607,7 +15607,7 @@ func (_ fastpathT) DecMapIntfUint16V(v map[interface{}]uint16, checkNil bool, ca
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uint16(dd.DecodeUint(16))
 			mv = uint16(dd.DecodeUint(16))
 			if v != nil {
 			if v != nil {
@@ -15619,7 +15619,7 @@ func (_ fastpathT) DecMapIntfUint16V(v map[interface{}]uint16, checkNil bool, ca
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uint16(dd.DecodeUint(16))
 			mv = uint16(dd.DecodeUint(16))
 			if v != nil {
 			if v != nil {
@@ -15674,7 +15674,7 @@ func (_ fastpathT) DecMapIntfUint32V(v map[interface{}]uint32, checkNil bool, ca
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uint32(dd.DecodeUint(32))
 			mv = uint32(dd.DecodeUint(32))
 			if v != nil {
 			if v != nil {
@@ -15686,7 +15686,7 @@ func (_ fastpathT) DecMapIntfUint32V(v map[interface{}]uint32, checkNil bool, ca
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uint32(dd.DecodeUint(32))
 			mv = uint32(dd.DecodeUint(32))
 			if v != nil {
 			if v != nil {
@@ -15741,7 +15741,7 @@ func (_ fastpathT) DecMapIntfUint64V(v map[interface{}]uint64, checkNil bool, ca
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeUint(64)
 			mv = dd.DecodeUint(64)
 			if v != nil {
 			if v != nil {
@@ -15753,7 +15753,7 @@ func (_ fastpathT) DecMapIntfUint64V(v map[interface{}]uint64, checkNil bool, ca
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeUint(64)
 			mv = dd.DecodeUint(64)
 			if v != nil {
 			if v != nil {
@@ -15808,7 +15808,7 @@ func (_ fastpathT) DecMapIntfUintptrV(v map[interface{}]uintptr, checkNil bool,
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uintptr(dd.DecodeUint(uintBitsize))
 			mv = uintptr(dd.DecodeUint(uintBitsize))
 			if v != nil {
 			if v != nil {
@@ -15820,7 +15820,7 @@ func (_ fastpathT) DecMapIntfUintptrV(v map[interface{}]uintptr, checkNil bool,
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = uintptr(dd.DecodeUint(uintBitsize))
 			mv = uintptr(dd.DecodeUint(uintBitsize))
 			if v != nil {
 			if v != nil {
@@ -15875,7 +15875,7 @@ func (_ fastpathT) DecMapIntfIntV(v map[interface{}]int, checkNil bool, canChang
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = int(dd.DecodeInt(intBitsize))
 			mv = int(dd.DecodeInt(intBitsize))
 			if v != nil {
 			if v != nil {
@@ -15887,7 +15887,7 @@ func (_ fastpathT) DecMapIntfIntV(v map[interface{}]int, checkNil bool, canChang
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = int(dd.DecodeInt(intBitsize))
 			mv = int(dd.DecodeInt(intBitsize))
 			if v != nil {
 			if v != nil {
@@ -15942,7 +15942,7 @@ func (_ fastpathT) DecMapIntfInt8V(v map[interface{}]int8, checkNil bool, canCha
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = int8(dd.DecodeInt(8))
 			mv = int8(dd.DecodeInt(8))
 			if v != nil {
 			if v != nil {
@@ -15954,7 +15954,7 @@ func (_ fastpathT) DecMapIntfInt8V(v map[interface{}]int8, checkNil bool, canCha
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = int8(dd.DecodeInt(8))
 			mv = int8(dd.DecodeInt(8))
 			if v != nil {
 			if v != nil {
@@ -16009,7 +16009,7 @@ func (_ fastpathT) DecMapIntfInt16V(v map[interface{}]int16, checkNil bool, canC
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = int16(dd.DecodeInt(16))
 			mv = int16(dd.DecodeInt(16))
 			if v != nil {
 			if v != nil {
@@ -16021,7 +16021,7 @@ func (_ fastpathT) DecMapIntfInt16V(v map[interface{}]int16, checkNil bool, canC
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = int16(dd.DecodeInt(16))
 			mv = int16(dd.DecodeInt(16))
 			if v != nil {
 			if v != nil {
@@ -16076,7 +16076,7 @@ func (_ fastpathT) DecMapIntfInt32V(v map[interface{}]int32, checkNil bool, canC
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = int32(dd.DecodeInt(32))
 			mv = int32(dd.DecodeInt(32))
 			if v != nil {
 			if v != nil {
@@ -16088,7 +16088,7 @@ func (_ fastpathT) DecMapIntfInt32V(v map[interface{}]int32, checkNil bool, canC
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = int32(dd.DecodeInt(32))
 			mv = int32(dd.DecodeInt(32))
 			if v != nil {
 			if v != nil {
@@ -16143,7 +16143,7 @@ func (_ fastpathT) DecMapIntfInt64V(v map[interface{}]int64, checkNil bool, canC
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeInt(64)
 			mv = dd.DecodeInt(64)
 			if v != nil {
 			if v != nil {
@@ -16155,7 +16155,7 @@ func (_ fastpathT) DecMapIntfInt64V(v map[interface{}]int64, checkNil bool, canC
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeInt(64)
 			mv = dd.DecodeInt(64)
 			if v != nil {
 			if v != nil {
@@ -16210,7 +16210,7 @@ func (_ fastpathT) DecMapIntfFloat32V(v map[interface{}]float32, checkNil bool,
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = float32(dd.DecodeFloat(true))
 			mv = float32(dd.DecodeFloat(true))
 			if v != nil {
 			if v != nil {
@@ -16222,7 +16222,7 @@ func (_ fastpathT) DecMapIntfFloat32V(v map[interface{}]float32, checkNil bool,
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = float32(dd.DecodeFloat(true))
 			mv = float32(dd.DecodeFloat(true))
 			if v != nil {
 			if v != nil {
@@ -16277,7 +16277,7 @@ func (_ fastpathT) DecMapIntfFloat64V(v map[interface{}]float64, checkNil bool,
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeFloat(false)
 			mv = dd.DecodeFloat(false)
 			if v != nil {
 			if v != nil {
@@ -16289,7 +16289,7 @@ func (_ fastpathT) DecMapIntfFloat64V(v map[interface{}]float64, checkNil bool,
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeFloat(false)
 			mv = dd.DecodeFloat(false)
 			if v != nil {
 			if v != nil {
@@ -16344,7 +16344,7 @@ func (_ fastpathT) DecMapIntfBoolV(v map[interface{}]bool, checkNil bool, canCha
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeBool()
 			mv = dd.DecodeBool()
 			if v != nil {
 			if v != nil {
@@ -16356,7 +16356,7 @@ func (_ fastpathT) DecMapIntfBoolV(v map[interface{}]bool, checkNil bool, canCha
 			mk = nil
 			mk = nil
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv)
+				mk = d.string(bv)
 			}
 			}
 			mv = dd.DecodeBool()
 			mv = dd.DecodeBool()
 			if v != nil {
 			if v != nil {

+ 2 - 2
codec/fast-path.go.tmpl

@@ -426,7 +426,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
 			{{ if eq .MapKey "interface{}" }}mk = nil 
 			{{ if eq .MapKey "interface{}" }}mk = nil 
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
+				mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
 			}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
 			}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
 			{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
 			{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
 			d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
 			d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
@@ -439,7 +439,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
 			{{ if eq .MapKey "interface{}" }}mk = nil 
 			{{ if eq .MapKey "interface{}" }}mk = nil 
 			d.decode(&mk)
 			d.decode(&mk)
 			if bv, bok := mk.([]byte); bok {
 			if bv, bok := mk.([]byte); bok {
-				mk = string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
+				mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
 			}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
 			}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
 			{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
 			{{ if eq .Elem "interface{}" }}if mapGet { mv = v[mk] } else { mv = nil }
 			d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
 			d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}

+ 8 - 2
codec/json.go

@@ -954,7 +954,14 @@ func (d *jsonDecDriver) DecodeString() (s string) {
 	if c := d.s.sc.sep(); c != 0 {
 	if c := d.s.sc.sep(); c != 0 {
 		d.expectChar(c)
 		d.expectChar(c)
 	}
 	}
+	return d.decString()
+}
+
+func (d *jsonDecDriver) decString() (s string) {
 	d.appendStringAsBytes()
 	d.appendStringAsBytes()
+	if x := d.s.sc; x != nil && x.st == '}' && x.so { // map key
+		return d.d.string(d.bs)
+	}
 	return string(d.bs)
 	return string(d.bs)
 }
 }
 
 
@@ -1051,8 +1058,7 @@ func (d *jsonDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurthe
 		decodeFurther = true
 		decodeFurther = true
 	case '"':
 	case '"':
 		vt = valueTypeString
 		vt = valueTypeString
-		d.appendStringAsBytes()
-		v = string(d.bs) // same as d.DecodeString(), but skipping sep() call.
+		v = d.decString() // same as d.DecodeString(), but skipping sep() call.
 	default: // number
 	default: // number
 		d.decNum(true)
 		d.decNum(true)
 		n := &d.n
 		n := &d.n