Merge pull request #282 from ianlancetaylor/master

handles, merge, odb: changes for Go 1.6 pointer passing rules
diff --git a/handles.go b/handles.go
index a062231..f5d30f0 100644
--- a/handles.go
+++ b/handles.go
@@ -10,15 +10,14 @@
 	sync.RWMutex
 	// stores the Go pointers
 	handles []interface{}
-	// Indicates which indices are in use, and keeps a pointer to slot int variable (the handle)
-	// in the Go world, so that the Go garbage collector does not free it.
-	set map[int]*int
+	// Indicates which indices are in use.
+	set map[int]bool
 }
 
 func NewHandleList() *HandleList {
 	return &HandleList{
 		handles: make([]interface{}, 5),
-		set:     make(map[int]*int),
+		set:     make(map[int]bool),
 	}
 }
 
@@ -26,8 +25,7 @@
 // list. You must only run this function while holding a write lock.
 func (v *HandleList) findUnusedSlot() int {
 	for i := 1; i < len(v.handles); i++ {
-		_, isUsed := v.set[i]
-		if !isUsed {
+		if !v.set[i] {
 			return i
 		}
 	}
@@ -48,16 +46,16 @@
 
 	slot := v.findUnusedSlot()
 	v.handles[slot] = pointer
-	v.set[slot] = &slot // Keep a pointer to slot in Go world, so it's not freed by GC.
+	v.set[slot] = true
 
 	v.Unlock()
 
-	return unsafe.Pointer(&slot)
+	return unsafe.Pointer(uintptr(slot))
 }
 
 // Untrack stops tracking the pointer given by the handle
 func (v *HandleList) Untrack(handle unsafe.Pointer) {
-	slot := *(*int)(handle)
+	slot := int(uintptr(handle))
 
 	v.Lock()
 
@@ -69,11 +67,11 @@
 
 // Get retrieves the pointer from the given handle
 func (v *HandleList) Get(handle unsafe.Pointer) interface{} {
-	slot := *(*int)(handle)
+	slot := int(uintptr(handle))
 
 	v.RLock()
 
-	if _, ok := v.set[slot]; !ok {
+	if !v.set[slot] {
 		panic(fmt.Sprintf("invalid pointer handle: %p", handle))
 	}
 
diff --git a/merge.go b/merge.go
index 272bf6a..756c792 100644
--- a/merge.go
+++ b/merge.go
@@ -6,6 +6,7 @@
 extern git_annotated_commit** _go_git_make_merge_head_array(size_t len);
 extern void _go_git_annotated_commit_array_set(git_annotated_commit** array, git_annotated_commit* ptr, size_t n);
 extern git_annotated_commit* _go_git_annotated_commit_array_get(git_annotated_commit** array, size_t n);
+extern int _go_git_merge_file(git_merge_file_result*, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, char*, size_t, char*, unsigned int, git_merge_file_options*);
 
 */
 import "C"
@@ -320,20 +321,6 @@
 	Contents []byte
 }
 
-// populate a C struct with merge file input, make sure to use freeMergeFileInput to clean up allocs
-func populateCMergeFileInput(c *C.git_merge_file_input, input MergeFileInput) {
-	c.path = C.CString(input.Path)
-	if input.Contents != nil {
-		c.ptr = (*C.char)(unsafe.Pointer(&input.Contents[0]))
-		c.size = C.size_t(len(input.Contents))
-	}
-	c.mode = C.uint(input.Mode)
-}
-
-func freeCMergeFileInput(c *C.git_merge_file_input) {
-	C.free(unsafe.Pointer(c.path))
-}
-
 type MergeFileFlags int
 
 const (
@@ -378,16 +365,26 @@
 
 func MergeFile(ancestor MergeFileInput, ours MergeFileInput, theirs MergeFileInput, options *MergeFileOptions) (*MergeFileResult, error) {
 
-	var cancestor C.git_merge_file_input
-	var cours C.git_merge_file_input
-	var ctheirs C.git_merge_file_input
+	ancestorPath := C.CString(ancestor.Path)
+	defer C.free(unsafe.Pointer(ancestorPath))
+	var ancestorContents *byte
+	if len(ancestor.Contents) > 0 {
+		ancestorContents = &ancestor.Contents[0]
+	}
 
-	populateCMergeFileInput(&cancestor, ancestor)
-	defer freeCMergeFileInput(&cancestor)
-	populateCMergeFileInput(&cours, ours)
-	defer freeCMergeFileInput(&cours)
-	populateCMergeFileInput(&ctheirs, theirs)
-	defer freeCMergeFileInput(&ctheirs)
+	oursPath := C.CString(ours.Path)
+	defer C.free(unsafe.Pointer(oursPath))
+	var oursContents *byte
+	if len(ours.Contents) > 0 {
+		oursContents = &ours.Contents[0]
+	}
+
+	theirsPath := C.CString(theirs.Path)
+	defer C.free(unsafe.Pointer(theirsPath))
+	var theirsContents *byte
+	if len(theirs.Contents) > 0 {
+		theirsContents = &theirs.Contents[0]
+	}
 
 	var copts *C.git_merge_file_options
 	if options != nil {
@@ -404,7 +401,11 @@
 	defer runtime.UnlockOSThread()
 
 	var result C.git_merge_file_result
-	ecode := C.git_merge_file(&result, &cancestor, &cours, &ctheirs, copts)
+	ecode := C._go_git_merge_file(&result,
+		(*C.char)(unsafe.Pointer(ancestorContents)), C.size_t(len(ancestor.Contents)), ancestorPath, C.uint(ancestor.Mode),
+		(*C.char)(unsafe.Pointer(oursContents)), C.size_t(len(ours.Contents)), oursPath, C.uint(ours.Mode),
+		(*C.char)(unsafe.Pointer(theirsContents)), C.size_t(len(theirs.Contents)), theirsPath, C.uint(theirs.Mode),
+		copts)
 	if ecode < 0 {
 		return nil, MakeGitError(ecode)
 	}
diff --git a/odb.go b/odb.go
index d881f63..9c6baa3 100644
--- a/odb.go
+++ b/odb.go
@@ -76,12 +76,15 @@
 
 func (v *Odb) Write(data []byte, otype ObjectType) (oid *Oid, err error) {
 	oid = new(Oid)
-	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&data))
+	var cptr unsafe.Pointer
+	if len(data) > 0 {
+		cptr = unsafe.Pointer(&data[0])
+	}
 
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	ret := C.git_odb_write(oid.toC(), v.ptr, unsafe.Pointer(hdr.Data), C.size_t(hdr.Len), C.git_otype(otype))
+	ret := C.git_odb_write(oid.toC(), v.ptr, cptr, C.size_t(len(data)), C.git_otype(otype))
 
 	if ret < 0 {
 		return nil, MakeGitError(ret)
diff --git a/wrapper.c b/wrapper.c
index 2b1a180..a0688c0 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -141,4 +141,27 @@
     return git_tag_foreach(repo, (git_tag_foreach_cb)&gitTagForeachCb, payload);
 }
 
+int _go_git_merge_file(git_merge_file_result* out, char* ancestorContents, size_t ancestorLen, char* ancestorPath, unsigned int ancestorMode, char* oursContents, size_t oursLen, char* oursPath, unsigned int oursMode, char* theirsContents, size_t theirsLen, char* theirsPath, unsigned int theirsMode, git_merge_file_options* copts) {
+	git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT;
+	git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT;
+	git_merge_file_input theirs = GIT_MERGE_FILE_INPUT_INIT;
+
+	ancestor.ptr = ancestorContents;
+	ancestor.size = ancestorLen;
+	ancestor.path = ancestorPath;
+	ancestor.mode = ancestorMode;
+
+	ours.ptr = oursContents;
+	ours.size = oursLen;
+	ours.path = oursPath;
+	ours.mode = oursMode;
+
+	theirs.ptr = theirsContents;
+	theirs.size = theirsLen;
+	theirs.path = theirsPath;
+	theirs.mode = theirsMode;
+
+	return git_merge_file(out, &ancestor, &ours, &theirs, copts);
+}
+
 /* EOF */