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  }