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)