github.com/parquet-go/parquet-go@v0.21.1-0.20240501160520-b3c3a0c3ed6f/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) }