| // 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/abi" |
| "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().common() |
| size := typ.Size() |
| hasPtr := typ.PtrBytes != 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() == abi.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) |
| } |
| } |