From: Brad Fitzpatrick Date: Fri, 30 Sep 2016 18:12:37 +0000 (+0000) Subject: reflect: add Swapper func X-Git-Tag: go1.8beta1~1092 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ca04091f5be4134291142cc4e7e577d0f627e788;p=gostls13.git 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 TryBot-Result: Gobot Gobot Reviewed-by: Russ Cox --- diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 3bf24d2250..1f2c752784 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -5780,3 +5780,84 @@ func BenchmarkNew(b *testing.B) { New(v) } } + +func TestSwapper(t *testing.T) { + type I int + var a, b, c I + type pair struct { + x, y int + } + type pairPtr struct { + x, y int + p *I + } + type S string + + tests := []struct { + in interface{} + i, j int + want interface{} + }{ + { + in: []int{1, 20, 300}, + i: 0, + j: 2, + want: []int{300, 20, 1}, + }, + { + in: []uintptr{1, 20, 300}, + i: 0, + j: 2, + want: []uintptr{300, 20, 1}, + }, + { + in: []int16{1, 20, 300}, + i: 0, + j: 2, + want: []int16{300, 20, 1}, + }, + { + in: []int8{1, 20, 100}, + i: 0, + j: 2, + want: []int8{100, 20, 1}, + }, + { + in: []*I{&a, &b, &c}, + i: 0, + j: 2, + want: []*I{&c, &b, &a}, + }, + { + in: []string{"eric", "sergey", "larry"}, + i: 0, + j: 2, + want: []string{"larry", "sergey", "eric"}, + }, + { + in: []S{"eric", "sergey", "larry"}, + i: 0, + j: 2, + want: []S{"larry", "sergey", "eric"}, + }, + { + in: []pair{{1, 2}, {3, 4}, {5, 6}}, + i: 0, + j: 2, + want: []pair{{5, 6}, {3, 4}, {1, 2}}, + }, + { + in: []pairPtr{{1, 2, &a}, {3, 4, &b}, {5, 6, &c}}, + i: 0, + j: 2, + want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}}, + }, + } + for i, tt := range tests { + inStr := fmt.Sprint(tt.in) + Swapper(tt.in)(tt.i, tt.j) + if !DeepEqual(tt.in, tt.want) { + t.Errorf("%d. swapping %v and %v of %v = %v; want %v", i, tt.i, tt.j, inStr, tt.in, tt.want) + } + } +} diff --git a/src/reflect/swapper.go b/src/reflect/swapper.go new file mode 100644 index 0000000000..5441cb0315 --- /dev/null +++ b/src/reflect/swapper.go @@ -0,0 +1,74 @@ +// 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 "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.kind&kindNoPointers == 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 := (*sliceHeader)(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) + val2 := arrayAt(s.Data, j, size) + typedmemmove(typ, tmp, val1) + typedmemmove(typ, val1, val2) + typedmemmove(typ, val2, tmp) + } +}