Skip to content

Commit

Permalink
fix go 1.6 build by changing pointer passing
Browse files Browse the repository at this point in the history
In Go 1.6, we're no longer allowed to pass a pointer to
a Go struct with Go fields to Cairo functions that want a callback.

Work around this by instead making a map keyed by integers and
passing integer keys through to C.  This is a bit cumbersome but
it's the recommended fix for this problem.
  • Loading branch information
evmar committed Feb 20, 2016
1 parent 2b68506 commit 6a65339
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 16 deletions.
16 changes: 10 additions & 6 deletions cairo/cairo.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ import (
#include <cairo-xlib.h>
#include <stdlib.h>
int gocairoWriteFunc(void* closure, const unsigned char* data, unsigned int length);
int gocairoReadFunc(void* closure, const unsigned char* data, unsigned int length);
int gocairoWriteFunc(int key, const unsigned char* data, unsigned int length);
int gocairoReadFunc(int key, const unsigned char* data, unsigned int length);
// A cairo_write_func_t for use in cairo_surface_write_to_png.
cairo_status_t gocairo_write_func(void *closure,
const unsigned char *data,
unsigned int length) {
return gocairoWriteFunc(closure, data, length)
return gocairoWriteFunc(*(int*)closure, data, length)
? CAIRO_STATUS_SUCCESS
: CAIRO_STATUS_WRITE_ERROR;
}
Expand All @@ -45,7 +45,7 @@ cairo_status_t gocairo_write_func(void *closure,
cairo_status_t gocairo_read_func(void *closure,
const unsigned char *data,
unsigned int length) {
return gocairoReadFunc(closure, data, length)
return gocairoReadFunc(*(int*)closure, data, length)
? CAIRO_STATUS_SUCCESS
: CAIRO_STATUS_WRITE_ERROR;
}
Expand All @@ -60,9 +60,11 @@ func (s Status) Error() string {
// WriteToPNG encodes a Surface to an io.Writer as a PNG file.
func (surface *Surface) WriteToPNG(w io.Writer) error {
data := writeClosure{w: w}
key := goPointers.put(data)
status := C.cairo_surface_write_to_png_stream((*C.cairo_surface_t)(surface.Ptr),
(C.cairo_write_func_t)(unsafe.Pointer(C.gocairo_write_func)),
unsafe.Pointer(&data))
unsafe.Pointer(&key))
goPointers.clear(key)
// TODO: which should we prefer between writeClosure.err and status?
// Perhaps test against CAIRO_STATUS_WRITE_ERROR? Needs a test case.
return Status(status).toError()
Expand All @@ -72,9 +74,11 @@ func (surface *Surface) WriteToPNG(w io.Writer) error {
// PNG data.
func ImageSurfaceCreateFromPNGStream(r io.Reader) (*ImageSurface, error) {
data := readClosure{r: r}
key := goPointers.put(data)
surf := &ImageSurface{wrapSurface(C.cairo_image_surface_create_from_png_stream(
(C.cairo_read_func_t)(unsafe.Pointer(C.gocairo_read_func)),
unsafe.Pointer(&data)))}
unsafe.Pointer(&key)))}
goPointers.clear(key)
// TODO: which should we prefer between readClosure.err and status?
// Perhaps test against CAIRO_STATUS_WRITE_ERROR? Needs a test case.
return surf, surf.status()
Expand Down
44 changes: 40 additions & 4 deletions cairo/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package cairo
import (
"io"
"reflect"
"sync"
"unsafe"
)

Expand All @@ -36,14 +37,49 @@ func (s Status) toError() error {
return s
}

// In Go 1.6, you're not allowed to pass Go pointers through C.
// To work around this, use a map keyed by integers for stashing
// arbitrary Go data.
type goPointerStash struct {
sync.Mutex
data map[C.int]interface{}
nextKey C.int
}

var goPointers = &goPointerStash{}

func (gp *goPointerStash) put(data interface{}) C.int {
gp.Lock()
defer gp.Unlock()
if gp.data == nil {
gp.data = make(map[C.int]interface{})
}
key := gp.nextKey
gp.nextKey++
gp.data[key] = data
return key
}

func (gp *goPointerStash) get(key C.int) interface{} {
gp.Lock()
defer gp.Unlock()
return gp.data[key]
}

func (gp *goPointerStash) clear(key C.int) {
gp.Lock()
defer gp.Unlock()
delete(gp.data, key)
}

type writeClosure struct {
w io.Writer
err error
}

//export gocairoWriteFunc
func gocairoWriteFunc(closure unsafe.Pointer, data unsafe.Pointer, clength C.uint) bool {
writeClosure := (*writeClosure)(closure)
func gocairoWriteFunc(key C.int, data unsafe.Pointer, clength C.uint) bool {
writeClosure := goPointers.get(key).(writeClosure)
length := uint(clength)
slice := ((*[1 << 30]byte)(data))[:length:length]
_, writeClosure.err = writeClosure.w.Write(slice)
Expand All @@ -56,8 +92,8 @@ type readClosure struct {
}

//export gocairoReadFunc
func gocairoReadFunc(closure unsafe.Pointer, data unsafe.Pointer, clength C.uint) bool {
readClosure := (*readClosure)(closure)
func gocairoReadFunc(key C.int, data unsafe.Pointer, clength C.uint) bool {
readClosure := goPointers.get(key).(readClosure)
length := uint(clength)
buf := ((*[1 << 30]byte)(data))[:length:length]
_, readClosure.err = io.ReadFull(readClosure.r, buf)
Expand Down
16 changes: 10 additions & 6 deletions gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -678,14 +678,14 @@ import (
#include <cairo-xlib.h>
#include <stdlib.h>
int gocairoWriteFunc(void* closure, const unsigned char* data, unsigned int length);
int gocairoReadFunc(void* closure, const unsigned char* data, unsigned int length);
int gocairoWriteFunc(int key, const unsigned char* data, unsigned int length);
int gocairoReadFunc(int key, const unsigned char* data, unsigned int length);
// A cairo_write_func_t for use in cairo_surface_write_to_png.
cairo_status_t gocairo_write_func(void *closure,
const unsigned char *data,
unsigned int length) {
return gocairoWriteFunc(closure, data, length)
return gocairoWriteFunc(*(int*)closure, data, length)
? CAIRO_STATUS_SUCCESS
: CAIRO_STATUS_WRITE_ERROR;
}
Expand All @@ -694,7 +694,7 @@ cairo_status_t gocairo_write_func(void *closure,
cairo_status_t gocairo_read_func(void *closure,
const unsigned char *data,
unsigned int length) {
return gocairoReadFunc(closure, data, length)
return gocairoReadFunc(*(int*)closure, data, length)
? CAIRO_STATUS_SUCCESS
: CAIRO_STATUS_WRITE_ERROR;
}
Expand All @@ -709,9 +709,11 @@ func (s Status) Error() string {
// WriteToPNG encodes a Surface to an io.Writer as a PNG file.
func (surface *Surface) WriteToPNG(w io.Writer) error {
data := writeClosure{w: w}
key := goPointers.put(data)
status := C.cairo_surface_write_to_png_stream((*C.cairo_surface_t)(surface.Ptr),
(C.cairo_write_func_t)(unsafe.Pointer(C.gocairo_write_func)),
unsafe.Pointer(&data))
unsafe.Pointer(&key))
goPointers.clear(key)
// TODO: which should we prefer between writeClosure.err and status?
// Perhaps test against CAIRO_STATUS_WRITE_ERROR? Needs a test case.
return Status(status).toError()
Expand All @@ -721,9 +723,11 @@ func (surface *Surface) WriteToPNG(w io.Writer) error {
// PNG data.
func ImageSurfaceCreateFromPNGStream(r io.Reader) (*ImageSurface, error) {
data := readClosure{r: r}
key := goPointers.put(data)
surf := &ImageSurface{wrapSurface(C.cairo_image_surface_create_from_png_stream(
(C.cairo_read_func_t)(unsafe.Pointer(C.gocairo_read_func)),
unsafe.Pointer(&data)))}
unsafe.Pointer(&key)))}
goPointers.clear(key)
// TODO: which should we prefer between readClosure.err and status?
// Perhaps test against CAIRO_STATUS_WRITE_ERROR? Needs a test case.
return surf, surf.status()
Expand Down

0 comments on commit 6a65339

Please sign in to comment.