-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
Proposal Details
I propose the addition of:
// IndexPointer returns the index of the element in s that p points to.
// It returns -1 if p is not found in s.
func IndexPointer[S ~[]E, E any](s S, p *E) intA use of this is to check whether some slice is a subslice of another slice, by checking:
len(subslice) > 0 && slices.IndexPointer(s, &subslice[0]) >= 0
Such an operation is useful:
- As an optimization, where knowing that one slice is a directly subslice of another allows you to skip certain operations
- As a means of precondition error checking where an operation will produce invalid results if one argument is a subslice of another. For example,
AppendFoo(dst, src)is will produce corrupt data ifsrcis a subslice ofdstand the operation writes more bytes todstthan it reads fromsrc. Such bugs are difficult to track down and ideally the API can return an error by detecting such situations (or just clone thesrcfor a slower but correct implementation).
An alternative API is:
func ContainsSubslice[S ~[]E, E any](s S, subslice S) boolbut:
- This is easy to hold wrong since both
sandsubsliceare of typeS. - It has weird edge cases where
sandsubsliceoverlap, but neither is subslice of the other. - Returning a
boolis strictly less flexible than returning an index. - The naming isn't clear what the definition of "Contains" means. Is it comparing the value of the elements? The naming of
IndexPointeris more clear about it's meaning.
Implementation
The naive implementation is straight forward:
func IndexPointer[S ~[]E, E any](s S, p *E) int {
for i := range s {
if p == &s[i] {
return i
}
}
return -1
}but is unfortunately non-performant as it performs an O(n) search over the slice.
A more efficient implementation takes advantage of pointer arithmetic:
func IndexPointer[S ~[]E, E any](s S, p *E) int {
i := 0
if p != nil && unsafe.Sizeof(*p) > 0 {
pd := unsafe.Pointer(unsafe.SliceData(s))
pe := unsafe.Pointer(p)
i = int((uintptr(pe) - uintptr(pd)) / unsafe.Sizeof(*p))
}
if uint(i) < uint(len(s)) && p == &s[i] {
return i
}
return -1
}which can now compute the result in O(1).
Without the helper function in "slices", it is currently impossible in Go to identify whether a given slice is a sub-slice of another slice in O(1) without the use of "unsafe". By including this helper in the "slices" package, callers can avoid the use of "unsafe" to perform this operation.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status