github.com/mitranim/gg@v0.1.17/unsafe.go (about) 1 package gg 2 3 import ( 4 r "reflect" 5 u "unsafe" 6 ) 7 8 /* 9 Amount of bytes in `uintptr`. At the time of writing, in Go 1.20, this is also 10 the amount of bytes in `int` and `uint`. 11 */ 12 const SizeofWord = u.Sizeof(uintptr(0)) 13 14 /* 15 Amount of bytes in any value of type `~string`. Note that this is the size of 16 the string header, not of the underlying data. 17 */ 18 const SizeofString = u.Sizeof(``) 19 20 /* 21 Amount of bytes in any slice header, for example of type `[]byte`. Note that the 22 size of a slice header is constant and does not reflect the size of the 23 underlying data. 24 */ 25 const SizeofSlice = u.Sizeof([]byte(nil)) 26 27 /* 28 Amount of bytes in our own `SliceHeader`. In the official Go implementation 29 (version 1.20 at the time of writing), this is equal to `SizeofSlice`. 30 In case of mismatch, using `SliceHeader` for anything is invalid. 31 */ 32 const SizeofSliceHeader = u.Sizeof(SliceHeader{}) 33 34 /* 35 Returns `unsafe.Sizeof` for the given type. Equivalent to `reflect.Type.Size` 36 for the same type. Due to Go's limitations, the result is not a constant, thus 37 you should prefer direct use of `unsafe.Sizeof` which returns a constant. 38 */ 39 func Size[A any]() uintptr { return u.Sizeof(Zero[A]()) } 40 41 /* 42 Memory representation of an arbitrary Go slice. Same as `reflect.SliceHeader` 43 but with `unsafe.Pointer` instead of `uintptr`. 44 */ 45 type SliceHeader struct { 46 Dat u.Pointer 47 Len int 48 Cap int 49 } 50 51 /* 52 Takes a regular slice header and converts it to its underlying representation 53 `SliceHeader`. 54 */ 55 func SliceHeaderOf[A any](src []A) SliceHeader { 56 return CastUnsafe[SliceHeader](src) 57 } 58 59 /* 60 Dangerous tool for performance fine-tuning. Converts the given pointer to 61 `unsafe.Pointer` and tricks the compiler into thinking that the memory 62 underlying the pointer should not be moved to the heap. Can negate failures of 63 Go escape analysis, but can also introduce tricky bugs. The caller MUST ensure 64 that the original is not freed while the resulting pointer is still in use. 65 */ 66 func PtrNoEscUnsafe[A any](val *A) u.Pointer { return noescape(u.Pointer(val)) } 67 68 // Dangerous tool for performance fine-tuning. 69 func NoEscUnsafe[A any](val A) A { return *(*A)(PtrNoEscUnsafe(&val)) } 70 71 // Dangerous tool for performance fine-tuning. 72 func AnyNoEscUnsafe(src any) any { return NoEscUnsafe(src) } 73 74 /* 75 Self-explanatory. Slightly cleaner and less error prone than direct use of 76 unsafe pointers. 77 */ 78 func CastUnsafe[Out, Src any](val Src) Out { return *(*Out)(u.Pointer(&val)) } 79 80 /* 81 Same as `CastUnsafe` but with additional validation: `unsafe.Sizeof` must be the 82 same for both types, otherwise this panics. 83 */ 84 func Cast[Out, Src any](src Src) Out { 85 out := CastUnsafe[Out](src) 86 srcSize := u.Sizeof(src) 87 outSize := u.Sizeof(out) 88 if srcSize == outSize { 89 return out 90 } 91 panic(errSizeMismatch(Type[Src](), srcSize, Type[Out](), outSize)) 92 } 93 94 func errSizeMismatch(src r.Type, srcSize uintptr, out r.Type, outSize uintptr) Err { 95 return Errf( 96 `size mismatch: %v (size %v) vs %v (size %v)`, 97 src, srcSize, out, outSize, 98 ) 99 } 100 101 /* 102 Similar to `CastUnsafe` between two slice types but with additional validation: 103 `unsafe.Sizeof` must be the same for both element types, otherwise this 104 panics. 105 */ 106 func CastSlice[Out, Src any](src []Src) []Out { 107 srcSize := Size[Src]() 108 outSize := Size[Out]() 109 if srcSize == outSize { 110 return CastUnsafe[[]Out](src) 111 } 112 panic(errSizeMismatch(Type[Src](), srcSize, Type[Out](), outSize)) 113 } 114 115 /* 116 Reinterprets existing memory as a byte slice. The resulting byte slice is backed 117 by the given pointer. Mutations of the resulting slice are reflected in the 118 source memory. Length and capacity are equal to the size of the referenced 119 memory. If the pointer is nil, the output is nil. 120 */ 121 func AsBytes[A any](tar *A) []byte { 122 if tar == nil { 123 return nil 124 } 125 return u.Slice(CastUnsafe[*byte](tar), Size[A]()) 126 }