Browse Source

codec: make decNakedContainers lazily initialized (on demand, as needed)

Also, remove unexported function call from shared_test.go i.e. basicHandle()
Ugorji Nwoke 7 years ago
parent
commit
688a245586
2 changed files with 56 additions and 34 deletions
  1. 56 31
      codec/decode.go
  2. 0 3
      codec/shared_test.go

+ 56 - 31
codec/decode.go

@@ -1228,6 +1228,7 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 		return
 		return
 	}
 	}
 	// var useRvn bool
 	// var useRvn bool
+	var idx uint8
 	switch n.v {
 	switch n.v {
 	case valueTypeMap:
 	case valueTypeMap:
 		// if json, default to a map type with string keys
 		// if json, default to a map type with string keys
@@ -1241,11 +1242,17 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 		}
 		}
 		if mtid == mapIntfIntfTypId {
 		if mtid == mapIntfIntfTypId {
 			n.initContainers()
 			n.initContainers()
-			if n.lm < arrayCacheLen {
-				n.ma[n.lm] = nil
-				rvn = n.rma[n.lm]
+			idx = n.lm
+			if idx < arrayCacheLen {
+				ptr := &n.ma[idx]
+				*ptr = nil
+				rvn = n.rma[idx]
+				if !rvn.IsValid() {
+					rvn = reflect.ValueOf(ptr).Elem()
+					n.rma[idx] = rvn
+				}
 				n.lm++
 				n.lm++
-				d.decode(&n.ma[n.lm-1])
+				d.decode(ptr)
 				n.lm--
 				n.lm--
 			} else {
 			} else {
 				var v2 map[interface{}]interface{}
 				var v2 map[interface{}]interface{}
@@ -1254,11 +1261,17 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 			}
 			}
 		} else if mtid == mapStrIntfTypId { // for json performance
 		} else if mtid == mapStrIntfTypId { // for json performance
 			n.initContainers()
 			n.initContainers()
-			if n.ln < arrayCacheLen {
-				n.na[n.ln] = nil
-				rvn = n.rna[n.ln]
+			idx = n.ln
+			if idx < arrayCacheLen {
+				ptr := &n.na[idx]
+				*ptr = nil
+				rvn = n.rna[idx]
+				if !rvn.IsValid() {
+					rvn = reflect.ValueOf(ptr).Elem()
+					n.rna[idx] = rvn
+				}
 				n.ln++
 				n.ln++
-				d.decode(&n.na[n.ln-1])
+				d.decode(ptr)
 				n.ln--
 				n.ln--
 			} else {
 			} else {
 				var v2 map[string]interface{}
 				var v2 map[string]interface{}
@@ -1278,11 +1291,17 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 	case valueTypeArray:
 	case valueTypeArray:
 		if d.stid == 0 || d.stid == intfSliceTypId {
 		if d.stid == 0 || d.stid == intfSliceTypId {
 			n.initContainers()
 			n.initContainers()
-			if n.ls < arrayCacheLen {
-				n.sa[n.ls] = nil
-				rvn = n.rsa[n.ls]
+			idx = n.ls
+			if idx < arrayCacheLen {
+				ptr := &n.sa[idx]
+				*ptr = nil
+				rvn = n.rsa[idx]
+				if !rvn.IsValid() {
+					rvn = reflect.ValueOf(ptr).Elem()
+					n.rsa[idx] = rvn
+				}
 				n.ls++
 				n.ls++
-				d.decode(&n.sa[n.ls-1])
+				d.decode(ptr)
 				n.ls--
 				n.ls--
 			} else {
 			} else {
 				var v2 []interface{}
 				var v2 []interface{}
@@ -1309,14 +1328,16 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 		tag, bytes := n.u, n.l // calling decode below might taint the values
 		tag, bytes := n.u, n.l // calling decode below might taint the values
 		if bytes == nil {
 		if bytes == nil {
 			n.initContainers()
 			n.initContainers()
-			if n.li < arrayCacheLen {
-				n.ia[n.li] = nil
+			idx = n.li
+			if idx < arrayCacheLen {
+				ptr := &n.ia[idx]
+				*ptr = nil
 				n.li++
 				n.li++
-				d.decode(&n.ia[n.li-1])
+				d.decode(ptr)
 				// v = *(&n.ia[l])
 				// v = *(&n.ia[l])
+				v = *ptr
+				*ptr = nil
 				n.li--
 				n.li--
-				v = n.ia[n.li]
-				n.ia[n.li] = nil
 			} else {
 			} else {
 				d.decode(&v)
 				d.decode(&v)
 			}
 			}
@@ -1963,14 +1984,14 @@ type decNakedContainers struct {
 	rma, rna, rsa [arrayCacheLen]reflect.Value // reflect.Value mapping to above
 	rma, rna, rsa [arrayCacheLen]reflect.Value // reflect.Value mapping to above
 }
 }
 
 
-func (n *decNakedContainers) init() {
-	for i := 0; i < arrayCacheLen; i++ {
-		// n.ria[i] = reflect.ValueOf(&(n.ia[i])).Elem()
-		n.rma[i] = reflect.ValueOf(&(n.ma[i])).Elem()
-		n.rna[i] = reflect.ValueOf(&(n.na[i])).Elem()
-		n.rsa[i] = reflect.ValueOf(&(n.sa[i])).Elem()
-	}
-}
+// func (n *decNakedContainers) init() {
+// 	for i := 0; i < arrayCacheLen; i++ {
+// 		// n.ria[i] = reflect.ValueOf(&(n.ia[i])).Elem()
+// 		n.rma[i] = reflect.ValueOf(&(n.ma[i])).Elem()
+// 		n.rna[i] = reflect.ValueOf(&(n.na[i])).Elem()
+// 		n.rsa[i] = reflect.ValueOf(&(n.sa[i])).Elem()
+// 	}
+// }
 
 
 type decNaked struct {
 type decNaked struct {
 	// r RawExt // used for RawExt, uint, []byte.
 	// r RawExt // used for RawExt, uint, []byte.
@@ -1988,7 +2009,7 @@ type decNaked struct {
 
 
 	// state
 	// state
 	v              valueType
 	v              valueType
-	li, lm, ln, ls int8
+	li, lm, ln, ls uint8
 	inited         bool
 	inited         bool
 
 
 	*decNakedContainers
 	*decNakedContainers
@@ -2017,7 +2038,7 @@ func (n *decNaked) init() {
 func (n *decNaked) initContainers() {
 func (n *decNaked) initContainers() {
 	if n.decNakedContainers == nil {
 	if n.decNakedContainers == nil {
 		n.decNakedContainers = new(decNakedContainers)
 		n.decNakedContainers = new(decNakedContainers)
-		n.decNakedContainers.init()
+		// n.decNakedContainers.init()
 	}
 	}
 }
 }
 
 
@@ -2633,6 +2654,8 @@ func (d *Decoder) finalize() {
 //
 //
 // It is important to call Close() when done with a Decoder, so those resources
 // It is important to call Close() when done with a Decoder, so those resources
 // are released instantly for use by subsequently created Decoders.
 // are released instantly for use by subsequently created Decoders.
+//
+// By default, Close() is automatically called unless the option DoNotClose is set.
 func (d *Decoder) Close() {
 func (d *Decoder) Close() {
 	if useFinalizers && removeFinalizerOnClose {
 	if useFinalizers && removeFinalizerOnClose {
 		runtime.SetFinalizer(d, nil)
 		runtime.SetFinalizer(d, nil)
@@ -2698,11 +2721,13 @@ func (d *Decoder) swallow() {
 		dd.DecodeNaked()
 		dd.DecodeNaked()
 		if n.v == valueTypeExt && n.l == nil {
 		if n.v == valueTypeExt && n.l == nil {
 			n.initContainers()
 			n.initContainers()
-			if n.li < arrayCacheLen {
-				n.ia[n.li] = nil
+			idx := n.li
+			if idx < arrayCacheLen {
+				ptr := &n.ia[idx]
+				*ptr = nil
 				n.li++
 				n.li++
-				d.decode(&n.ia[n.li-1])
-				n.ia[n.li-1] = nil
+				d.decode(ptr)
+				*ptr = nil
 				n.li--
 				n.li--
 			} else {
 			} else {
 				var v2 interface{}
 				var v2 interface{}

+ 0 - 3
codec/shared_test.go

@@ -140,9 +140,6 @@ func init() {
 	testHandles = append(testHandles,
 	testHandles = append(testHandles,
 		// testNoopH,
 		// testNoopH,
 		testMsgpackH, testBincH, testSimpleH, testCborH, testJsonH)
 		testMsgpackH, testBincH, testSimpleH, testCborH, testJsonH)
-	for _, h := range testHandles {
-		_ = basicHandle(h) // ensure all basic handles are initialized
-	}
 	// set DoNotClose on each handle
 	// set DoNotClose on each handle
 	testMsgpackH.DoNotClose = true
 	testMsgpackH.DoNotClose = true
 	testBincH.DoNotClose = true
 	testBincH.DoNotClose = true