reflect: implement AppendSlice

This implementation of AppendSlice simply calls through to the version
used in the runtime: runtime.sliceAppend.
Этот коммит содержится в:
Ayke van Laethem 2021-05-22 18:26:37 +02:00 коммит произвёл Ron Evans
родитель c8742e2b96
коммит 541d8dcd2e
2 изменённых файлов: 87 добавлений и 0 удалений

Просмотреть файл

@ -780,6 +780,9 @@ func memcpy(dst, src unsafe.Pointer, size uintptr)
//go:linkname alloc runtime.alloc
func alloc(size uintptr) unsafe.Pointer
//go:linkname sliceAppend runtime.sliceAppend
func sliceAppend(srcBuf, elemsBuf unsafe.Pointer, srcLen, srcCap, elemsLen uintptr, elemSize uintptr) (unsafe.Pointer, uintptr, uintptr)
// Copy copies the contents of src into dst until either
// dst has been filled or src has been exhausted.
func Copy(dst, src Value) int {
@ -792,6 +795,34 @@ func Append(s Value, x ...Value) Value {
panic("unimplemented: reflect.Append()")
}
// AppendSlice appends a slice t to a slice s and returns the resulting slice.
// The slices s and t must have the same element type.
func AppendSlice(s, t Value) Value {
if s.typecode.Kind() != Slice || t.typecode.Kind() != Slice || s.typecode != t.typecode {
// Not a very helpful error message, but shortened to just one error to
// keep code size down.
panic("reflect.AppendSlice: invalid types")
}
if !s.isExported() || !t.isExported() {
// One of the sides was not exported, so can't access the data.
panic("reflect.AppendSlice: unexported")
}
sSlice := (*sliceHeader)(s.value)
tSlice := (*sliceHeader)(t.value)
elemSize := s.typecode.elem().Size()
ptr, len, cap := sliceAppend(sSlice.data, tSlice.data, sSlice.len, sSlice.cap, tSlice.len, elemSize)
result := &sliceHeader{
data: ptr,
len: len,
cap: cap,
}
return Value{
typecode: s.typecode,
value: unsafe.Pointer(result),
flags: valueFlagExported,
}
}
func (v Value) SetMapIndex(key, elem Value) {
panic("unimplemented: (reflect.Value).SetMapIndex()")
}

56
testdata/reflect.go предоставленный
Просмотреть файл

@ -280,6 +280,8 @@ func main() {
panic("slice was changed while setting part of it")
}
testAppendSlice()
// Test types that are created in reflect and never created elsewhere in a
// value-to-interface conversion.
v := reflect.ValueOf(new(unreferencedType))
@ -409,6 +411,45 @@ func assertSize(ok bool, typ string) {
}
}
// Test whether appending to a slice is equivalent between reflect and native
// slice append.
func testAppendSlice() {
for i := 0; i < 100; i++ {
dst := makeRandomSlice(i)
src := makeRandomSlice(i)
result1 := append(dst, src...)
result2 := reflect.AppendSlice(reflect.ValueOf(dst), reflect.ValueOf(src)).Interface().([]uint32)
if !sliceEqual(result1, result2) {
println("slice: mismatch after runtime.SliceAppend with", len(dst), cap(dst), len(src), cap(src))
}
}
}
func makeRandomSlice(max int) []uint32 {
cap := randuint32() % uint32(max+1)
len := randuint32() % (cap + 1)
s := make([]uint32, len, cap)
for i := uint32(0); i < len; i++ {
s[i] = randuint32()
}
return s
}
func sliceEqual(s1, s2 []uint32) bool {
if len(s1) != len(s2) {
return false
}
for i, val := range s1 {
if s2[i] != val {
return false
}
}
// Note: can't compare cap because the Go implementation has a different
// behavior between the built-in append function and
// reflect.AppendSlice.
return true
}
type unreferencedType int
type totallyUnreferencedType int
@ -427,3 +468,18 @@ func TestStructTag() {
field := st.Field(0)
println(field.Tag.Get("color"), field.Tag.Get("species"))
}
var xorshift32State uint32 = 1
func xorshift32(x uint32) uint32 {
// Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs"
x ^= x << 13
x ^= x >> 17
x ^= x << 5
return x
}
func randuint32() uint32 {
xorshift32State = xorshift32(xorshift32State)
return xorshift32State
}