1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package reflect
6
7import (
8	"internal/abi"
9	"internal/goarch"
10	"internal/unsafeheader"
11	"unsafe"
12)
13
14// Swapper returns a function that swaps the elements in the provided
15// slice.
16//
17// Swapper panics if the provided interface is not a slice.
18func Swapper(slice any) func(i, j int) {
19	v := ValueOf(slice)
20	if v.Kind() != Slice {
21		panic(&ValueError{Method: "Swapper", Kind: v.Kind()})
22	}
23	// Fast path for slices of size 0 and 1. Nothing to swap.
24	switch v.Len() {
25	case 0:
26		return func(i, j int) { panic("reflect: slice index out of range") }
27	case 1:
28		return func(i, j int) {
29			if i != 0 || j != 0 {
30				panic("reflect: slice index out of range")
31			}
32		}
33	}
34
35	typ := v.Type().Elem().common()
36	size := typ.Size()
37	hasPtr := typ.Pointers()
38
39	// Some common & small cases, without using memmove:
40	if hasPtr {
41		if size == goarch.PtrSize {
42			ps := *(*[]unsafe.Pointer)(v.ptr)
43			return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
44		}
45		if typ.Kind() == abi.String {
46			ss := *(*[]string)(v.ptr)
47			return func(i, j int) { ss[i], ss[j] = ss[j], ss[i] }
48		}
49	} else {
50		switch size {
51		case 8:
52			is := *(*[]int64)(v.ptr)
53			return func(i, j int) { is[i], is[j] = is[j], is[i] }
54		case 4:
55			is := *(*[]int32)(v.ptr)
56			return func(i, j int) { is[i], is[j] = is[j], is[i] }
57		case 2:
58			is := *(*[]int16)(v.ptr)
59			return func(i, j int) { is[i], is[j] = is[j], is[i] }
60		case 1:
61			is := *(*[]int8)(v.ptr)
62			return func(i, j int) { is[i], is[j] = is[j], is[i] }
63		}
64	}
65
66	s := (*unsafeheader.Slice)(v.ptr)
67	tmp := unsafe_New(typ) // swap scratch space
68
69	return func(i, j int) {
70		if uint(i) >= uint(s.Len) || uint(j) >= uint(s.Len) {
71			panic("reflect: slice index out of range")
72		}
73		val1 := arrayAt(s.Data, i, size, "i < s.Len")
74		val2 := arrayAt(s.Data, j, size, "j < s.Len")
75		typedmemmove(typ, tmp, val1)
76		typedmemmove(typ, val1, val2)
77		typedmemmove(typ, val2, tmp)
78	}
79}
80