Skip to content

Commit

Permalink
all: add reflect.SliceAt function
Browse files Browse the repository at this point in the history
Fixes #61308

Change-Id: Ic17d737fda055a60779985d5da497745c80d5cfa
Reviewed-on: https://go-review.googlesource.com/c/go/+/516597
LUCI-TryBot-Result: Go LUCI <[email protected]>
Auto-Submit: Cuong Manh Le <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
Reviewed-by: Than McIntosh <[email protected]>
  • Loading branch information
cuonglm authored and gopherbot committed Apr 2, 2024
1 parent 3d61f24 commit d08a957
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 0 deletions.
1 change: 1 addition & 0 deletions api/next/61308.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pkg reflect, func SliceAt(Type, unsafe.Pointer, int) Value #61308
3 changes: 3 additions & 0 deletions doc/next/6-stdlib/99-minor/reflect/61308.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The [`SliceAt(typ Type, p unsafe.Pointer, len int)`](/pkg/reflect#SliceAt) function
returns a Value representing a slice whose underlying array starts at p and whose
length and capacity are len.
41 changes: 41 additions & 0 deletions src/reflect/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8548,3 +8548,44 @@ func TestValuePointerAndUnsafePointer(t *testing.T) {
})
}
}

// Test cases copied from ../../test/unsafebuiltins.go
func TestSliceAt(t *testing.T) {
const maxUintptr = 1 << (8 * unsafe.Sizeof(uintptr(0)))
var p [10]byte

typ := TypeOf(p[0])

s := SliceAt(typ, unsafe.Pointer(&p[0]), len(p))
if s.Pointer() != uintptr(unsafe.Pointer(&p[0])) {
t.Fatalf("unexpected underlying array: %d, want: %d", s.Pointer(), uintptr(unsafe.Pointer(&p[0])))
}
if s.Len() != len(p) || s.Cap() != len(p) {
t.Fatalf("unexpected len or cap, len: %d, cap: %d, want: %d", s.Len(), s.Cap(), len(p))
}

typ = TypeOf(0)
if !SliceAt(typ, unsafe.Pointer((*int)(nil)), 0).IsNil() {
t.Fatal("nil pointer with zero length must return nil")
}

// nil pointer with positive length panics
shouldPanic("", func() { _ = SliceAt(typ, unsafe.Pointer((*int)(nil)), 1) })

// negative length
var neg int = -1
shouldPanic("", func() { _ = SliceAt(TypeOf(byte(0)), unsafe.Pointer(&p[0]), neg) })

// size overflows address space
n := uint64(0)
shouldPanic("", func() { _ = SliceAt(TypeOf(n), unsafe.Pointer(&n), maxUintptr/8) })
shouldPanic("", func() { _ = SliceAt(TypeOf(n), unsafe.Pointer(&n), maxUintptr/8+1) })

// sliced memory overflows address space
last := (*byte)(unsafe.Pointer(^uintptr(0)))
// This panics here, but won't panic in ../../test/unsafebuiltins.go,
// because unsafe.Slice(last, 1) does not escape.
//
// _ = SliceAt(typ, unsafe.Pointer(last), 1)
shouldPanic("", func() { _ = SliceAt(typ, unsafe.Pointer(last), 2) })
}
13 changes: 13 additions & 0 deletions src/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -3211,6 +3211,16 @@ func MakeSlice(typ Type, len, cap int) Value {
return Value{&typ.(*rtype).t, unsafe.Pointer(&s), flagIndir | flag(Slice)}
}

// SliceAt returns a [Value] representing a slice whose underlying
// data starts at p, with length and capacity equal to n.
//
// This is like [unsafe.Slice].
func SliceAt(typ Type, p unsafe.Pointer, n int) Value {
unsafeslice(typ.common(), p, n)
s := unsafeheader.Slice{Data: p, Len: n, Cap: n}
return Value{SliceOf(typ).common(), unsafe.Pointer(&s), flagIndir | flag(Slice)}
}

// MakeChan creates a new channel with the specified type and buffer size.
func MakeChan(typ Type, buffer int) Value {
if typ.Kind() != Chan {
Expand Down Expand Up @@ -3978,6 +3988,9 @@ func verifyNotInHeapPtr(p uintptr) bool
//go:noescape
func growslice(t *abi.Type, old unsafeheader.Slice, num int) unsafeheader.Slice

//go:noescape
func unsafeslice(t *abi.Type, ptr unsafe.Pointer, len int)

// Dummy annotation marking that the value x escapes,
// for use in cases where the reflect code is so clever that
// the compiler cannot follow.
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/unsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,8 @@ func panicunsafeslicenilptr1(pc uintptr) {
panicCheck1(pc, "unsafe.Slice: ptr is nil and len is not zero")
panic(errorString("unsafe.Slice: ptr is nil and len is not zero"))
}

//go:linkname reflect_unsafeslice reflect.unsafeslice
func reflect_unsafeslice(et *_type, ptr unsafe.Pointer, len int) {
unsafeslice(et, ptr, len)
}

0 comments on commit d08a957

Please sign in to comment.