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