Skip to content

Commit ca04091

Browse files
committed
reflect: add Swapper func
Swapper returns a func that swaps two elements in a slice. Updates #16721 Change-Id: I7f2287a675c10a05019e02b7d62fb870af31216f Reviewed-on: https://go-review.googlesource.com/30088 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 9491e7d commit ca04091

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

src/reflect/all_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5780,3 +5780,84 @@ func BenchmarkNew(b *testing.B) {
57805780
New(v)
57815781
}
57825782
}
5783+
5784+
func TestSwapper(t *testing.T) {
5785+
type I int
5786+
var a, b, c I
5787+
type pair struct {
5788+
x, y int
5789+
}
5790+
type pairPtr struct {
5791+
x, y int
5792+
p *I
5793+
}
5794+
type S string
5795+
5796+
tests := []struct {
5797+
in interface{}
5798+
i, j int
5799+
want interface{}
5800+
}{
5801+
{
5802+
in: []int{1, 20, 300},
5803+
i: 0,
5804+
j: 2,
5805+
want: []int{300, 20, 1},
5806+
},
5807+
{
5808+
in: []uintptr{1, 20, 300},
5809+
i: 0,
5810+
j: 2,
5811+
want: []uintptr{300, 20, 1},
5812+
},
5813+
{
5814+
in: []int16{1, 20, 300},
5815+
i: 0,
5816+
j: 2,
5817+
want: []int16{300, 20, 1},
5818+
},
5819+
{
5820+
in: []int8{1, 20, 100},
5821+
i: 0,
5822+
j: 2,
5823+
want: []int8{100, 20, 1},
5824+
},
5825+
{
5826+
in: []*I{&a, &b, &c},
5827+
i: 0,
5828+
j: 2,
5829+
want: []*I{&c, &b, &a},
5830+
},
5831+
{
5832+
in: []string{"eric", "sergey", "larry"},
5833+
i: 0,
5834+
j: 2,
5835+
want: []string{"larry", "sergey", "eric"},
5836+
},
5837+
{
5838+
in: []S{"eric", "sergey", "larry"},
5839+
i: 0,
5840+
j: 2,
5841+
want: []S{"larry", "sergey", "eric"},
5842+
},
5843+
{
5844+
in: []pair{{1, 2}, {3, 4}, {5, 6}},
5845+
i: 0,
5846+
j: 2,
5847+
want: []pair{{5, 6}, {3, 4}, {1, 2}},
5848+
},
5849+
{
5850+
in: []pairPtr{{1, 2, &a}, {3, 4, &b}, {5, 6, &c}},
5851+
i: 0,
5852+
j: 2,
5853+
want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}},
5854+
},
5855+
}
5856+
for i, tt := range tests {
5857+
inStr := fmt.Sprint(tt.in)
5858+
Swapper(tt.in)(tt.i, tt.j)
5859+
if !DeepEqual(tt.in, tt.want) {
5860+
t.Errorf("%d. swapping %v and %v of %v = %v; want %v", i, tt.i, tt.j, inStr, tt.in, tt.want)
5861+
}
5862+
}
5863+
}

src/reflect/swapper.go

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

0 commit comments

Comments
 (0)