فهرست منبع

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.
 	//
 	// 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
+
+	// 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 {
 				rvk = rvk.Elem()
 				if rvk.Type() == uint8SliceTyp {
-					rvk = reflect.ValueOf(string(rvk.Bytes()))
+					rvk = reflect.ValueOf(d.string(rvk.Bytes()))
 				}
 			}
 			if mapGet {
@@ -976,7 +986,7 @@ func (f *decFnInfo) kMap(rv reflect.Value) {
 			if ktypeId == intfTypId {
 				rvk = rvk.Elem()
 				if rvk.Type() == uint8SliceTyp {
-					rvk = reflect.ValueOf(string(rvk.Bytes()))
+					rvk = reflect.ValueOf(d.string(rvk.Bytes()))
 				}
 			}
 			if mapGet {
@@ -1020,6 +1030,8 @@ type Decoder struct {
 
 	ri ioDecReader
 	f  map[uintptr]*decFn
+	is map[string]string // used for interning strings
+
 	// _  uintptr // for alignment purposes, so next one starts from a cache line
 
 	b [scratchByteArrayLen]byte
@@ -1040,6 +1052,9 @@ func NewDecoder(r io.Reader, h Handle) (d *Decoder) {
 		d.ri.br = &d.ri.bs
 	}
 	d.r = &d.ri
+	if d.h.InternString {
+		d.is = make(map[string]string, 32)
+	}
 	_, d.js = h.(*JsonHandle)
 	d.d = h.newDecDriver(d)
 	return
@@ -1053,6 +1068,9 @@ func NewDecoderBytes(in []byte, h Handle) (d *Decoder) {
 	d.rb.b = in
 	d.rb.a = len(in)
 	d.r = &d.rb
+	if d.h.InternString {
+		d.is = make(map[string]string, 32)
+	}
 	_, d.js = h.(*JsonHandle)
 	d.d = h.newDecDriver(d)
 	// d.d = h.newDecDriver(decReaderT{true, &d.rb, &d.ri})
@@ -1535,6 +1553,24 @@ func (d *Decoder) errorf(format string, params ...interface{}) {
 	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.

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

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