Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert Go []byte to a C *char

Tags:

go

cgo

I have a byte.Buffer that I pack with data using the binary.Write() function. I then need to send this byte array to a C function. Using Go 1.6 I have not been successful at figuring this out.

buf := new(bytes.Buffer) //create my buffer
....
binary.Write(buf, binary.LittleEndian, data) //write my data to buffer here
addr := (*C.uchar)(unsafe.Pointer(&buf.Bytes()[0])) //convert buffers byte array to a C array
rc := C.the_function(addr, C.int(buf.Len())) //Fails here

It fails on the line calling the C function saying:

panic: runtime error: cgo argument has Go pointer to Go pointer

The C function:

int the_function(const void *data, int nbytes);

I was able to get the following to work, but it felt wrong converting the byte array to a string. Is there a better way to do this? Does this method risk side effects to the data?

addr := unsafe.Pointer(C.CString(string(buf.Bytes()[0]))

Again this needs to work under Go 1.6 which introduced stricter cgo pointer rules.

Thank you.

like image 327
dangeroushobo Avatar asked Feb 27 '16 17:02

dangeroushobo


People also ask

What is a [] byte in Golang?

[]byte and string are small headers pointing to data, with lengths indicating how much data is present. []byte has two lengths: the current length of the data and the capacity. The capacity tells us how many more bytes we can add to the data before needing to go and get a bigger piece of memory.

Is there char in golang?

In Go, there is no char data type. It uses byte and rune to represent character values. The byte data type represents ASCII characters while the rune data type represents a more broader set of Unicode characters that are encoded in UTF-8 format.

How many bytes is a Go?

Like byte type, Go has another integer type rune . It is aliases for int32 (4 bytes) data types and is equal to int32 in all ways.


1 Answers

If you want to use your first approach, you need to create the slice outside the function call arguments, and avoid the temporarily allocated slice header or the outer structure in the arguments, so the cgo checks don't see it as a pointer stored in Go.

b := buf.Bytes()
rc := C.the_function(unsafe.Pointer(&b[0]), C.int(buf.Len()))

The C.CString method will be safer, in that the data is copied into a C buffer, so there is no pointer to Go memory, and there's no chance the slice behind the bytes.Buffer will be modified or go out of scope. You will want to convert the whole string, not just the first byte. This methods does need to allocate and copy twice, however if the amount of data is small it's probably not a concern compared to the overhead of the cgo call itself.

str := buf.String()
p := unsafe.Pointer(C.CString(str))
defer C.free(p)
rc = C.the_function(p, C.int(len(str)))

If the 2 copies of the data aren't acceptable in that solution, there is a third option where you malloc the C buffer yourself, and make a single copy into that buffer:

p := C.malloc(C.size_t(len(b)))
defer C.free(p)

// copy the data into the buffer, by converting it to a Go array
cBuf := (*[1 << 30]byte)(p)
copy(cBuf[:], b)
rc = C.the_function(p, C.int(buf.Len()))

But with both of those latter options, don't forget to free the malloc'ed pointer.

like image 165
JimB Avatar answered Oct 17 '22 01:10

JimB