78 lines
2 KiB
Go
78 lines
2 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package reflect
|
|
|
|
import (
|
|
"internal/goarch"
|
|
"internal/unsafeheader"
|
|
"unsafe"
|
|
)
|
|
|
|
// Swapper returns a function that swaps the elements in the provided
|
|
// slice.
|
|
//
|
|
// Swapper panics if the provided interface is not a slice.
|
|
func Swapper(slice any) func(i, j int) {
|
|
v := ValueOf(slice)
|
|
if v.Kind() != Slice {
|
|
panic(&ValueError{Method: "Swapper", Kind: v.Kind()})
|
|
}
|
|
// Fast path for slices of size 0 and 1. Nothing to swap.
|
|
switch v.Len() {
|
|
case 0:
|
|
return func(i, j int) { panic("reflect: slice index out of range") }
|
|
case 1:
|
|
return func(i, j int) {
|
|
if i != 0 || j != 0 {
|
|
panic("reflect: slice index out of range")
|
|
}
|
|
}
|
|
}
|
|
|
|
typ := v.Type().Elem().(*rtype)
|
|
size := typ.Size()
|
|
hasPtr := typ.ptrdata != 0
|
|
|
|
// Some common & small cases, without using memmove:
|
|
if hasPtr {
|
|
if size == goarch.PtrSize {
|
|
ps := *(*[]unsafe.Pointer)(v.ptr)
|
|
return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
|
|
}
|
|
if typ.Kind() == String {
|
|
ss := *(*[]string)(v.ptr)
|
|
return func(i, j int) { ss[i], ss[j] = ss[j], ss[i] }
|
|
}
|
|
} else {
|
|
switch size {
|
|
case 8:
|
|
is := *(*[]int64)(v.ptr)
|
|
return func(i, j int) { is[i], is[j] = is[j], is[i] }
|
|
case 4:
|
|
is := *(*[]int32)(v.ptr)
|
|
return func(i, j int) { is[i], is[j] = is[j], is[i] }
|
|
case 2:
|
|
is := *(*[]int16)(v.ptr)
|
|
return func(i, j int) { is[i], is[j] = is[j], is[i] }
|
|
case 1:
|
|
is := *(*[]int8)(v.ptr)
|
|
return func(i, j int) { is[i], is[j] = is[j], is[i] }
|
|
}
|
|
}
|
|
|
|
s := (*unsafeheader.Slice)(v.ptr)
|
|
tmp := unsafe_New(typ) // swap scratch space
|
|
|
|
return func(i, j int) {
|
|
if uint(i) >= uint(s.Len) || uint(j) >= uint(s.Len) {
|
|
panic("reflect: slice index out of range")
|
|
}
|
|
val1 := arrayAt(s.Data, i, size, "i < s.Len")
|
|
val2 := arrayAt(s.Data, j, size, "j < s.Len")
|
|
typedmemmove(typ, tmp, val1)
|
|
typedmemmove(typ, val1, val2)
|
|
typedmemmove(typ, val2, tmp)
|
|
}
|
|
}
|