Browse Source

codec: support encoding non-addressable arrays.

arrays may be unaddressable, and thus we cannot get slice from them.
Consequently, we can't share the same method by grabbing a slice from the array.
Instead, we have to duplicate the methods.

Also, when encoding or decoding anything that looks like a sequence of bytes
([]byte, [X]byte), we should treat as raw bytes (which is what the user most likely
would expect).

Fixes #21 and Fixes #22 .
Ugorji Nwoke 12 years ago
parent
commit
eb2067e438
3 changed files with 78 additions and 8 deletions
  1. 31 4
      codec/codecs_test.go
  2. 2 2
      codec/decode.go
  3. 45 2
      codec/encode.go

+ 31 - 4
codec/codecs_test.go

@@ -694,6 +694,35 @@ func testCodecMiscOne(t *testing.T, h Handle) {
 		logT(t, "Not Equal: %v. t2: %v, t3: %v", err, t2, t3)
 		logT(t, "Not Equal: %v. t2: %v, t3: %v", err, t2, t3)
 		t.FailNow()
 		t.FailNow()
 	}
 	}
+
+	// println(">>>>>")
+	// test simple arrays, non-addressable arrays, slices
+	type tarr struct {
+		A int64 
+		B [3]int64 
+		C []byte  
+		D [3]byte 
+	}
+	var tarr0 = tarr{1, [3]int64{2,3,4}, []byte{4,5,6}, [3]byte{7,8,9} }
+	// test both pointer and non-pointer (value)
+	for _, tarr1 := range []interface{}{tarr0, &tarr0} {
+		bs, err = testMarshal(tarr1, h)
+		if err != nil {
+			logT(t, "Error marshalling tarr: %v, Err: %v", tarr1, err)
+			t.FailNow()
+		}
+		var tarr2 tarr 
+		err = testUnmarshal(&tarr2, bs, h)
+		if err != nil {
+			logT(t, "Error unmarshalling into &tarr2: %v, Err: %v", tarr2, err)
+			t.FailNow()
+		}
+		if err = deepEqual(tarr0, tarr2); err != nil {
+			logT(t, "Not Equal: %v. tarr1: %v, tarr2: %v", err, tarr0, tarr2)
+			t.FailNow()
+		}
+		// fmt.Printf(">>>> err: %v. tarr1: %v, tarr2: %v\n", err, tarr0, tarr2)
+	}
 }
 }
 
 
 func testCodecEmbeddedPointer(t *testing.T, h Handle) {
 func testCodecEmbeddedPointer(t *testing.T, h Handle) {
@@ -706,18 +735,16 @@ func testCodecEmbeddedPointer(t *testing.T, h Handle) {
 		*A
 		*A
 		MoreInt int
 		MoreInt int
 	}
 	}
-	var err error
-	var buf bytes.Buffer
 	var z Z = 4
 	var z Z = 4
 	x1 := &B{&z, &A{5}, 6}
 	x1 := &B{&z, &A{5}, 6}
-	err = NewEncoder(&buf, h).Encode(x1)
+	bs, err := testMarshal(x1, h)
 	if err != nil {
 	if err != nil {
 		logT(t, "Error encoding %v, Err: %v", x1, err)
 		logT(t, "Error encoding %v, Err: %v", x1, err)
 		t.FailNow()
 		t.FailNow()
 	}
 	}
 	// fmt.Printf("buf: len(%v): %x\n", buf.Len(), buf.Bytes())
 	// fmt.Printf("buf: len(%v): %x\n", buf.Len(), buf.Bytes())
 	var x2 = new(B)
 	var x2 = new(B)
-	err = NewDecoder(&buf, h).Decode(x2)
+	err = testUnmarshal(x2, bs, h)
 	if err != nil {
 	if err != nil {
 		logT(t, "Error decoding into %v, Err: %v", x2, err)
 		logT(t, "Error decoding into %v, Err: %v", x2, err)
 		t.FailNow()
 		t.FailNow()

+ 2 - 2
codec/decode.go

@@ -432,7 +432,7 @@ func (f *decFnInfo) kSlice(rv reflect.Value) {
 		}
 		}
 	}
 	}
 
 
-	if f.ti.rtid == uint8SliceTypId { // rawbytes
+	if f.ti.rtid == uint8SliceTypId || f.ti.rt.Elem().Kind() == reflect.Uint8 { 
 		if bs2, changed2 := f.dd.decodeBytes(rv.Bytes()); changed2 {
 		if bs2, changed2 := f.dd.decodeBytes(rv.Bytes()); changed2 {
 			rv.SetBytes(bs2)
 			rv.SetBytes(bs2)
 		}
 		}
@@ -1018,7 +1018,7 @@ func decContLens(dd decDriver) (containerLen, containerLenS int) {
 		containerLen = dd.readMapLen()
 		containerLen = dd.readMapLen()
 		containerLenS = containerLen * 2
 		containerLenS = containerLen * 2
 	default:
 	default:
-		decErr("Only encoded map or array can be decoded into a slice. (valueType: %x)",
+		decErr("Only encoded map or array can be decoded into a slice. (valueType: %0x)",
 			currEncodedType)
 			currEncodedType)
 	}
 	}
 	return
 	return

+ 45 - 2
codec/encode.go

@@ -379,10 +379,13 @@ func (f *encFnInfo) kSlice(rv reflect.Value) {
 		}
 		}
 	}
 	}
 
 
-	if f.ti.rtid == uint8SliceTypId {
+	// If in this method, then there was no extension function defined.
+	// So it's okay to treat as []byte.
+	if f.ti.rtid == uint8SliceTypId || f.ti.rt.Elem().Kind() == reflect.Uint8 {
 		f.ee.encodeStringBytes(c_RAW, rv.Bytes())
 		f.ee.encodeStringBytes(c_RAW, rv.Bytes())
 		return
 		return
 	}
 	}
+	
 	l := rv.Len()
 	l := rv.Len()
 	if f.ti.mbs {
 	if f.ti.mbs {
 		if l%2 == 1 {
 		if l%2 == 1 {
@@ -402,8 +405,48 @@ func (f *encFnInfo) kSlice(rv reflect.Value) {
 }
 }
 
 
 func (f *encFnInfo) kArray(rv reflect.Value) {
 func (f *encFnInfo) kArray(rv reflect.Value) {
+	// We cannot share kSlice method, because the array may be non-addressable.
+	// E.g. type struct S{B [2]byte}; Encode(S{}) will bomb on "panic: slice of unaddressable array".
+	// So we have to duplicate the functionality here.
 	// f.e.encodeValue(rv.Slice(0, rv.Len()))
 	// f.e.encodeValue(rv.Slice(0, rv.Len()))
-	f.kSlice(rv.Slice(0, rv.Len()))
+	// f.kSlice(rv.Slice(0, rv.Len()))
+	
+	// Handle an array of bytes specially (in line with what is done for slices)
+	if f.ti.rt.Elem().Kind() == reflect.Uint8 {
+		rvlen := rv.Len()
+		if rvlen == 0 {
+			f.ee.encodeStringBytes(c_RAW, nil)
+			return
+		}
+		var bs []byte 
+		if rv.CanAddr() {
+			bs = rv.Slice(0, rv.Len()).Bytes()
+		} else {
+			bs = make([]byte, rv.Len())
+			for i := 0; i < len(bs); i++ {
+				bs[i] = byte(rv.Index(i).Uint())
+			}
+		}
+		f.ee.encodeStringBytes(c_RAW, bs)
+		return
+	}
+	
+	l := rv.Len()
+	if f.ti.mbs {
+		if l%2 == 1 {
+			encErr("mapBySlice: invalid length (must be divisible by 2): %v", l)
+		}
+		f.ee.encodeMapPreamble(l / 2)
+	} else {
+		f.ee.encodeArrayPreamble(l)
+	}
+	if l == 0 {
+		return
+	}
+	for j := 0; j < l; j++ {
+		// TODO: Consider perf implication of encoding odd index values as symbols if type is string
+		f.e.encodeValue(rv.Index(j))
+	}
 }
 }
 
 
 func (f *encFnInfo) kStruct(rv reflect.Value) {
 func (f *encFnInfo) kStruct(rv reflect.Value) {