Source file src/reflect/swapper.go

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

View as plain text