Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang: unsafe dynamic byte array

I am trying to interface with a Windows dll using Go. The dll function I want to use accepts a pointer to a byte array. Therefore I need to give it that byte array.

I am using the syscall libary to call the dll, as demonstrated here. My basic requirements are:

  • I am given the required size for the byte array
  • I create the byte array
  • I must get a pointer to the byte array
  • I then pass the pointer to the Windows dll

I can't figure out how to create a byte array in go, and get a pointer to it. This is obviously an unsafe operation, and the unsafe library can be helpful, but I need to create a dynamic-length byte array in the first place. Creating a slice with "make" doesn't help me, unless I can get a pointer to the slice's backing array.

Has anyone else encountered this or have any ideas?

like image 492
chowey Avatar asked Nov 01 '13 04:11

chowey


2 Answers

I think syscall.ComputerName implementation https://golang.org/src/syscall/syscall_windows.go#395 would be a good example. It uses uint16s, not bytes, but otherwise ...

In your case it would be ptr := &myslice[0].

Alex

like image 182
alex Avatar answered Nov 02 '22 18:11

alex


Well I found one gross solution. Apparently the structure of a slice contains a pointer to the backing byte array, the length of the backing byte array, and then the capacity of the backing byte array.

I am only interested in a pointer to the byte array, so I only need the first member of the slice's internal data.

Go's unsafe.Pointer will not cast a slice to an unsafe pointer, but it will cast a pointer to a slice as an unsafe pointer. Since I can cast an unsafe pointer to any old type of pointer I want, I can cast it to a pointer-to-a-pointer, which recovers the first member of the slice's internal data.

Here's a working example. I wanted a uintptr but you could cast it to any pointer type.

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    // Arbitrary size
    n := 4

    // Create a slice of the correct size
    m := make([]int, n)

    // Use convoluted indirection to cast the first few bytes of the slice
    // to an unsafe uintptr
    mPtr := *(*uintptr)(unsafe.Pointer(&m))

    // Check it worked
    m[0] = 987
    // (we have to recast the uintptr to a *int to examine it)
    fmt.Println(m[0], *(*int)(unsafe.Pointer(mPtr)))
}

If you wanted a *int instead, you could do mPtr := *(**int)(unsafe.Pointer(&m))

This works as long as a slice maintains this internal data structure. I am definitely open to a more robust solution that doesn't depend on the structure of Go's internals.

like image 27
chowey Avatar answered Nov 02 '22 16:11

chowey