From 245e386e4c56d3d92843b390871d763392fad26a Mon Sep 17 00:00:00 2001 From: Tim Cooper Date: Thu, 12 Oct 2017 17:42:18 -0300 Subject: [PATCH] reflect: allow Copy to a byte array or byte slice from a string 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 --- src/reflect/all_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ src/reflect/value.go | 21 +++++++++++++++++---- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index efa9fe13d2..0a1a38dd2e 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -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} diff --git a/src/reflect/value.go b/src/reflect/value.go index e9bfe550f4..d3b03e9b02 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -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) -- 2.48.1