github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/simd/multibyte_generic.go (about)

     1  // Copyright 2021 GRAIL, Inc.  All rights reserved.
     2  // Use of this source code is governed by the Apache-2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build !amd64 && !appengine
     6  // +build !amd64,!appengine
     7  
     8  package simd
     9  
    10  import (
    11  	"reflect"
    12  	"unsafe"
    13  )
    14  
    15  // This file contains functions which operate on slices of 2- or 4-byte
    16  // elements (typically small structs or integers) in ways that differ from the
    17  // corresponding operations on single-byte elements.
    18  // In this context, there is little point in making the interface based on
    19  // []byte, since the caller will need to unsafely cast to it.  Instead, most
    20  // functions take unsafe.Pointer(s) and a count, and have names ending in
    21  // 'Raw'; the caller should write safe wrappers around them when appropriate.
    22  // We provide sample wrappers for the int16 and uint16 cases.  (Originally did
    23  // this for int32/uint32, but turns out the compiler has hardcoded
    24  // optimizations for those cases which are currently missing for {u}int16.)
    25  
    26  // Memset16Raw assumes dst points to an array of nElem 2-byte elements, and
    27  // valPtr points to a single 2-byte element.  It fills dst with copies of
    28  // *valPtr.
    29  func Memset16Raw(dst, valPtr unsafe.Pointer, nElem int) {
    30  	val := *((*uint16)(valPtr))
    31  	for idx := 0; idx != nElem; idx++ {
    32  		*((*uint16)(dst)) = val
    33  		dst = unsafe.Add(dst, 2)
    34  	}
    35  }
    36  
    37  // Memset32Raw assumes dst points to an array of nElem 4-byte elements, and
    38  // valPtr points to a single 4-byte element.  It fills dst with copies of
    39  // *valPtr.
    40  func Memset32Raw(dst, valPtr unsafe.Pointer, nElem int) {
    41  	val := *((*uint32)(valPtr))
    42  	for idx := 0; idx != nElem; idx++ {
    43  		*((*uint32)(dst)) = val
    44  		dst = unsafe.Add(dst, 4)
    45  	}
    46  }
    47  
    48  // RepeatI16 fills dst[] with the given int16.
    49  func RepeatI16(dst []int16, val int16) {
    50  	dstHeader := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
    51  	Memset16Raw(unsafe.Pointer(dstHeader.Data), unsafe.Pointer(&val), dstHeader.Len)
    52  }
    53  
    54  // RepeatU16 fills dst[] with the given uint16.
    55  func RepeatU16(dst []uint16, val uint16) {
    56  	dstHeader := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
    57  	Memset16Raw(unsafe.Pointer(dstHeader.Data), unsafe.Pointer(&val), dstHeader.Len)
    58  }
    59  
    60  // IndexU16 returns the index of the first instance of val in main, or -1 if
    61  // val is not present in main.
    62  func IndexU16(main []uint16, val uint16) int {
    63  	for i, v := range main {
    64  		if v == val {
    65  			return i
    66  		}
    67  	}
    68  	return -1
    69  }
    70  
    71  // (Add a function which has the original little-endian byte-slice semantics if
    72  // we ever need it.)
    73  
    74  // Reverse16InplaceRaw assumes main points to an array of ct 2-byte elements,
    75  // and reverses it in-place.
    76  func Reverse16InplaceRaw(main unsafe.Pointer, nElem int) {
    77  	nElemDiv2 := nElem >> 1
    78  	fwdIter := main
    79  	revIter := unsafe.Add(main, (nElem-1)*2)
    80  	for idx := 0; idx != nElemDiv2; idx++ {
    81  		origLeftVal := *((*uint16)(fwdIter))
    82  		*((*uint16)(fwdIter)) = *((*uint16)(revIter))
    83  		*((*uint16)(revIter)) = origLeftVal
    84  		fwdIter = unsafe.Add(fwdIter, 2)
    85  		revIter = unsafe.Add(revIter, -2)
    86  	}
    87  }
    88  
    89  // Reverse16Raw assumes dst and src both point to arrays of ct 2-byte elements,
    90  // and sets dst[pos] := src[ct - 1 - pos] for each position.
    91  func Reverse16Raw(dst, src unsafe.Pointer, nElem int) {
    92  	srcIter := unsafe.Add(src, (nElem-1)*2)
    93  	dstIter := dst
    94  	for idx := 0; idx != nElem; idx++ {
    95  		*((*uint16)(dstIter)) = *((*uint16)(srcIter))
    96  		srcIter = unsafe.Add(srcIter, -2)
    97  		dstIter = unsafe.Add(dstIter, 2)
    98  	}
    99  }
   100  
   101  // ReverseI16Inplace reverses a []int16 in-place.
   102  func ReverseI16Inplace(main []int16) {
   103  	mainHeader := (*reflect.SliceHeader)(unsafe.Pointer(&main))
   104  	Reverse16InplaceRaw(unsafe.Pointer(mainHeader.Data), mainHeader.Len)
   105  }
   106  
   107  // ReverseU16Inplace reverses a []uint16 in-place.
   108  func ReverseU16Inplace(main []uint16) {
   109  	mainHeader := (*reflect.SliceHeader)(unsafe.Pointer(&main))
   110  	Reverse16InplaceRaw(unsafe.Pointer(mainHeader.Data), mainHeader.Len)
   111  }
   112  
   113  // ReverseI16 sets dst[len(src) - 1 - pos] := src[pos] for each position in
   114  // src.  It panics if len(src) != len(dst).
   115  func ReverseI16(dst, src []int16) {
   116  	srcHeader := (*reflect.SliceHeader)(unsafe.Pointer(&src))
   117  	dstHeader := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
   118  	nElem := srcHeader.Len
   119  	if nElem != dstHeader.Len {
   120  		panic("ReverseI16() requires len(src) == len(dst).")
   121  	}
   122  	Reverse16Raw(unsafe.Pointer(dstHeader.Data), unsafe.Pointer(srcHeader.Data), nElem)
   123  }
   124  
   125  // ReverseU16 sets dst[len(src) - 1 - pos] := src[pos] for each position in
   126  // src.  It panics if len(src) != len(dst).
   127  func ReverseU16(dst, src []uint16) {
   128  	srcHeader := (*reflect.SliceHeader)(unsafe.Pointer(&src))
   129  	dstHeader := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
   130  	nElem := srcHeader.Len
   131  	if nElem != dstHeader.Len {
   132  		panic("ReverseU16() requires len(src) == len(dst).")
   133  	}
   134  	Reverse16Raw(unsafe.Pointer(dstHeader.Data), unsafe.Pointer(srcHeader.Data), nElem)
   135  }
   136  
   137  // Benchmark results suggest that Reverse32Raw is unimportant.