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

View as plain text