github.com/go4org/go4@v0.0.0-20200104003542-c7e774b10ea0/reflectutil/swapper_unsafe.go (about) 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 // +build !go1.8 6 // +build !ppc64,!ppc64le,!arm64,!mips,!mipsle,!mips64,!mips64le 7 // +build !js,!appengine,!safe 8 9 package reflectutil 10 11 import ( 12 "reflect" 13 "unsafe" 14 ) 15 16 const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const 17 18 // arrayAt returns the i-th element of p, a C-array whose elements are 19 // eltSize wide (in bytes). 20 func arrayAt(p unsafe.Pointer, i int, eltSize uintptr) unsafe.Pointer { 21 return unsafe.Pointer(uintptr(p) + uintptr(i)*eltSize) 22 } 23 24 type sliceHeader struct { 25 Data unsafe.Pointer 26 Len int 27 Cap int 28 } 29 30 func swapper(v reflect.Value) func(i, j int) { 31 maxLen := uint(v.Len()) 32 33 s := sliceHeader{unsafe.Pointer(v.Pointer()), int(maxLen), int(maxLen)} 34 tt := v.Type() 35 elemt := tt.Elem() 36 typ := unsafe.Pointer(reflect.ValueOf(elemt).Pointer()) 37 38 size := elemt.Size() 39 hasPtr := hasPointers(elemt) 40 41 // Some common & small cases, without using memmove: 42 if hasPtr { 43 if size == ptrSize { 44 var ps []unsafe.Pointer 45 *(*sliceHeader)(unsafe.Pointer(&ps)) = s 46 return func(i, j int) { ps[i], ps[j] = ps[j], ps[i] } 47 } 48 if elemt.Kind() == reflect.String { 49 var ss []string 50 *(*sliceHeader)(unsafe.Pointer(&ss)) = s 51 return func(i, j int) { ss[i], ss[j] = ss[j], ss[i] } 52 } 53 } else { 54 switch size { 55 case 8: 56 var is []int64 57 *(*sliceHeader)(unsafe.Pointer(&is)) = s 58 return func(i, j int) { is[i], is[j] = is[j], is[i] } 59 case 4: 60 var is []int32 61 *(*sliceHeader)(unsafe.Pointer(&is)) = s 62 return func(i, j int) { is[i], is[j] = is[j], is[i] } 63 case 2: 64 var is []int16 65 *(*sliceHeader)(unsafe.Pointer(&is)) = s 66 return func(i, j int) { is[i], is[j] = is[j], is[i] } 67 case 1: 68 var is []int8 69 *(*sliceHeader)(unsafe.Pointer(&is)) = s 70 return func(i, j int) { is[i], is[j] = is[j], is[i] } 71 } 72 } 73 74 // Allocate scratch space for swaps: 75 tmpVal := reflect.New(elemt) 76 tmp := unsafe.Pointer(tmpVal.Pointer()) 77 78 // If no pointers (or Go 1.4 or below), we don't require typedmemmove: 79 if !haveTypedMemmove || !hasPtr { 80 return func(i, j int) { 81 if uint(i) >= maxLen || uint(j) >= maxLen { 82 panic("reflect: slice index out of range") 83 } 84 val1 := arrayAt(s.Data, i, size) 85 val2 := arrayAt(s.Data, j, size) 86 memmove(tmp, val1, size) 87 memmove(val1, val2, size) 88 memmove(val2, tmp, size) 89 } 90 } 91 92 return func(i, j int) { 93 if uint(i) >= maxLen || uint(j) >= maxLen { 94 panic("reflect: slice index out of range") 95 } 96 val1 := arrayAt(s.Data, i, size) 97 val2 := arrayAt(s.Data, j, size) 98 typedmemmove(typ, tmp, val1) 99 typedmemmove(typ, val1, val2) 100 typedmemmove(typ, val2, tmp) 101 } 102 } 103 104 // memmove copies size bytes from src to dst. 105 // The memory must not contain any pointers. 106 //go:noescape 107 func memmove(dst, src unsafe.Pointer, size uintptr)