Skip to content

Instantly share code, notes, and snippets.

@ORBAT
Created March 26, 2020 10:27
Show Gist options
  • Save ORBAT/c7d608e68f3b54d227e957e01be1221c to your computer and use it in GitHub Desktop.
Save ORBAT/c7d608e68f3b54d227e957e01be1221c to your computer and use it in GitHub Desktop.
Unsafe fuckery to turn uint64s into slices
package main
import (
"fmt"
"unsafe"
)
// Uint64ToBytes converts n to an 8-element byte slice. The order of the elements (i.e. whether the
// lowest byte is first or last) is dependent on hardware, but most modern CPUs are little-endian
// and have the lowest byte first (https://en.wikipedia.org/wiki/Endianness)
func Uint64ToBytes(n uint64) []byte {
// create a slice header where Data (the pointer to the first element of the slice) is &n (i.e.
// a pointer to the the first byte of n)
header := sliceHeader{
Data: unsafe.Pointer(&n),
Len: 8, // 64 bits can hold 8 bytes
Cap: 8, // cap has to be 8 as well
}
// - take a pointer to the slice header
// - turn the *sliceHeader into an unsafe.Pointer
// - turn the unsafe.Pointer into a *[]byte, allowed due to unsafe fuckery
// - dereference *[]byte to give us a []byte
return *(*[]byte)(unsafe.Pointer(&header))
}
// sliceHeader has the same memory layout as Go slice headers (and this will break if it's ever
// changed). The difference is that sliceHeader uses an unsafe.Pointer instead of uintptr. The
// reason for this is that uintptr is completely "invisible" to the garbage collector as the compiler doesn't keep track of what an uintptr points to. This means that with uintptr (like in the stdlib reflect.SliceHeader),
// you'd need to call runtime.KeepAlive() on whatever Data points to to make sure it doesn't get
// garbage collected.
// unsafe.Pointer however _is_ understood by the compiler, so it keeps any references alive without
// you having to manually call runtime.KeepAlive() on the reference.
//
// Tutorial here https://go101.org/article/unsafe.html
//
// See https://github.com/golang/go/issues/19367
//
type sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
func main() {
var zot uint64 = 0x0807060504030201
slice := Uint64ToBytes(zot)
fmt.Printf("%v, %X\n", slice, zot)
// Output: [1 2 3 4 5 6 7 8], 0807060504030201
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment