github.com/parquet-go/parquet-go@v0.20.0/internal/unsafecast/unsafecast.go (about) 1 // Package unsafecast exposes functions to bypass the Go type system and perform 2 // conversions between types that would otherwise not be possible. 3 // 4 // The functions of this package are mostly useful as optimizations to avoid 5 // memory copies when converting between compatible memory layouts; for example, 6 // casting a [][16]byte to a []byte in order to use functions of the standard 7 // bytes package on the slices. 8 // 9 // With great power comes great responsibility. 10 package unsafecast 11 12 import ( 13 "reflect" 14 "unsafe" 15 ) 16 17 // AddressOf returns the address to the first element in data, even if the slice 18 // has length zero. 19 func AddressOf[T any](data []T) *T { 20 return *(**T)(unsafe.Pointer(&data)) 21 } 22 23 // AddressOfBytes returns the address of the first byte in data. 24 func AddressOfBytes(data []byte) *byte { 25 return *(**byte)(unsafe.Pointer(&data)) 26 } 27 28 // AddressOfString returns the address of the first byte in data. 29 func AddressOfString(data string) *byte { 30 return *(**byte)(unsafe.Pointer(&data)) 31 } 32 33 // PointerOf is like AddressOf but returns an unsafe.Pointer, losing type 34 // information about the underlying data. 35 func PointerOf[T any](data []T) unsafe.Pointer { 36 return unsafe.Pointer(AddressOf(data)) 37 } 38 39 // PointerOfString is like AddressOfString but returns an unsafe.Pointer, losing 40 // type information about the underlying data. 41 func PointerOfString(data string) unsafe.Pointer { 42 return unsafe.Pointer(AddressOfString(data)) 43 } 44 45 // PointerOfValue returns the address of the object packed in the given value. 46 // 47 // This function is like value.UnsafePointer but works for any underlying type, 48 // bypassing the safety checks done by the reflect package. 49 func PointerOfValue(value reflect.Value) unsafe.Pointer { 50 return (*[2]unsafe.Pointer)(unsafe.Pointer(&value))[1] 51 } 52 53 // The slice type represents the memory layout of slices in Go. It is similar to 54 // reflect.SliceHeader but uses a unsafe.Pointer instead of uintptr to for the 55 // backing array to allow the garbage collector to track track the reference. 56 type slice struct { 57 ptr unsafe.Pointer 58 len int 59 cap int 60 } 61 62 // Slice converts the data slice of type []From to a slice of type []To sharing 63 // the same backing array. The length and capacity of the returned slice are 64 // scaled according to the size difference between the source and destination 65 // types. 66 // 67 // Note that the function does not perform any checks to ensure that the memory 68 // layouts of the types are compatible, it is possible to cause memory 69 // corruption if the layouts mismatch (e.g. the pointers in the From are different 70 // than the pointers in To). 71 func Slice[To, From any](data []From) []To { 72 // This function could use unsafe.Slice but it would drop the capacity 73 // information, so instead we implement the type conversion. 74 var zf From 75 var zt To 76 var s = (*slice)(unsafe.Pointer(&data)) 77 s.len = int((uintptr(s.len) * unsafe.Sizeof(zf)) / unsafe.Sizeof(zt)) 78 s.cap = int((uintptr(s.cap) * unsafe.Sizeof(zf)) / unsafe.Sizeof(zt)) 79 return *(*[]To)(unsafe.Pointer(s)) 80 } 81 82 // Bytes constructs a byte slice. The pointer to the first element of the slice 83 // is set to data, the length and capacity are set to size. 84 func Bytes(data *byte, size int) []byte { 85 return *(*[]byte)(unsafe.Pointer(&slice{ 86 ptr: unsafe.Pointer(data), 87 len: size, 88 cap: size, 89 })) 90 } 91 92 // BytesToString converts a byte slice to a string value. The returned string 93 // shares the backing array of the byte slice. 94 // 95 // Programs using this function are responsible for ensuring that the data slice 96 // is not modified while the returned string is in use, otherwise the guarantee 97 // of immutability of Go string values will be violated, resulting in undefined 98 // behavior. 99 func BytesToString(data []byte) string { 100 return *(*string)(unsafe.Pointer(&data)) 101 } 102 103 // StringToBytes applies the inverse conversion of BytesToString. 104 func StringToBytes(data string) []byte { 105 return *(*[]byte)(unsafe.Pointer(&slice{ 106 ptr: PointerOfString(data), 107 len: len(data), 108 cap: len(data), 109 })) 110 } 111 112 // ----------------------------------------------------------------------------- 113 // TODO: the functions below are used for backward compatibility with Go 1.17 114 // where generics weren't available. We should remove them and inline calls to 115 // unsafecast.Slice when we change our minimum supported Go version to 1.18. 116 // ----------------------------------------------------------------------------- 117 118 func BoolToBytes(data []bool) []byte { return Slice[byte](data) } 119 120 func Int8ToBytes(data []int8) []byte { return Slice[byte](data) } 121 122 func Int16ToBytes(data []int16) []byte { return Slice[byte](data) } 123 124 func Int32ToBytes(data []int32) []byte { return Slice[byte](data) } 125 126 func Int64ToBytes(data []int64) []byte { return Slice[byte](data) } 127 128 func Float32ToBytes(data []float32) []byte { return Slice[byte](data) } 129 130 func Float64ToBytes(data []float64) []byte { return Slice[byte](data) } 131 132 func Uint32ToBytes(data []uint32) []byte { return Slice[byte](data) } 133 134 func Uint64ToBytes(data []uint64) []byte { return Slice[byte](data) } 135 136 func Uint128ToBytes(data [][16]byte) []byte { return Slice[byte](data) } 137 138 func Int16ToUint16(data []int16) []uint16 { return Slice[uint16](data) } 139 140 func Int32ToUint32(data []int32) []uint32 { return Slice[uint32](data) } 141 142 func Int64ToUint64(data []int64) []uint64 { return Slice[uint64](data) } 143 144 func Float32ToUint32(data []float32) []uint32 { return Slice[uint32](data) } 145 146 func Float64ToUint64(data []float64) []uint64 { return Slice[uint64](data) } 147 148 func Uint32ToInt32(data []uint32) []int32 { return Slice[int32](data) } 149 150 func Uint32ToInt64(data []uint32) []int64 { return Slice[int64](data) } 151 152 func Uint64ToInt64(data []uint64) []int64 { return Slice[int64](data) } 153 154 func BytesToBool(data []byte) []bool { return Slice[bool](data) } 155 156 func BytesToInt8(data []byte) []int8 { return Slice[int8](data) } 157 158 func BytesToInt16(data []byte) []int16 { return Slice[int16](data) } 159 160 func BytesToInt32(data []byte) []int32 { return Slice[int32](data) } 161 162 func BytesToInt64(data []byte) []int64 { return Slice[int64](data) } 163 164 func BytesToUint32(data []byte) []uint32 { return Slice[uint32](data) } 165 166 func BytesToUint64(data []byte) []uint64 { return Slice[uint64](data) } 167 168 func BytesToUint128(data []byte) [][16]byte { return Slice[[16]byte](data) } 169 170 func BytesToFloat32(data []byte) []float32 { return Slice[float32](data) } 171 172 func BytesToFloat64(data []byte) []float64 { return Slice[float64](data) }