cshared ======= Building -------- go 1.6+ ``` go build -o libgogit.so -buildmode=c-shared github.com/src-d/go-git/cshared ``` Two files must appear: libgogit.h and libgogit.so. The second must be a shared library, not an ar archive (may happen when something goes wrong). Check the exported symbols with `nm -g`. How it works ------------ Nearly every public Go function is mirrored in the corresponding *_cshared.go file. struct fields are also mirrored with getters and setters. The functions are marked with `//export ...` "magic" cgo comment so that they appear in defined symbols of a shared library built with `-buildmode=c-shared`. Go pointers may not be passed out of cgo functions, so we maintain the two-way registry of all active Go objects mapped to `Handle`-s (`uint64`). Every time we need to return a reference to Go object outside, we call `RegisterObject(interface{})` which returns a new `Handle` or reuses an existing one if the object has already been registered. Then we return the obtained `Handle`. When we need to receive a Go object reference in cgo function parameters, we accept `uint64` and retrieve the `interface{}` with `GetObject(Handle)` which can be casted to the underlying type with a type assertion. When the object is no longer needed, we invoke `UnregisterObject(Handle)`. Although `interface{]` is just two `uintptr`-s inside, it is not a hashable type and we cannot use it a as key in our backward registry mapping. We are using the data `uintptr` as the key there. Since several distinct objects may exist with the same data pointer (e.g. struct and first field of the struct), the value of that mapping is a slice of `Handle`-s. All the mentioned service functions are goroutine- and threadsafe. `std_cshared.go` contains the cgo wrappers for standard library objects. Debugging --------- `c_dump_object()` prints the current state of the two-way object registry to stdout. `c_set_trace()` activates echoing of `RegisterObject()` and `UnregisterObject()` invocations. Caveats ------- Normally, we pass over a pointer to object as `interface{}` into `RegisterObject()` so that it can be mutated later. It requires the corresponding pointer-to-type type assertion in cgo functions. If you mess with this, the cgo function will, of course, panic. A cgo function is allowed to take Go's `string` parameters. `string`'s data must point to some memory and cgo does not copy the incoming foreign memory into Go memory automatically. What's worse, `string`-s are immutable and when you copy it, the copy points to the same memory. This means that if you pass in a `string` which was constructed using `malloc()`, for example, and later `free()` it, all Go strings created from the function parameter will point to the invalid memory. Actually, this allowance violates the cgo pointer passing rules stated just several blocks of texts below the example of string parameters - this is crazy, but we have to live with this, as usual in Go world. So, *all incoming `string`-s must be immediately safely copied with `CopyString()` once they are used*. Returning strings and byte slices is also funny: you have to use `C.CString` -> `*C.char` and additionally return the length as another result tuple member if needed. `C.CString` copies the memory pointed by `string` to a `malloc()`-ed region and it is the responsibility of the other side to `free()` it or it will leak otherwise. Another tricky part is in `c_std_map_get_str_str` and similar places where you need to return `*C.char` from an unaddressed array accessed under a pseudonym type through reflection. The only way I've found working is using `reflect.Copy` to byte slice (copy) and then conversion to `string` (copy), then `C.CString` (copy) and finally another (copy) on the receiving side because the latter must be `free()`-d. Extremely efficient.