Sfoglia il codice sorgente

codec: honor SelfExt during naked decoding

Include some minor clean up for lint warnings, etc

Updates #274
Ugorji Nwoke 6 anni fa
parent
commit
7a66d0f64d
5 ha cambiato i file con 48 aggiunte e 47 eliminazioni
  1. 1 4
      README.md
  2. 27 15
      codec/decode.go
  3. 1 3
      codec/doc.go
  4. 19 20
      codec/helper.go
  5. 0 5
      codec/values_flex_test.go

+ 1 - 4
README.md

@@ -31,10 +31,7 @@ Supported Serialization formats are:
 
 This package will carefully use 'package unsafe' for performance reasons in
 specific places. You can build without unsafe use by passing the safe or
-appengine tag i.e. 'go install -tags=safe ...'. Note that unsafe is only
-supported for the last 4 go releases e.g. current go release is go 1.12, so
-we support unsafe use only from go 1.9+ . This is because supporting unsafe
-requires knowledge of implementation details.
+appengine tag i.e. 'go install -tags=safe ...'.
 
 For detailed usage information, read the primer at
 http://ugorji.net/blog/go-codec-primer .

+ 27 - 15
codec/decode.go

@@ -1292,26 +1292,38 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 			}
 		}
 	case valueTypeExt:
-		var v interface{}
 		tag, bytes := n.u, n.l // calling decode below might taint the values
-		if bytes == nil {
-			d.decode(&v)
-		}
 		bfn := d.h.getExtForTag(tag)
-		if bfn == nil {
-			var re RawExt
-			re.Tag = tag
-			re.Data = detachZeroCopyBytes(d.bytes, nil, bytes)
-			re.Value = v
-			rvn = reflect.ValueOf(&re).Elem()
+		var re = RawExt{Tag: tag}
+		if bytes == nil {
+			// it is one of the InterfaceExt ones: json and cbor
+			// almost definitely cbor, as json naked decoding never reveals valueTypeExt (no tagging support).
+			if bfn == nil {
+				d.decode(&re.Value)
+				rvn = reflect.ValueOf(&re).Elem()
+			} else {
+				rvn = reflect.New(bfn.rt)
+				if bfn.ext == SelfExt {
+					d.decodeValue(rvn, d.h.fnNoExt(rvn.Type().Elem()))
+				} else {
+					d.interfaceExtConvertAndDecode(rv2i(rvn), bfn.ext)
+				}
+				rvn = rvn.Elem()
+			}
 		} else {
-			rvnA := reflect.New(bfn.rt)
-			if bytes != nil {
-				bfn.ext.ReadExt(rv2i(rvnA), bytes)
+			// one of the BytesExt ones: binc, msgpack, simple
+			if bfn == nil {
+				re.Data = detachZeroCopyBytes(d.bytes, nil, bytes)
+				rvn = reflect.ValueOf(&re).Elem()
 			} else {
-				bfn.ext.UpdateExt(rv2i(rvnA), v)
+				rvn = reflect.New(bfn.rt)
+				if bfn.ext == SelfExt {
+					d.sideDecode(rv2i(rvn), bytes)
+				} else {
+					bfn.ext.ReadExt(rv2i(rvn), bytes)
+				}
+				rvn = rvn.Elem()
 			}
-			rvn = rvnA.Elem()
 		}
 	case valueTypeNil:
 		// no-op

+ 1 - 3
codec/doc.go

@@ -16,9 +16,7 @@ Supported Serialization formats are:
 
 This package will carefully use 'package unsafe' for performance reasons in specific places.
 You can build without unsafe use by passing the safe or appengine tag
-i.e. 'go install -tags=safe ...'. Note that unsafe is only supported for the last 4
-go releases e.g. current go release is go 1.12, so we support unsafe use only from
-go 1.9+ . This is because supporting unsafe requires knowledge of implementation details.
+i.e. 'go install -tags=safe ...'.
 
 For detailed usage information, read the primer at http://ugorji.net/blog/go-codec-primer .
 

+ 19 - 20
codec/helper.go

@@ -683,26 +683,26 @@ LOOP:
 	return
 }
 
-func (c *BasicHandle) fn(rt reflect.Type) (fn *codecFn) {
-	return c.fnVia(rt, &c.rtidFns, true)
+func (x *BasicHandle) fn(rt reflect.Type) (fn *codecFn) {
+	return x.fnVia(rt, &x.rtidFns, true)
 }
 
-func (c *BasicHandle) fnNoExt(rt reflect.Type) (fn *codecFn) {
-	return c.fnVia(rt, &c.rtidFnsNoExt, false)
+func (x *BasicHandle) fnNoExt(rt reflect.Type) (fn *codecFn) {
+	return x.fnVia(rt, &x.rtidFnsNoExt, false)
 }
 
-func (c *BasicHandle) fnVia(rt reflect.Type, fs *atomicRtidFnSlice, checkExt bool) (fn *codecFn) {
+func (x *BasicHandle) fnVia(rt reflect.Type, fs *atomicRtidFnSlice, checkExt bool) (fn *codecFn) {
 	// xdebug2f("fnVia: rt: %v, checkExt: %v", rt, checkExt)
 	rtid := rt2id(rt)
 	sp := fs.load()
 	if sp != nil {
 		if _, fn = findFn(sp, rtid); fn != nil {
-			// xdebugf("<<<< %c: found fn for %v in rtidfns of size: %v", c.n, rt, len(sp))
+			// xdebugf("<<<< %c: found fn for %v in rtidfns of size: %v", x.n, rt, len(sp))
 			return
 		}
 	}
-	fn = c.fnLoad(rt, rtid, checkExt)
-	c.mu.Lock()
+	fn = x.fnLoad(rt, rtid, checkExt)
+	x.mu.Lock()
 	var sp2 []codecRtidFn
 	sp = fs.load()
 	if sp == nil {
@@ -717,27 +717,27 @@ func (c *BasicHandle) fnVia(rt reflect.Type, fs *atomicRtidFnSlice, checkExt boo
 			copy(sp2, sp[:idx])
 			copy(sp2[idx+1:], sp[idx:])
 			sp2[idx] = codecRtidFn{rtid, fn}
-			c.rtidFns.store(sp2)
+			x.rtidFns.store(sp2)
 			// xdebugf(">>>> adding rt: %v to rtidfns of size: %v", rt, len(sp2))
 
 		}
 	}
-	c.mu.Unlock()
+	x.mu.Unlock()
 	return
 }
 
-func (c *BasicHandle) fnLoad(rt reflect.Type, rtid uintptr, checkExt bool) (fn *codecFn) {
-	// xdebugf("#### for %c: load fn for %v in rtidfns of size: %v", c.n, rt, len(sp))
+func (x *BasicHandle) fnLoad(rt reflect.Type, rtid uintptr, checkExt bool) (fn *codecFn) {
+	// xdebugf("#### for %c: load fn for %v in rtidfns of size: %v", x.n, rt, len(sp))
 	fn = new(codecFn)
 	fi := &(fn.i)
-	ti := c.getTypeInfo(rtid, rt)
+	ti := x.getTypeInfo(rtid, rt)
 	fi.ti = ti
 
 	rk := reflect.Kind(ti.kind)
 
 	// anything can be an extension except the built-in ones: time, raw and rawext
 
-	if rtid == timeTypId && !c.TimeNotBuiltin {
+	if rtid == timeTypId && !x.TimeNotBuiltin {
 		fn.fe = (*Encoder).kTime
 		fn.fd = (*Decoder).kTime
 	} else if rtid == rawTypId {
@@ -749,7 +749,7 @@ func (c *BasicHandle) fnLoad(rt reflect.Type, rtid uintptr, checkExt bool) (fn *
 		fi.addrF = true
 		fi.addrD = true
 		fi.addrE = true
-	} else if xfFn := c.getExt(rtid, checkExt); xfFn != nil {
+	} else if xfFn := x.getExt(rtid, checkExt); xfFn != nil {
 		fi.xfTag, fi.xfFn = xfFn.tag, xfFn.ext
 		fn.fe = (*Encoder).ext
 		fn.fd = (*Decoder).ext
@@ -764,13 +764,13 @@ func (c *BasicHandle) fnLoad(rt reflect.Type, rtid uintptr, checkExt bool) (fn *
 		fi.addrF = true
 		fi.addrD = ti.csp
 		fi.addrE = ti.csp
-	} else if supportMarshalInterfaces && c.isBe() && (ti.bm || ti.bmp) && (ti.bu || ti.bup) {
+	} else if supportMarshalInterfaces && x.isBe() && (ti.bm || ti.bmp) && (ti.bu || ti.bup) {
 		fn.fe = (*Encoder).binaryMarshal
 		fn.fd = (*Decoder).binaryUnmarshal
 		fi.addrF = true
 		fi.addrD = ti.bup
 		fi.addrE = ti.bmp
-	} else if supportMarshalInterfaces && !c.isBe() && c.isJs() &&
+	} else if supportMarshalInterfaces && !x.isBe() && x.isJs() &&
 		(ti.jm || ti.jmp) && (ti.ju || ti.jup) {
 		//If JSON, we should check JSONMarshal before textMarshal
 		fn.fe = (*Encoder).jsonMarshal
@@ -778,7 +778,7 @@ func (c *BasicHandle) fnLoad(rt reflect.Type, rtid uintptr, checkExt bool) (fn *
 		fi.addrF = true
 		fi.addrD = ti.jup
 		fi.addrE = ti.jmp
-	} else if supportMarshalInterfaces && !c.isBe() && (ti.tm || ti.tmp) && (ti.tu || ti.tup) {
+	} else if supportMarshalInterfaces && !x.isBe() && (ti.tm || ti.tmp) && (ti.tu || ti.tup) {
 		fn.fe = (*Encoder).textMarshal
 		fn.fd = (*Decoder).textUnmarshal
 		fi.addrF = true
@@ -2091,9 +2091,8 @@ func usableByteSlice(bs []byte, slen int) []byte {
 	if cap(bs) >= slen {
 		if bs == nil {
 			return []byte{}
-		} else {
-			return bs[:slen]
 		}
+		return bs[:slen]
 	}
 	return make([]byte, slen)
 }

+ 0 - 5
codec/values_flex_test.go

@@ -145,11 +145,6 @@ type TestSelfExtImpl struct {
 	testSelfExtHelper
 }
 
-func (t *TestSelfExtImpl) CodecConvertExt() interface{} {
-	return &t.testSelfExtHelper
-}
-func (t *TestSelfExtImpl) CodecUpdateExt(v interface{}) {}
-
 var testWRepeated512 wrapBytes
 var testStrucTime = time.Date(2012, 2, 2, 2, 2, 2, 2000, time.UTC).UTC()