diff options
author | Joe Tsai <joetsai@digital-static.net> | 2021-09-04 13:02:09 -0700 |
---|---|---|
committer | Joseph Tsai <joetsai@digital-static.net> | 2022-03-02 18:06:42 +0000 |
commit | a5b8b56d1d05d186999e4abf1e2147b6aa203ec9 (patch) | |
tree | 78a3ef01c71bcf0a173efd7087208cebee92039b | |
parent | ec4687f337465b719efdeef72b357fa0b05879bb (diff) | |
download | go-a5b8b56d1d05d186999e4abf1e2147b6aa203ec9.tar.gz go-a5b8b56d1d05d186999e4abf1e2147b6aa203ec9.zip |
reflect: allow Value.Bytes on addressable byte arrays
Modify Value.Bytes to be callable addressable byte arrays.
While related, the behavior of Value.SetBytes was not modified.
Fixes #47066
Change-Id: Ic3ba4432353b8da5f33b3188e20034a33b2f6ee8
Reviewed-on: https://go-review.googlesource.com/c/go/+/357331
Trust: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Joseph Tsai <joetsai@digital-static.net>
TryBot-Result: Gopher Robot <gobot@golang.org>
-rw-r--r-- | src/reflect/all_test.go | 29 | ||||
-rw-r--r-- | src/reflect/value.go | 26 |
2 files changed, 47 insertions, 8 deletions
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 866f38e687..5364166eab 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -3682,8 +3682,11 @@ func TestTagGet(t *testing.T) { } func TestBytes(t *testing.T) { - type B []byte - x := B{1, 2, 3, 4} + shouldPanic("on int Value", func() { ValueOf(0).Bytes() }) + shouldPanic("of non-byte slice", func() { ValueOf([]string{}).Bytes() }) + + type S []byte + x := S{1, 2, 3, 4} y := ValueOf(x).Bytes() if !bytes.Equal(x, y) { t.Fatalf("ValueOf(%v).Bytes() = %v", x, y) @@ -3691,6 +3694,28 @@ func TestBytes(t *testing.T) { if &x[0] != &y[0] { t.Errorf("ValueOf(%p).Bytes() = %p", &x[0], &y[0]) } + + type A [4]byte + a := A{1, 2, 3, 4} + shouldPanic("unaddressable", func() { ValueOf(a).Bytes() }) + shouldPanic("on ptr Value", func() { ValueOf(&a).Bytes() }) + b := ValueOf(&a).Elem().Bytes() + if !bytes.Equal(a[:], y) { + t.Fatalf("ValueOf(%v).Bytes() = %v", a, b) + } + if &a[0] != &b[0] { + t.Errorf("ValueOf(%p).Bytes() = %p", &a[0], &b[0]) + } + + // Per issue #24746, it was decided that Bytes can be called on byte slices + // that normally cannot be converted from per Go language semantics. + type B byte + type SB []B + type AB [4]B + ValueOf([]B{1, 2, 3, 4}).Bytes() // should not panic + ValueOf(new([4]B)).Elem().Bytes() // should not panic + ValueOf(SB{1, 2, 3, 4}).Bytes() // should not panic + ValueOf(new(AB)).Elem().Bytes() // should not panic } func TestSetBytes(t *testing.T) { diff --git a/src/reflect/value.go b/src/reflect/value.go index dcc359dae4..89f0253570 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -286,14 +286,28 @@ func (v Value) Bool() bool { } // Bytes returns v's underlying value. -// It panics if v's underlying value is not a slice of bytes. +// It panics if v's underlying value is not a slice of bytes or +// an addressable array of bytes. func (v Value) Bytes() []byte { - v.mustBe(Slice) - if v.typ.Elem().Kind() != Uint8 { - panic("reflect.Value.Bytes of non-byte slice") + switch v.kind() { + case Slice: + if v.typ.Elem().Kind() != Uint8 { + panic("reflect.Value.Bytes of non-byte slice") + } + // Slice is always bigger than a word; assume flagIndir. + return *(*[]byte)(v.ptr) + case Array: + if v.typ.Elem().Kind() != Uint8 { + panic("reflect.Value.Bytes of non-byte array") + } + if !v.CanAddr() { + panic("reflect.Value.Bytes of unaddressable byte array") + } + p := (*byte)(v.ptr) + n := int((*arrayType)(unsafe.Pointer(v.typ)).len) + return unsafe.Slice(p, n) } - // Slice is always bigger than a word; assume flagIndir. - return *(*[]byte)(v.ptr) + panic(&ValueError{"reflect.Value.Bytes", v.kind()}) } // runes returns v's underlying value. |