| // 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 reflectlite | |
| import ( | |
| "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 interface{}) 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 == 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) | |
| } | |
| } |