Convert Go [] bytes to C * char

I have a byte.Buffer that I collect with data using the binary.Write () function. Then I need to send this byte array to function C. Using Go 1.6 I could not figure it 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 does not work on the line calling the C function:

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

C function:

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

I managed to get the following to work, but it was wrong to convert an array of bytes to a string. Is there a better way to do this? Does this method have side effects for data?

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

Again, this should work under Go 1.6, which introduced stricter cgo pointer rules.

Thanks.

+5
source share
2 answers

If you want to use your first approach, you need to create a slice outside the arguments of the function call and avoid temporarily highlighting the slice header or external structure in the arguments, so cgo checks cgo not 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 since the data will be copied to the C buffer, so there is no pointer to Go memory, and there is no way for the bytes.Buffer slice to be changed or out of volume. You will want to convert the entire string, not just the first byte. These methods need to be selected and copied twice, however, if the amount of data is small, this is 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 2 copies of data are unacceptable in this solution, there is a third option when you yourself create a C buffer and make one copy in this 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 these last options, don't forget to free the malloc'ed pointer.

+7
source

Your program crashes because the rules for passing pointers to C are changed in go1.6 (see https://tip.golang.org/doc/go1.6#cgo for details).

I do not know why your program crashes, so I created a Go problem https://github.com/golang/go/issues/14546 .

But regardless of the answer to the question, I would not use the internal byte bits. Buffer (like you) to go directly to cgo. The implementation of bytes.Buffer may change in the future and the program will mysteriously erupt. I would just copy the data that you need into any structure that is suitable and use to go to cgo.

Alex

0
source

Source: https://habr.com/ru/post/1244017/


All Articles