This is a C struct that I want to port into Go struct:
struct InputBuffer_t {
char* buffer;
size_t buffer_length;
ssize_t input_length;
};
But there is a way to declare buffer_length variable without using C.size_t cgo pointer in Go.
This is a concern for portability. If I write the Go struct in this way, is it will be portable?
type InputBuffer struct {
Buffer string
BufferLength uint32
InputLength uint32
};
Related
c.h
typedef void* MVar;
C_FUNC(
MVar* myvar //[out], return value
)
test.go
var cvar unsafe.Pointer
_ = C.C_FUNC(&cvar)
when I run test.go, it tells me
cannot use _cgo5 (type *unsafe.Pointer) as type *_Ctype_MVar
in argument to _Cfunc_C_FUNC
In this document Command cgo: Go references to C, it says "The C type void* is represented by Go's unsafe.Pointer."
cannot use _cgo5 (type *unsafe.Pointer) as type *_Ctype_MVar
in argument to _Cfunc_C_FUNC
The Go toolchain says you have a type mismatch.
An equivalent, working example, with matching types,
package main
import (
"fmt"
"unsafe"
)
/*
typedef void* pv_t;
void cfunc(pv_t *p);
#include <stdio.h>
int i;
void cfunc(pv_t *p) {
i = 42;
*p = &i;
printf("%p %p %d\n", p, *p, *(int*)(*p));
}
*/
import "C"
func main() {
var cptr unsafe.Pointer
C.cfunc((*C.pv_t)(&cptr))
fmt.Println(&cptr, cptr, *(*C.int)(cptr))
}
Output:
$ go run so.go
0xc000010028 0x592d18 42
0xc000010028 0x592d18 42
$
I'm using syscall.Syscall(...) to call a C method in a dll.
This is the C method signature:
SENSEI_API HSENSEI SENSEI_open(const char* sensigrafo, const char* options, SENSEI_ERR* se);
This is the SENSEI_ERR struct:
typedef struct
{
int code;
char* error_string;
} SENSEI_ERR;
In my GO program I declared a struct:
type senseiErr struct {
code int
error_string *byte
}
And tried to call the method:
var nargs uintptr = 3
var err senseiErr
ret, _, callErr := syscall.Syscall(uintptr(senseiOpen),
nargs,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("en"))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(""))),
uintptr(unsafe.Pointer(&err)),
)
As you may have guessed, the SENSEI_open method fill the SENSEI_ERR argument with the code and the text of the error.
Now I need to read the content of that error.
err.code actually has the correct value.
About err.error_string I don't know. I'm new to GO and i have some questions:
Since the C struct has the field char* error_string, is error_string *byte in my GO struct correct?
Should I use []byte or something else?
How do I read the content of the error_string field?
fmt.Println(err.error_string) prints the memory address
fmt.Println(*err.error_string) prints always "101"
1) I doubt that cost char* meant to be UTF16 encoded. So all what you need is just getting raw data:
sensigrafo := "en\000" // \000 = 0 = null termination, \0 does not valid
options := "\000"
...
uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&sensigrafo))
uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&options))
// *(*unsafe.Pointer) are accessing the first field of string header:
type string struct {
data *byte
len int
}
// same with slices
// but for them there's less ugly way:
sensigrafo := []byte("en\000")
options := []byte("\000")
uintptr(unsafe.Pointer(&sensigrafo[0]))
uintptr(unsafe.Pointer(&options[0]))
2) C's int and Golang's int might have different sizeof, so this requires cgo declaration (C.int) or manual matching with random selection (try also int32, int64 if you don't want to use cgo)
type senseiErr struct {
code C.int /* Golang's int32/int64 */
error_string *byte // pointer types are same as C's void* or Golang's unsafe.Pointer
}
Wrong offset might cause error_string be empty or point to random addr.
3) To read content you have to use same methods as C does (read data until null terminated byte, considering that *byte points to first element of string), but I propose to use already implemented runtime functions:
//go:linkname gostringn runtime.gostringn
func gostringn(p *byte, l int) string
//go:linkname findnull runtime.findnull
//go:nosplit
func findnull(s *byte) int
...
error_string := gostringn(err.error_string, findnull(err.error_string))
// or cgo one:
type senseiErr struct {
code C.int
error_string *C.char
}
...
error_string := C.GoString(err.error_string)
I want to write a small go application for processing videos and decided to use this library goav which is a FFmpeg binding for go. However, it does not support reading the video straightaway from memory. As a workaround, I decided to call the C function directly.
The signature of the function I want to call is as follows.
AVIOContext *avio_alloc_context(
unsigned char *buffer,
int buffer_size,
int write_flag,
void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
int64_t (*seek)(void *opaque, int64_t offset, int whence));
The go code I wrote to call this -
package video
// #include<libavformat/avio.h>
import "C"
import (
"bytes"
"github.com/giorgisio/goav/avformat"
"github.com/giorgisio/goav/avutil"
"unsafe"
)
func DecodeStream(data *bytes.Buffer) {
bufferSize := 8192
formatContext := avformat.AvformatAllocContext()
buffer := (*C.uchar)(avutil.AvMalloc(uintptr(bufferSize)))
ioContext := C.avio_alloc_context(
buffer,
C.int(bufferSize),
C.int(0),
unsafe.Pointer(data),
&[0]byte{},
&[0]byte{},
&[0]byte{},
) // <- error in this line
formatContext.SetPb((*avformat.AvIOContext)(unsafe.Pointer(ioContext)))
if formatContext.AvformatFindStreamInfo(nil) < 0 {
panic("Couldn't find stream info")
}
println("DURATION: ", formatContext.Duration())
}
However, I am getting this error runtime error: cgo argument has Go pointer to Go pointer.
I tried replacing &[0]byte{} with nil too. Can't seem to get away!
The runtime error is actually complaining about the unsafe.Pointer(data) argument. The bytes.Buffer type has internal (Go) pointers, and cgo doesn't allow passing pointers to C if those pointers point to Go objects that contain Go pointers themselves.
These rules are detailed in the cgo documentation here: https://golang.org/cmd/cgo/#hdr-Passing_pointers
The short summary is that the Go garbage collector needs to be aware when Go pointers are modified. The Go compilers make sure that compiled code appropriately informs the garbage collector, but C compilers don't know to do this. So to prevent GC problems, cgo simply doesn't allow passing pointers like this.
What's the proper way to reading a binary file written by C?
I've a C header file, there are some 'struct's.
Is it possible to use these header file instead of manual re-write them in Go.
/* sample.h */
#define HEADER_SIZE 2048 //* large then sizeof(header)
typedef struct {
uint8_t version;
uint8_t endian;
uint32_t createTime;
} header;
typedef struct {
uint64_t data1;
uint32_t data2;
char name[128];
} record;
Here is my starting Go program with pseudo code
package "main"
// #include "sample.h"
import "C"
func main() {
f, _ := os.Open("sample_file")
// read(buf, HEADER_SIZE) and print out
// use structure header to decode the buf
// while not end of file {
// read(buf, sizeof(record) and print out
// }
}
Read them using the encoding/binary package that you can find at https://golang.org/pkg/encoding/binary/
You can use binary.Read to read into a Go struct. You'll need to account for the padding the C version will add. In your example, default padding would put 2 pad bytes before createTime.
With GCC, I can do this:
typedef struct {
char a;
int n;
} MyStruct;
MyStruct ms __attribute__((section("MySection"))) = {'x', 33};
MyStruct *pms = &ms;
But when I use compound literal as follows, GCC complains warning: 'section' attribute does not apply to types [-Wattributes].
typedef struct {
char a;
int n;
} MyStruct;
MyStruct *pms = &(MyStruct __attribute__((section("MySection")))){'x', 33};
Is there any way to get by? Thank you.