github.com/grailbio/base@v0.0.11/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.