Browse Source

codec: if json and the struct field name (or encode name) is ascii and alphanumeric, just write it out

This will bypass json EncodeString calculation in the common case.
Ugorji Nwoke 7 years ago
parent
commit
eeb0478a81
6 changed files with 486 additions and 117 deletions
  1. 29 27
      codec/encode.go
  2. 17 1
      codec/gen-helper.generated.go
  3. 16 1
      codec/gen-helper.go.tmpl
  4. 6 0
      codec/gen.go
  5. 33 11
      codec/helper.go
  6. 385 77
      codec/mammoth2_codecgen_generated_test.go

+ 29 - 27
codec/encode.go

@@ -492,14 +492,14 @@ func (e *Encoder) kStructNoOmitempty(f *codecFnInfo, rv reflect.Value) {
 			for _, si := range tisfi {
 			for _, si := range tisfi {
 				ee.WriteMapElemKey()
 				ee.WriteMapElemKey()
 				// ee.EncodeString(cUTF8, si.encName)
 				// ee.EncodeString(cUTF8, si.encName)
-				encStructFieldKey(ee, fti.keyType, si.encName)
+				e.kStructFieldKey(fti.keyType, si)
 				ee.WriteMapElemValue()
 				ee.WriteMapElemValue()
 				e.encodeValue(sfn.field(si), nil, true)
 				e.encodeValue(sfn.field(si), nil, true)
 			}
 			}
 		} else {
 		} else {
 			for _, si := range tisfi {
 			for _, si := range tisfi {
 				// ee.EncodeString(cUTF8, si.encName)
 				// ee.EncodeString(cUTF8, si.encName)
-				encStructFieldKey(ee, fti.keyType, si.encName)
+				e.kStructFieldKey(fti.keyType, si)
 				e.encodeValue(sfn.field(si), nil, true)
 				e.encodeValue(sfn.field(si), nil, true)
 			}
 			}
 		}
 		}
@@ -520,22 +520,24 @@ func (e *Encoder) kStructNoOmitempty(f *codecFnInfo, rv reflect.Value) {
 	}
 	}
 }
 }
 
 
-func encStructFieldKey(ee encDriver, keyType valueType, s string) {
+func (e *Encoder) kStructFieldKey(keyType valueType, s *structFieldInfo) {
 	var m must
 	var m must
-
 	// use if-else-if, not switch (which compiles to binary-search)
 	// use if-else-if, not switch (which compiles to binary-search)
 	// since keyType is typically valueTypeString, branch prediction is pretty good.
 	// since keyType is typically valueTypeString, branch prediction is pretty good.
-
 	if keyType == valueTypeString {
 	if keyType == valueTypeString {
-		ee.EncodeString(cUTF8, s)
+		if e.js && s.encNameAsciiAlphaNum { // keyType == valueTypeString
+			e.w.writen1('"')
+			e.w.writestr(s.encName)
+			e.w.writen1('"')
+		} else { // keyType == valueTypeString
+			e.e.EncodeString(cUTF8, s.encName)
+		}
 	} else if keyType == valueTypeInt {
 	} else if keyType == valueTypeInt {
-		ee.EncodeInt(m.Int(strconv.ParseInt(s, 10, 64)))
+		e.e.EncodeInt(m.Int(strconv.ParseInt(s.encName, 10, 64)))
 	} else if keyType == valueTypeUint {
 	} else if keyType == valueTypeUint {
-		ee.EncodeUint(m.Uint(strconv.ParseUint(s, 10, 64)))
+		e.e.EncodeUint(m.Uint(strconv.ParseUint(s.encName, 10, 64)))
 	} else if keyType == valueTypeFloat {
 	} else if keyType == valueTypeFloat {
-		ee.EncodeFloat64(m.Float(strconv.ParseFloat(s, 64)))
-	} else {
-		ee.EncodeString(cUTF8, s)
+		e.e.EncodeFloat64(m.Float(strconv.ParseFloat(s.encName, 64)))
 	}
 	}
 }
 }
 
 
@@ -565,29 +567,29 @@ func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 
 
 	var spool *sync.Pool
 	var spool *sync.Pool
 	var poolv interface{}
 	var poolv interface{}
-	var fkvs []stringRv
+	var fkvs []sfiRv
 	// fmt.Printf(">>>>>>>>>>>>>> encode.kStruct: newlen: %d\n", newlen)
 	// fmt.Printf(">>>>>>>>>>>>>> encode.kStruct: newlen: %d\n", newlen)
 	if newlen <= 8 {
 	if newlen <= 8 {
-		spool, poolv = pool.stringRv8()
-		fkvs = poolv.(*[8]stringRv)[:newlen]
+		spool, poolv = pool.sfiRv8()
+		fkvs = poolv.(*[8]sfiRv)[:newlen]
 	} else if newlen <= 16 {
 	} else if newlen <= 16 {
-		spool, poolv = pool.stringRv16()
-		fkvs = poolv.(*[16]stringRv)[:newlen]
+		spool, poolv = pool.sfiRv16()
+		fkvs = poolv.(*[16]sfiRv)[:newlen]
 	} else if newlen <= 32 {
 	} else if newlen <= 32 {
-		spool, poolv = pool.stringRv32()
-		fkvs = poolv.(*[32]stringRv)[:newlen]
+		spool, poolv = pool.sfiRv32()
+		fkvs = poolv.(*[32]sfiRv)[:newlen]
 	} else if newlen <= 64 {
 	} else if newlen <= 64 {
-		spool, poolv = pool.stringRv64()
-		fkvs = poolv.(*[64]stringRv)[:newlen]
+		spool, poolv = pool.sfiRv64()
+		fkvs = poolv.(*[64]sfiRv)[:newlen]
 	} else if newlen <= 128 {
 	} else if newlen <= 128 {
-		spool, poolv = pool.stringRv128()
-		fkvs = poolv.(*[128]stringRv)[:newlen]
+		spool, poolv = pool.sfiRv128()
+		fkvs = poolv.(*[128]sfiRv)[:newlen]
 	} else {
 	} else {
-		fkvs = make([]stringRv, newlen)
+		fkvs = make([]sfiRv, newlen)
 	}
 	}
 
 
 	newlen = 0
 	newlen = 0
-	var kv stringRv
+	var kv sfiRv
 	recur := e.h.RecursiveEmptyCheck
 	recur := e.h.RecursiveEmptyCheck
 	sfn := structFieldNode{v: rv, update: false}
 	sfn := structFieldNode{v: rv, update: false}
 	for _, si := range tisfi {
 	for _, si := range tisfi {
@@ -597,7 +599,7 @@ func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 			if si.omitEmpty() && isEmptyValue(kv.r, e.h.TypeInfos, recur, recur) {
 			if si.omitEmpty() && isEmptyValue(kv.r, e.h.TypeInfos, recur, recur) {
 				continue
 				continue
 			}
 			}
-			kv.v = si.encName
+			kv.v = si // si.encName
 		} else {
 		} else {
 			// use the zero value.
 			// use the zero value.
 			// if a reference or struct, set to nil (so you do not output too much)
 			// if a reference or struct, set to nil (so you do not output too much)
@@ -619,7 +621,7 @@ func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 				kv = fkvs[j]
 				kv = fkvs[j]
 				ee.WriteMapElemKey()
 				ee.WriteMapElemKey()
 				// ee.EncodeString(cUTF8, kv.v)
 				// ee.EncodeString(cUTF8, kv.v)
-				encStructFieldKey(ee, fti.keyType, kv.v)
+				e.kStructFieldKey(fti.keyType, kv.v)
 				ee.WriteMapElemValue()
 				ee.WriteMapElemValue()
 				e.encodeValue(kv.r, nil, true)
 				e.encodeValue(kv.r, nil, true)
 			}
 			}
@@ -627,7 +629,7 @@ func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 			for j := 0; j < newlen; j++ {
 			for j := 0; j < newlen; j++ {
 				kv = fkvs[j]
 				kv = fkvs[j]
 				// ee.EncodeString(cUTF8, kv.v)
 				// ee.EncodeString(cUTF8, kv.v)
-				encStructFieldKey(ee, fti.keyType, kv.v)
+				e.kStructFieldKey(fti.keyType, kv.v)
 				e.encodeValue(kv.r, nil, true)
 				e.encodeValue(kv.r, nil, true)
 			}
 			}
 		}
 		}

+ 17 - 1
codec/gen-helper.generated.go

@@ -10,6 +10,7 @@ package codec
 import (
 import (
 	"encoding"
 	"encoding"
 	"reflect"
 	"reflect"
+	"strconv"
 )
 )
 
 
 // GenVersion is the current version of codecgen.
 // GenVersion is the current version of codecgen.
@@ -49,7 +50,17 @@ type genHelperEncDriver struct {
 
 
 func (x genHelperEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {}
 func (x genHelperEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {}
 func (x genHelperEncDriver) EncStructFieldKey(keyType valueType, s string) {
 func (x genHelperEncDriver) EncStructFieldKey(keyType valueType, s string) {
-	encStructFieldKey(x.encDriver, keyType, s)
+	var m must
+	if keyType == valueTypeString {
+		x.encDriver.EncodeString(cUTF8, s)
+	} else if keyType == valueTypeInt {
+		x.encDriver.EncodeInt(m.Int(strconv.ParseInt(s, 10, 64)))
+	} else if keyType == valueTypeUint {
+		x.encDriver.EncodeUint(m.Uint(strconv.ParseUint(s, 10, 64)))
+	} else if keyType == valueTypeFloat {
+		x.encDriver.EncodeFloat64(m.Float(strconv.ParseFloat(s, 64)))
+	}
+	// encStructFieldKey(x.encDriver, keyType, s)
 }
 }
 func (x genHelperEncDriver) EncodeSymbol(s string) {
 func (x genHelperEncDriver) EncodeSymbol(s string) {
 	x.encDriver.EncodeString(cUTF8, s)
 	x.encDriver.EncodeString(cUTF8, s)
@@ -169,6 +180,11 @@ func (f genHelperEncoder) EncExtension(v interface{}, xfFn *extTypeTagFn) {
 	f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 	f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 }
 }
 
 
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) WriteStr(s string) {
+	f.e.w.writestr(s)
+}
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 //
 //
 // Deprecated: No longer used,
 // Deprecated: No longer used,

+ 16 - 1
codec/gen-helper.go.tmpl

@@ -10,6 +10,7 @@ package codec
 import (
 import (
 	"encoding"
 	"encoding"
 	"reflect"
 	"reflect"
+	"strconv"
 )
 )
 
 
 // GenVersion is the current version of codecgen.
 // GenVersion is the current version of codecgen.
@@ -49,7 +50,17 @@ type genHelperEncDriver struct {
 
 
 func (x genHelperEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {}
 func (x genHelperEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {}
 func (x genHelperEncDriver) EncStructFieldKey(keyType valueType, s string) {
 func (x genHelperEncDriver) EncStructFieldKey(keyType valueType, s string) {
-	encStructFieldKey(x.encDriver, keyType, s)
+	var m must
+	if keyType == valueTypeString {
+		x.encDriver.EncodeString(cUTF8, s)
+	} else if keyType == valueTypeInt {
+		x.encDriver.EncodeInt(m.Int(strconv.ParseInt(s, 10, 64)))
+	} else if keyType == valueTypeUint {
+		x.encDriver.EncodeUint(m.Uint(strconv.ParseUint(s, 10, 64)))
+	} else if keyType == valueTypeFloat {
+		x.encDriver.EncodeFloat64(m.Float(strconv.ParseFloat(s, 64)))
+	} 
+	// encStructFieldKey(x.encDriver, keyType, s)
 }
 }
 func (x genHelperEncDriver) EncodeSymbol(s string) {
 func (x genHelperEncDriver) EncodeSymbol(s string) {
 	x.encDriver.EncodeString(cUTF8, s)
 	x.encDriver.EncodeString(cUTF8, s)
@@ -158,6 +169,10 @@ func (f genHelperEncoder) EncExtension(v interface{}, xfFn *extTypeTagFn) {
 	f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 	f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
 }
 }
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) WriteStr(s string) {
+	f.e.w.writestr(s)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 //
 //
 // Deprecated: No longer used,
 // Deprecated: No longer used,
 // but leave in-place so that old generated files continue to work without regeneration.
 // but leave in-place so that old generated files continue to work without regeneration.

+ 6 - 0
codec/gen.go

@@ -1057,7 +1057,13 @@ func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
 		case valueTypeFloat:
 		case valueTypeFloat:
 			x.linef("r.EncodeFloat64(z.M.Float(strconv.ParseFloat(`%s`, 64)))", si.encName)
 			x.linef("r.EncodeFloat64(z.M.Float(strconv.ParseFloat(`%s`, 64)))", si.encName)
 		default: // string
 		default: // string
+			if si.encNameAsciiAlphaNum {
+				x.linef(`if z.IsJSONHandle() { z.WriteStr("\"%s\"") } else { `, si.encName)
+			}
 			x.linef("r.EncodeString(codecSelferCcUTF8%s, `%s`)", x.xs, si.encName)
 			x.linef("r.EncodeString(codecSelferCcUTF8%s, `%s`)", x.xs, si.encName)
+			if si.encNameAsciiAlphaNum {
+				x.linef("}")
+			}
 		}
 		}
 		// x.linef("r.EncStructFieldKey(codecSelferValueType%s%s, `%s`)", ti.keyType.String(), x.xs, si.encName)
 		// x.linef("r.EncStructFieldKey(codecSelferValueType%s%s, `%s`)", ti.keyType.String(), x.xs, si.encName)
 		x.line("r.WriteMapElemValue()")
 		x.line("r.WriteMapElemValue()")

+ 33 - 11
codec/helper.go

@@ -814,6 +814,8 @@ type structFieldInfo struct {
 
 
 	is  [maxLevelsEmbedding]uint16 // (recursive/embedded) field index in struct
 	is  [maxLevelsEmbedding]uint16 // (recursive/embedded) field index in struct
 	nis uint8                      // num levels of embedding. if 1, then it's not embedded.
 	nis uint8                      // num levels of embedding. if 1, then it's not embedded.
+
+	encNameAsciiAlphaNum bool // the encName only contains ascii alphabet and numbers
 	structFieldInfoFlag
 	structFieldInfoFlag
 }
 }
 
 
@@ -1169,7 +1171,13 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
 
 
 	// do not hold lock while computing this.
 	// do not hold lock while computing this.
 	// it may lead to duplication, but that's ok.
 	// it may lead to duplication, but that's ok.
-	ti := typeInfo{rt: rt, rtid: rtid, kind: uint8(rk), pkgpath: rt.PkgPath()}
+	ti := typeInfo{
+		rt:      rt,
+		rtid:    rtid,
+		kind:    uint8(rk),
+		pkgpath: rt.PkgPath(),
+		keyType: valueTypeString, // default it - so it's never 0
+	}
 	// ti.rv0 = reflect.Zero(rt)
 	// ti.rv0 = reflect.Zero(rt)
 
 
 	// ti.comparable = rt.Comparable()
 	// ti.comparable = rt.Comparable()
@@ -1356,6 +1364,15 @@ LOOP:
 		} else if si.encName == "" {
 		} else if si.encName == "" {
 			si.encName = f.Name
 			si.encName = f.Name
 		}
 		}
+		si.encNameAsciiAlphaNum = true
+		for i := len(si.encName) - 1; i >= 0; i-- {
+			b := si.encName[i]
+			if (b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') {
+				continue
+			}
+			si.encNameAsciiAlphaNum = false
+			break
+		}
 		si.fieldName = f.Name
 		si.fieldName = f.Name
 		si.flagSet(structFieldInfoFlagReady)
 		si.flagSet(structFieldInfoFlagReady)
 
 
@@ -2021,6 +2038,11 @@ func (p boolSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
 
 
 // ---------------------
 // ---------------------
 
 
+type sfiRv struct {
+	v *structFieldInfo
+	r reflect.Value
+}
+
 type intRv struct {
 type intRv struct {
 	v int64
 	v int64
 	r reflect.Value
 	r reflect.Value
@@ -2244,29 +2266,29 @@ type pooler struct {
 }
 }
 
 
 func (p *pooler) init() {
 func (p *pooler) init() {
-	p.strRv8.New = func() interface{} { return new([8]stringRv) }
-	p.strRv16.New = func() interface{} { return new([16]stringRv) }
-	p.strRv32.New = func() interface{} { return new([32]stringRv) }
-	p.strRv64.New = func() interface{} { return new([64]stringRv) }
-	p.strRv128.New = func() interface{} { return new([128]stringRv) }
+	p.strRv8.New = func() interface{} { return new([8]sfiRv) }
+	p.strRv16.New = func() interface{} { return new([16]sfiRv) }
+	p.strRv32.New = func() interface{} { return new([32]sfiRv) }
+	p.strRv64.New = func() interface{} { return new([64]sfiRv) }
+	p.strRv128.New = func() interface{} { return new([128]sfiRv) }
 	p.dn.New = func() interface{} { x := new(decNaked); x.init(); return x }
 	p.dn.New = func() interface{} { x := new(decNaked); x.init(); return x }
 	p.tiload.New = func() interface{} { return new(typeInfoLoadArray) }
 	p.tiload.New = func() interface{} { return new(typeInfoLoadArray) }
 	p.cfn.New = func() interface{} { return new(codecFner) }
 	p.cfn.New = func() interface{} { return new(codecFner) }
 }
 }
 
 
-func (p *pooler) stringRv8() (sp *sync.Pool, v interface{}) {
+func (p *pooler) sfiRv8() (sp *sync.Pool, v interface{}) {
 	return &p.strRv8, p.strRv8.Get()
 	return &p.strRv8, p.strRv8.Get()
 }
 }
-func (p *pooler) stringRv16() (sp *sync.Pool, v interface{}) {
+func (p *pooler) sfiRv16() (sp *sync.Pool, v interface{}) {
 	return &p.strRv16, p.strRv16.Get()
 	return &p.strRv16, p.strRv16.Get()
 }
 }
-func (p *pooler) stringRv32() (sp *sync.Pool, v interface{}) {
+func (p *pooler) sfiRv32() (sp *sync.Pool, v interface{}) {
 	return &p.strRv32, p.strRv32.Get()
 	return &p.strRv32, p.strRv32.Get()
 }
 }
-func (p *pooler) stringRv64() (sp *sync.Pool, v interface{}) {
+func (p *pooler) sfiRv64() (sp *sync.Pool, v interface{}) {
 	return &p.strRv64, p.strRv64.Get()
 	return &p.strRv64, p.strRv64.Get()
 }
 }
-func (p *pooler) stringRv128() (sp *sync.Pool, v interface{}) {
+func (p *pooler) sfiRv128() (sp *sync.Pool, v interface{}) {
 	return &p.strRv128, p.strRv128.Get()
 	return &p.strRv128, p.strRv128.Get()
 }
 }
 func (p *pooler) decNaked() (sp *sync.Pool, v interface{}) {
 func (p *pooler) decNaked() (sp *sync.Pool, v interface{}) {

File diff suppressed because it is too large
+ 385 - 77
codec/mammoth2_codecgen_generated_test.go


Some files were not shown because too many files changed in this diff