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