]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: allow Copy to a byte array or byte slice from a string
authorTim Cooper <tim.cooper@layeh.com>
Thu, 12 Oct 2017 20:42:18 +0000 (17:42 -0300)
committerIan Lance Taylor <iant@golang.org>
Fri, 13 Oct 2017 02:35:56 +0000 (02:35 +0000)
This somewhat mirrors the special case behavior of the copy built-in.

Fixes #22215

Change-Id: Ic353003ad3de659d3a6b4e9d97295b42510f3bf7
Reviewed-on: https://go-review.googlesource.com/70431
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/reflect/all_test.go
src/reflect/value.go

index efa9fe13d23520fbd3a87f0deccdb152fe49c83b..0a1a38dd2ed12ef53e413c0d32be81e370d1938c 100644 (file)
@@ -668,6 +668,47 @@ func TestCopy(t *testing.T) {
        }
 }
 
+func TestCopyString(t *testing.T) {
+       t.Run("Slice", func(t *testing.T) {
+               s := bytes.Repeat([]byte{'_'}, 8)
+               val := ValueOf(s)
+
+               n := Copy(val, ValueOf(""))
+               if expecting := []byte("________"); n != 0 || !bytes.Equal(s, expecting) {
+                       t.Errorf("got n = %d, s = %s, expecting n = 0, s = %s", n, s, expecting)
+               }
+
+               n = Copy(val, ValueOf("hello"))
+               if expecting := []byte("hello___"); n != 5 || !bytes.Equal(s, expecting) {
+                       t.Errorf("got n = %d, s = %s, expecting n = 5, s = %s", n, s, expecting)
+               }
+
+               n = Copy(val, ValueOf("helloworld"))
+               if expecting := []byte("hellowor"); n != 8 || !bytes.Equal(s, expecting) {
+                       t.Errorf("got n = %d, s = %s, expecting n = 8, s = %s", n, s, expecting)
+               }
+       })
+       t.Run("Array", func(t *testing.T) {
+               s := [...]byte{'_', '_', '_', '_', '_', '_', '_', '_'}
+               val := ValueOf(&s).Elem()
+
+               n := Copy(val, ValueOf(""))
+               if expecting := []byte("________"); n != 0 || !bytes.Equal(s[:], expecting) {
+                       t.Errorf("got n = %d, s = %s, expecting n = 0, s = %s", n, s[:], expecting)
+               }
+
+               n = Copy(val, ValueOf("hello"))
+               if expecting := []byte("hello___"); n != 5 || !bytes.Equal(s[:], expecting) {
+                       t.Errorf("got n = %d, s = %s, expecting n = 5, s = %s", n, s[:], expecting)
+               }
+
+               n = Copy(val, ValueOf("helloworld"))
+               if expecting := []byte("hellowor"); n != 8 || !bytes.Equal(s[:], expecting) {
+                       t.Errorf("got n = %d, s = %s, expecting n = 8, s = %s", n, s[:], expecting)
+               }
+       })
+}
+
 func TestCopyArray(t *testing.T) {
        a := [8]int{1, 2, 3, 4, 10, 9, 8, 7}
        b := [11]int{11, 22, 33, 44, 1010, 99, 88, 77, 66, 55, 44}
index e9bfe550f4c3b1cc2fa2e5a0e8bf8fbbae6c77f2..d3b03e9b028c8f8d96a845ff2b906f3f958f6dd4 100644 (file)
@@ -1864,6 +1864,8 @@ func AppendSlice(s, t Value) Value {
 // It returns the number of elements copied.
 // Dst and src each must have kind Slice or Array, and
 // dst and src must have the same element type.
+//
+// As a special case, src can have kind String if the element type of dst is kind Uint8.
 func Copy(dst, src Value) int {
        dk := dst.kind()
        if dk != Array && dk != Slice {
@@ -1875,14 +1877,20 @@ func Copy(dst, src Value) int {
        dst.mustBeExported()
 
        sk := src.kind()
+       var stringCopy bool
        if sk != Array && sk != Slice {
-               panic(&ValueError{"reflect.Copy", sk})
+               stringCopy = sk == String && dst.typ.Elem().Kind() == Uint8
+               if !stringCopy {
+                       panic(&ValueError{"reflect.Copy", sk})
+               }
        }
        src.mustBeExported()
 
        de := dst.typ.Elem()
-       se := src.typ.Elem()
-       typesMustMatch("reflect.Copy", de, se)
+       if !stringCopy {
+               se := src.typ.Elem()
+               typesMustMatch("reflect.Copy", de, se)
+       }
 
        var ds, ss sliceHeader
        if dk == Array {
@@ -1896,8 +1904,13 @@ func Copy(dst, src Value) int {
                ss.Data = src.ptr
                ss.Len = src.Len()
                ss.Cap = ss.Len
-       } else {
+       } else if sk == Slice {
                ss = *(*sliceHeader)(src.ptr)
+       } else {
+               sh := *(*stringHeader)(src.ptr)
+               ss.Data = sh.Data
+               ss.Len = sh.Len
+               ss.Cap = sh.Len
        }
 
        return typedslicecopy(de.common(), ds, ss)