aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Markovtsev <vadim@sourced.tech>2016-06-10 18:49:50 +0200
committerVadim Markovtsev <vadim@sourced.tech>2016-06-15 10:29:46 +0200
commitbea415417e87fbb403095e8cd3fb8512a1a97af8 (patch)
treee34cbd4d5dc894be5d6f9cc9570366b1cbc5f5ed
parent08f9e7015aad2ca768638b446fb8632f11601899 (diff)
downloadgo-git-bea415417e87fbb403095e8cd3fb8512a1a97af8.tar.gz
Add cshared files to allow building wrappers in other languages
-rw-r--r--README.md8
-rw-r--r--cshared/README.md79
-rw-r--r--cshared/auth_method_cshared.go192
-rw-r--r--cshared/commit_cshared.go178
-rw-r--r--cshared/objects.go182
-rw-r--r--cshared/objects_cshared.go99
-rw-r--r--cshared/remote_cshared.go192
-rw-r--r--cshared/repository_cshared.go247
-rw-r--r--cshared/std_cshared.go143
9 files changed, 1320 insertions, 0 deletions
diff --git a/README.md b/README.md
index 57f7f64..4ce3ed1 100644
--- a/README.md
+++ b/README.md
@@ -113,6 +113,14 @@ fmt.Println(commit)
```
+Wrapping
+--------
+
+go-git can be wrapped into any language which supports shared library interop.
+[Python wrapper](https://github.com/src-d/gypogit) already exists.
+This is provided by "cshared" [cgo](https://golang.org/cmd/cgo/) files which can be built
+with `go build -o libgogit.so -buildmode=c-shared github.com/src-d/go-git/cshared`.
+
Acknowledgements
----------------
diff --git a/cshared/README.md b/cshared/README.md
new file mode 100644
index 0000000..51e424e
--- /dev/null
+++ b/cshared/README.md
@@ -0,0 +1,79 @@
+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. \ No newline at end of file
diff --git a/cshared/auth_method_cshared.go b/cshared/auth_method_cshared.go
new file mode 100644
index 0000000..3e1479e
--- /dev/null
+++ b/cshared/auth_method_cshared.go
@@ -0,0 +1,192 @@
+// +build ignore
+package main
+
+import (
+ "C"
+ "strings"
+
+ "golang.org/x/crypto/ssh"
+ "gopkg.in/src-d/go-git.v3/clients/http"
+ gssh "gopkg.in/src-d/go-git.v3/clients/ssh"
+)
+
+//export c_NewBasicAuth
+func c_NewBasicAuth(username, password string) uint64 {
+ auth := http.NewBasicAuth(CopyString(username), CopyString(password))
+ return uint64(RegisterObject(auth))
+}
+
+//export c_ParseRawPrivateKey
+func c_ParseRawPrivateKey(pemBytes []byte) (uint64, int, *C.char) {
+ pkey, err := ssh.ParseRawPrivateKey(pemBytes)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ // pointer is received - no need for &
+ return uint64(RegisterObject(pkey)), ErrorCodeSuccess, nil
+}
+
+//export c_ParsePrivateKey
+func c_ParsePrivateKey(pemBytes []byte) (uint64, int, *C.char) {
+ signer, err := ssh.ParsePrivateKey(pemBytes)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(&signer)), ErrorCodeSuccess, nil
+}
+
+//export c_NewPublicKey
+func c_NewPublicKey(key uint64) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(key))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ key_obj := obj.(ssh.PublicKey)
+ pkey, err := ssh.NewPublicKey(key_obj)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(&pkey)), ErrorCodeSuccess, nil
+}
+
+//export c_NewSignerFromKey
+func c_NewSignerFromKey(key uint64) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(key))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ signer, err := ssh.NewSignerFromKey(obj)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(&signer)), ErrorCodeSuccess, nil
+}
+
+//export c_MarshalAuthorizedKey
+func c_MarshalAuthorizedKey(key uint64) (*C.char, int) {
+ obj, ok := GetObject(Handle(key))
+ if !ok {
+ return nil, 0
+ }
+ obj_key := obj.(ssh.PublicKey)
+ mak := ssh.MarshalAuthorizedKey(obj_key)
+ return C.CString(string(mak)), len(mak)
+}
+
+//export c_ParsePublicKey
+func c_ParsePublicKey(in []byte) (uint64, int, *C.char) {
+ pkey, err := ssh.ParsePublicKey(in)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(&pkey)), ErrorCodeSuccess, nil
+}
+
+//export c_ParseAuthorizedKey
+func c_ParseAuthorizedKey(in []byte) (uint64, *C.char, *C.char, *C.char, int, int, *C.char) {
+ pkey, comment, options, rest, err := ssh.ParseAuthorizedKey(in)
+ if err != nil {
+ return IH, nil, nil, nil, 0, ErrorCodeInternal,
+ C.CString(err.Error())
+ }
+ pkey_handle := RegisterObject(&pkey)
+ mopt := strings.Join(options, "\xff")
+ return uint64(pkey_handle), C.CString(comment), C.CString(mopt),
+ C.CString(string(rest)), len(rest), ErrorCodeSuccess, nil
+}
+
+//export c_ssh_Password_New
+func c_ssh_Password_New(user, pass string) uint64 {
+ obj := gssh.Password{User: CopyString(user), Pass: CopyString(pass)}
+ return uint64(RegisterObject(&obj))
+}
+
+//export c_ssh_Password_get_User
+func c_ssh_Password_get_User(p uint64) *C.char {
+ obj, ok := GetObject(Handle(p))
+ if !ok {
+ return nil
+ }
+ return C.CString(obj.(*gssh.Password).User)
+}
+
+//export c_ssh_Password_set_User
+func c_ssh_Password_set_User(p uint64, v string) {
+ obj, ok := GetObject(Handle(p))
+ if !ok {
+ return
+ }
+ obj.(*gssh.Password).User = CopyString(v)
+}
+
+//export c_ssh_Password_get_Pass
+func c_ssh_Password_get_Pass(p uint64) *C.char {
+ obj, ok := GetObject(Handle(p))
+ if !ok {
+ return nil
+ }
+ return C.CString(obj.(*gssh.Password).Pass)
+}
+
+//export c_ssh_Password_set_Pass
+func c_ssh_Password_set_Pass(p uint64, v string) {
+ obj, ok := GetObject(Handle(p))
+ if !ok {
+ return
+ }
+ obj.(*gssh.Password).Pass = CopyString(v)
+}
+
+//c_ssh_PublicKeys_New
+func c_ssh_PublicKeys_New(user string, signer uint64) uint64 {
+ obj, ok := GetObject(Handle(signer))
+ if !ok {
+ return IH
+ }
+ pk := gssh.PublicKeys{User: CopyString(user), Signer: obj.(ssh.Signer)}
+ return uint64(RegisterObject(&pk))
+}
+
+//export c_ssh_PublicKeys_get_User
+func c_ssh_PublicKeys_get_User(p uint64) *C.char {
+ obj, ok := GetObject(Handle(p))
+ if !ok {
+ return nil
+ }
+ return C.CString(obj.(*gssh.PublicKeys).User)
+}
+
+//export c_ssh_PublicKeys_set_User
+func c_ssh_PublicKeys_set_User(p uint64, v string) {
+ obj, ok := GetObject(Handle(p))
+ if !ok {
+ return
+ }
+ obj.(*gssh.PublicKeys).User = CopyString(v)
+}
+
+//export c_ssh_PublicKeys_get_Signer
+func c_ssh_PublicKeys_get_Signer(p uint64) uint64 {
+ obj, ok := GetObject(Handle(p))
+ if !ok {
+ return IH
+ }
+ handle, ok := GetHandle(&obj.(*gssh.PublicKeys).Signer)
+ if !ok {
+ return IH
+ }
+ return uint64(handle)
+}
+
+//export c_ssh_PublicKeys_set_Signer
+func c_ssh_PublicKeys_set_Signer(p uint64, v uint64) {
+ obj, ok := GetObject(Handle(p))
+ if !ok {
+ return
+ }
+ signer, ok := GetObject(Handle(v))
+ if !ok {
+ return
+ }
+ obj.(*gssh.PublicKeys).Signer = *signer.(*ssh.Signer)
+} \ No newline at end of file
diff --git a/cshared/commit_cshared.go b/cshared/commit_cshared.go
new file mode 100644
index 0000000..791660b
--- /dev/null
+++ b/cshared/commit_cshared.go
@@ -0,0 +1,178 @@
+// +build ignore
+package main
+
+import (
+ "C"
+ "io"
+
+ "gopkg.in/src-d/go-git.v3"
+ "gopkg.in/src-d/go-git.v3/core"
+)
+
+//export c_Commit_get_Hash
+func c_Commit_get_Hash(c uint64) *C.char {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return nil
+ }
+ commit := obj.(*git.Commit)
+ return C.CString(string(commit.Hash[:]))
+}
+
+//export c_Commit_get_Author
+func c_Commit_get_Author(c uint64) uint64 {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return IH
+ }
+ commit := obj.(*git.Commit)
+ author := &commit.Author
+ author_handle := RegisterObject(author)
+ return uint64(author_handle)
+}
+
+//export c_Commit_get_Committer
+func c_Commit_get_Committer(c uint64) uint64 {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return IH
+ }
+ commit := obj.(*git.Commit)
+ committer := &commit.Committer
+ committer_handle := RegisterObject(committer)
+ return uint64(committer_handle)
+}
+
+//export c_Commit_get_Message
+func c_Commit_get_Message(c uint64) *C.char {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return nil
+ }
+ commit := obj.(*git.Commit)
+ return C.CString(commit.Message)
+}
+
+//export c_Commit_Tree
+func c_Commit_Tree(c uint64) uint64 {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return IH
+ }
+ commit := obj.(*git.Commit)
+ tree := commit.Tree()
+ tree_handle := RegisterObject(tree)
+ return uint64(tree_handle)
+}
+
+//export c_Commit_Parents
+func c_Commit_Parents(c uint64) uint64 {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return IH
+ }
+ commit := obj.(*git.Commit)
+ parents := commit.Parents()
+ parents_handle := RegisterObject(parents)
+ return uint64(parents_handle)
+}
+
+//export c_Commit_NumParents
+func c_Commit_NumParents(c uint64) int {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return -1
+ }
+ commit := obj.(*git.Commit)
+ return commit.NumParents()
+}
+
+//export c_Commit_File
+func c_Commit_File(c uint64, path string) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ commit := obj.(*git.Commit)
+ file, err := commit.File(CopyString(path))
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ file_handle := RegisterObject(file)
+ return uint64(file_handle), ErrorCodeSuccess, nil
+}
+
+//export c_Commit_ID
+func c_Commit_ID(c uint64) *C.char {
+ return c_Commit_get_Hash(c)
+}
+
+//export c_Commit_Type
+func c_Commit_Type(c uint64) int8 {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return -1
+ }
+ commit := obj.(*git.Commit)
+ return int8(commit.Type())
+}
+
+//export c_Commit_Decode
+func c_Commit_Decode(o uint64) (uint64, int, *C.char) {
+ commit := git.Commit{}
+ obj, ok := GetObject(Handle(o))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ cobj := obj.(*core.Object)
+ err := commit.Decode(*cobj)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(&commit)), ErrorCodeSuccess, nil
+}
+
+//export c_Commit_String
+func c_Commit_String(c uint64) *C.char {
+ obj, ok := GetObject(Handle(c))
+ if !ok {
+ return nil
+ }
+ commit := obj.(*git.Commit)
+ return C.CString(commit.String())
+}
+
+//export c_NewCommitIter
+func c_NewCommitIter(r uint64, iter uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ repo := obj.(*git.Repository)
+ obj, ok = GetObject(Handle(iter))
+ if !ok {
+ return IH
+ }
+ obj_iter := obj.(*core.ObjectIter)
+ commit_iter := git.NewCommitIter(repo, *obj_iter)
+ handle := RegisterObject(commit_iter)
+ return uint64(handle)
+}
+
+//export c_CommitIter_Next
+func c_CommitIter_Next(iter uint64) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(iter))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ commitIter := obj.(*git.CommitIter)
+ commit, err := commitIter.Next()
+ if err != nil {
+ if err == io.EOF {
+ return IH, ErrorCodeSuccess, nil
+ }
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ handle := RegisterObject(commit)
+ return uint64(handle), ErrorCodeSuccess, nil
+}
diff --git a/cshared/objects.go b/cshared/objects.go
new file mode 100644
index 0000000..0517449
--- /dev/null
+++ b/cshared/objects.go
@@ -0,0 +1,182 @@
+// +build ignore
+package main
+
+import (
+ "C"
+ "fmt"
+ "math"
+ "sync"
+ "reflect"
+)
+
+type Handle uint64
+
+const (
+ ErrorCodeSuccess = iota
+ ErrorCodeNotFound = -iota
+ ErrorCodeInternal = -iota
+)
+
+const MessageNotFound string = "object not found"
+const InvalidHandle Handle = 0
+const IH uint64 = uint64(InvalidHandle)
+var counter Handle = InvalidHandle
+var opMutex sync.Mutex
+var registryHandle2Obj map[Handle]interface{} = map[Handle]interface{}{}
+var registryObj2Handle map[uintptr][]Handle = map[uintptr][]Handle{}
+var trace bool = false
+
+func getNewHandle() Handle {
+ counter++
+ if counter == math.MaxUint64 {
+ panic("Handle cache is exhausted")
+ }
+ return counter
+}
+
+func RegisterObject(obj interface{}) Handle {
+ data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1]
+ if trace {
+ fmt.Printf("RegisterObject 0x%x\t%v\n", data_ptr, obj)
+ }
+ opMutex.Lock()
+ defer opMutex.Unlock()
+ handles, ok := registryObj2Handle[data_ptr]
+ if ok {
+ for _, h := range(handles) {
+ other, ok := registryHandle2Obj[h]
+ if !ok {
+ panic("Inconsistent internal object mapping state (1)")
+ }
+ if other == obj {
+ if trace {
+ fmt.Printf("RegisterObject 0x%x reused %d\n", data_ptr, h)
+ }
+ return h
+ }
+ }
+ }
+ handle := getNewHandle()
+ registryHandle2Obj[handle] = obj
+ registryObj2Handle[data_ptr] = append(registryObj2Handle[data_ptr], handle)
+ if trace {
+ c_dump_objects()
+ }
+ return handle
+}
+
+func UnregisterObject(handle Handle) int {
+ if trace {
+ fmt.Printf("UnregisterObject %d\n", handle)
+ }
+ if handle == InvalidHandle {
+ return ErrorCodeNotFound
+ }
+ opMutex.Lock()
+ defer opMutex.Unlock()
+ obj, ok := registryHandle2Obj[handle]
+ if !ok {
+ return ErrorCodeNotFound
+ }
+ delete(registryHandle2Obj, handle)
+ data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1]
+ other_handles, ok := registryObj2Handle[data_ptr]
+ if !ok {
+ panic(fmt.Sprintf("Inconsistent internal object mapping state (2): %d",
+ handle))
+ }
+ hi := -1
+ for i, h := range(other_handles) {
+ if h == handle {
+ hi = i
+ break
+ }
+ }
+ if hi < 0 {
+ panic(fmt.Sprintf("Inconsistent internal object mapping state (3): %d",
+ handle))
+ }
+ if len(other_handles) == 1 {
+ delete(registryObj2Handle, data_ptr)
+ } else {
+ registryObj2Handle[data_ptr] = append(other_handles[:hi], other_handles[hi + 1:]...)
+ }
+ if trace {
+ c_dump_objects()
+ }
+ return ErrorCodeSuccess
+}
+
+func GetObject(handle Handle) (interface{}, bool) {
+ if handle == InvalidHandle {
+ return nil, false
+ }
+ opMutex.Lock()
+ defer opMutex.Unlock()
+ a, b := registryHandle2Obj[handle]
+ return a, b
+}
+
+func GetHandle(obj interface{}) (Handle, bool) {
+ data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1]
+ opMutex.Lock()
+ defer opMutex.Unlock()
+ handles, ok := registryObj2Handle[data_ptr]
+ if !ok {
+ return InvalidHandle, false
+ }
+ for _, h := range(handles) {
+ candidate := registryHandle2Obj[h]
+ if candidate == obj {
+ return h, true
+ }
+ }
+ return InvalidHandle, false
+}
+
+func CopyString(str string) string {
+ buf := make([]byte, len(str))
+ copy(buf, []byte(str))
+ return string(buf)
+}
+
+func SafeIsNil(v reflect.Value) bool {
+ defer func() { recover() }()
+ return v.IsNil()
+}
+
+//export c_dispose
+func c_dispose(handle uint64) {
+ UnregisterObject(Handle(handle))
+}
+
+//export c_objects_size
+func c_objects_size() int {
+ return len(registryHandle2Obj)
+}
+
+//export c_dump_objects
+func c_dump_objects() {
+ fmt.Printf("handles (%d):\n", len(registryHandle2Obj))
+ for h, obj := range(registryHandle2Obj) {
+ fmt.Printf("0x%x\t0x%x %v\n", h,
+ reflect.ValueOf(&obj).Elem().InterfaceData()[1], obj)
+ }
+ fmt.Println()
+ phs := 0
+ for _, h := range(registryObj2Handle) {
+ phs += len(h)
+ }
+ fmt.Printf("pointers (%d):\n", phs)
+ for ptr, h := range(registryObj2Handle) {
+ fmt.Printf("0x%x\t%v\n", ptr, h)
+ }
+}
+
+//export c_set_trace
+func c_set_trace(val bool) {
+ trace = val
+}
+
+// dummy main() is needed by the linker
+func main() {}
diff --git a/cshared/objects_cshared.go b/cshared/objects_cshared.go
new file mode 100644
index 0000000..5c794c3
--- /dev/null
+++ b/cshared/objects_cshared.go
@@ -0,0 +1,99 @@
+// +build ignore
+package main
+
+import (
+ "C"
+ "io/ioutil"
+ "time"
+
+ "gopkg.in/src-d/go-git.v3"
+ "gopkg.in/src-d/go-git.v3/core"
+)
+
+//export c_Signature_Name
+func c_Signature_Name(s uint64) *C.char {
+ obj, ok := GetObject(Handle(s))
+ if !ok {
+ return nil
+ }
+ sign := obj.(*git.Signature)
+ return C.CString(sign.Name)
+}
+
+//export c_Signature_Email
+func c_Signature_Email(s uint64) *C.char {
+ obj, ok := GetObject(Handle(s))
+ if !ok {
+ return nil
+ }
+ sign := obj.(*git.Signature)
+ return C.CString(sign.Email)
+}
+
+//export c_Signature_When
+func c_Signature_When(s uint64) *C.char {
+ obj, ok := GetObject(Handle(s))
+ if !ok {
+ return nil
+ }
+ sign := obj.(*git.Signature)
+ return C.CString(sign.When.Format(time.RFC3339))
+}
+
+//export c_Signature_Decode
+func c_Signature_Decode(b []byte) uint64 {
+ sign := git.Signature{}
+ sign.Decode(b)
+ return uint64(RegisterObject(&sign))
+}
+
+//export c_Blob_get_Hash
+func c_Blob_get_Hash(b uint64) *C.char {
+ obj, ok := GetObject(Handle(b))
+ if !ok {
+ return nil
+ }
+ blob := obj.(*git.Blob)
+ return C.CString(string(blob.Hash[:]))
+}
+
+//export c_Blob_Size
+func c_Blob_Size(b uint64) int64 {
+ obj, ok := GetObject(Handle(b))
+ if !ok {
+ return -1
+ }
+ blob := obj.(*git.Blob)
+ return blob.Size
+}
+
+//export c_Blob_Decode
+func c_Blob_Decode(o uint64) uint64 {
+ obj, ok := GetObject(Handle(o))
+ if !ok {
+ return IH
+ }
+ cobj := obj.(*core.Object)
+ blob := git.Blob{}
+ blob.Decode(*cobj)
+ return uint64(RegisterObject(&blob))
+}
+
+//export c_Blob_Read
+func c_Blob_Read(b uint64) (int, *C.char) {
+ obj, ok := GetObject(Handle(b))
+ if !ok {
+ return ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ blob := obj.(*git.Blob)
+ reader, err := blob.Reader()
+ if err != nil {
+ return ErrorCodeInternal, C.CString(err.Error())
+ }
+ data, err := ioutil.ReadAll(reader)
+ reader.Close()
+ if err != nil {
+ return ErrorCodeInternal, C.CString(err.Error())
+ }
+ return len(data), C.CString(string(data))
+} \ No newline at end of file
diff --git a/cshared/remote_cshared.go b/cshared/remote_cshared.go
new file mode 100644
index 0000000..04a26de
--- /dev/null
+++ b/cshared/remote_cshared.go
@@ -0,0 +1,192 @@
+// +build ignore
+package main
+
+import (
+ "C"
+
+ "gopkg.in/src-d/go-git.v3"
+ "gopkg.in/src-d/go-git.v3/clients/common"
+)
+
+//export c_Remote_get_Endpoint
+func c_Remote_get_Endpoint(r uint64) *C.char {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return nil
+ }
+ remote := obj.(*git.Remote)
+ return C.CString(string(remote.Endpoint))
+}
+
+//export c_Remote_set_Endpoint
+func c_Remote_set_Endpoint(r uint64, value string) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return
+ }
+ remote := obj.(*git.Remote)
+ remote.Endpoint = common.Endpoint(CopyString(value))
+}
+
+//export c_Remote_get_Auth
+func c_Remote_get_Auth(r uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ remote := obj.(*git.Remote)
+ return uint64(RegisterObject(&remote.Auth))
+}
+
+//export c_Remote_set_Auth
+func c_Remote_set_Auth(r uint64, value uint64) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return
+ }
+ remote := obj.(*git.Remote)
+ obj, ok = GetObject(Handle(value))
+ if !ok {
+ return
+ }
+ remote.Auth = *obj.(*common.AuthMethod)
+}
+
+//export c_NewRemote
+func c_NewRemote(url string) (uint64, int, *C.char) {
+ remote, err := git.NewRemote(CopyString(url))
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(remote)), ErrorCodeSuccess, nil
+}
+
+//export c_NewAuthenticatedRemote
+func c_NewAuthenticatedRemote(url string, auth uint64) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(auth))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ auth_method := *obj.(*common.AuthMethod)
+ remote, err := git.NewAuthenticatedRemote(CopyString(url), auth_method)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(remote)), ErrorCodeSuccess, nil
+}
+
+//export c_Remote_Connect
+func c_Remote_Connect(r uint64) (int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return ErrorCodeNotFound, nil
+ }
+ remote := obj.(*git.Remote)
+ err := remote.Connect()
+ if err != nil {
+ return ErrorCodeInternal, C.CString(err.Error())
+ }
+ return ErrorCodeSuccess, nil
+}
+
+//export c_Remote_Info
+func c_Remote_Info(r uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ remote := obj.(*git.Remote)
+ return uint64(RegisterObject(remote.Info()))
+}
+
+//export c_Remote_Capabilities
+func c_Remote_Capabilities(r uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ remote := obj.(*git.Remote)
+ return uint64(RegisterObject(remote.Capabilities()))
+}
+
+//export c_Remote_DefaultBranch
+func c_Remote_DefaultBranch(r uint64) *C.char {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return nil
+ }
+ remote := obj.(*git.Remote)
+ return C.CString(remote.DefaultBranch())
+}
+
+//export c_Remote_Head
+func c_Remote_Head(r uint64) (*C.char, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return nil, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ remote := obj.(*git.Remote)
+ hash, err := remote.Head()
+ if err != nil {
+ return nil, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return C.CString(string(hash[:])), ErrorCodeSuccess, nil
+}
+
+//export c_Remote_Fetch
+func c_Remote_Fetch(r uint64, req uint64) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ remote := obj.(*git.Remote)
+ obj, ok = GetObject(Handle(req))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ request := obj.(*common.GitUploadPackRequest)
+ reader, err := remote.Fetch(request)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(reader)), ErrorCodeSuccess, nil
+}
+
+//export c_Remote_FetchDefaultBranch
+func c_Remote_FetchDefaultBranch(r uint64) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ remote := obj.(*git.Remote)
+ reader, err := remote.FetchDefaultBranch()
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return uint64(RegisterObject(reader)), ErrorCodeSuccess, nil
+}
+
+//export c_Remote_Ref
+func c_Remote_Ref(r uint64, refName string) (*C.char, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return nil, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ remote := obj.(*git.Remote)
+ hash, err := remote.Ref(CopyString(refName))
+ if err != nil {
+ return nil, ErrorCodeInternal, C.CString(err.Error())
+ }
+ return C.CString(string(hash[:])), ErrorCodeSuccess, nil
+}
+
+//export c_Remote_Refs
+func c_Remote_Refs(r uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ remote := obj.(*git.Remote)
+ refs := remote.Refs()
+ return uint64(RegisterObject(refs))
+} \ No newline at end of file
diff --git a/cshared/repository_cshared.go b/cshared/repository_cshared.go
new file mode 100644
index 0000000..6ca600f
--- /dev/null
+++ b/cshared/repository_cshared.go
@@ -0,0 +1,247 @@
+// +build ignore
+package main
+
+import (
+ "C"
+
+ "gopkg.in/src-d/go-git.v3"
+ "gopkg.in/src-d/go-git.v3/core"
+ "gopkg.in/src-d/go-git.v3/clients/common"
+)
+
+//export c_Repository
+func c_Repository() uint64 {
+ repo := &git.Repository{}
+ repo_handle := RegisterObject(repo)
+ return uint64(repo_handle)
+}
+
+//export c_NewRepository
+func c_NewRepository(url string, auth uint64) (uint64, int, *C.char) {
+ var repo *git.Repository
+ var err error
+ url = CopyString(url)
+ if auth != IH {
+ real_auth, ok := GetObject(Handle(auth))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ repo, err = git.NewRepository(url, real_auth.(common.AuthMethod))
+ } else {
+ repo, err = git.NewRepository(url, nil)
+ }
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ repo_handle := RegisterObject(repo)
+ return uint64(repo_handle), ErrorCodeSuccess, nil
+}
+
+//export c_NewPlainRepository
+func c_NewPlainRepository() uint64 {
+ return uint64(RegisterObject(git.NewPlainRepository()))
+}
+
+//export c_Repository_get_Remotes
+func c_Repository_get_Remotes(r uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ repo := obj.(*git.Repository)
+ return uint64(RegisterObject(&repo.Remotes))
+}
+
+//export c_Repository_set_Remotes
+func c_Repository_set_Remotes(r uint64, val uint64) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return
+ }
+ repo := obj.(*git.Repository)
+ obj, ok = GetObject(Handle(val))
+ if !ok {
+ return
+ }
+ repo.Remotes = *obj.(*map[string]*git.Remote)
+}
+
+//export c_Repository_get_Storage
+func c_Repository_get_Storage(r uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ repo := obj.(*git.Repository)
+ return uint64(RegisterObject(&repo.Storage))
+}
+
+//export c_Repository_set_Storage
+func c_Repository_set_Storage(r uint64, val uint64) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return
+ }
+ repo := obj.(*git.Repository)
+ obj, ok = GetObject(Handle(val))
+ if !ok {
+ return
+ }
+ repo.Storage = *obj.(*core.ObjectStorage)
+}
+
+//export c_Repository_get_URL
+func c_Repository_get_URL(r uint64) *C.char {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return nil
+ }
+ return C.CString(obj.(*git.Repository).URL)
+}
+
+//export c_Repository_set_URL
+func c_Repository_set_URL(r uint64, val string) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return
+ }
+ repo := obj.(*git.Repository)
+ repo.URL = CopyString(val)
+}
+
+//export c_Repository_Pull
+func c_Repository_Pull(r uint64, remoteName, branch string) (int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ repo := obj.(*git.Repository)
+ err := repo.Pull(remoteName, CopyString(branch))
+ if err == nil {
+ return ErrorCodeSuccess, nil
+ }
+ return ErrorCodeInternal, C.CString(err.Error())
+}
+
+//export c_Repository_PullDefault
+func c_Repository_PullDefault(r uint64) (int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ repo := obj.(*git.Repository)
+ err := repo.PullDefault()
+ if err == nil {
+ return ErrorCodeSuccess, nil
+ }
+ return ErrorCodeInternal, C.CString(err.Error())
+}
+
+//export c_Repository_Commit
+func c_Repository_Commit(r uint64, h []byte) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ repo := obj.(*git.Repository)
+ var hash core.Hash
+ copy(hash[:], h)
+ commit, err := repo.Commit(hash)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ commit_handle := RegisterObject(commit)
+ return uint64(commit_handle), ErrorCodeSuccess, nil
+}
+
+//export c_Repository_Commits
+func c_Repository_Commits(r uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ repo := obj.(*git.Repository)
+ iter := repo.Commits()
+ iter_handle := RegisterObject(iter)
+ return uint64(iter_handle)
+}
+
+//export c_Repository_Tree
+func c_Repository_Tree(r uint64, h []byte) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ repo := obj.(*git.Repository)
+ var hash core.Hash
+ copy(hash[:], h)
+ tree, err := repo.Tree(hash)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ tree_handle := RegisterObject(tree)
+ return uint64(tree_handle), ErrorCodeSuccess, nil
+}
+
+//export c_Repository_Blob
+func c_Repository_Blob(r uint64, h []byte) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ repo := obj.(*git.Repository)
+ var hash core.Hash
+ copy(hash[:], h)
+ blob, err := repo.Blob(hash)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ blob_handle := RegisterObject(blob)
+ return uint64(blob_handle), ErrorCodeSuccess, nil
+}
+
+//export c_Repository_Tag
+func c_Repository_Tag(r uint64, h []byte) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ repo := obj.(*git.Repository)
+ var hash core.Hash
+ copy(hash[:], h)
+ tag, err := repo.Tag(hash)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ tag_handle := RegisterObject(tag)
+ return uint64(tag_handle), ErrorCodeSuccess, nil
+}
+
+//export c_Repository_Tags
+func c_Repository_Tags(r uint64) uint64 {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH
+ }
+ repo := obj.(*git.Repository)
+ iter := repo.Tags()
+ iter_handle := RegisterObject(iter)
+ return uint64(iter_handle)
+}
+
+//export c_Repository_Object
+func c_Repository_Object(r uint64, h []byte) (uint64, int, *C.char) {
+ obj, ok := GetObject(Handle(r))
+ if !ok {
+ return IH, ErrorCodeNotFound, C.CString(MessageNotFound)
+ }
+ repo := obj.(*git.Repository)
+ var hash core.Hash
+ copy(hash[:], h)
+ robj, err := repo.Object(hash)
+ if err != nil {
+ return IH, ErrorCodeInternal, C.CString(err.Error())
+ }
+ robj_handle := RegisterObject(robj)
+ return uint64(robj_handle), ErrorCodeSuccess, nil
+}
diff --git a/cshared/std_cshared.go b/cshared/std_cshared.go
new file mode 100644
index 0000000..5df9a21
--- /dev/null
+++ b/cshared/std_cshared.go
@@ -0,0 +1,143 @@
+// +build ignore
+package main
+
+import (
+ "C"
+ "reflect"
+ "strings"
+)
+
+//export c_std_map_get_str_str
+func c_std_map_get_str_str(m uint64, key string) *C.char {
+ obj, ok := GetObject(Handle(m))
+ if !ok {
+ return nil
+ }
+ mapval := reflect.ValueOf(obj)
+ if mapval.Kind() == reflect.Ptr {
+ mapval = mapval.Elem()
+ }
+ if mapval.Kind() != reflect.Map {
+ return nil
+ }
+ val := mapval.MapIndex(reflect.ValueOf(key))
+ if !val.IsValid() || SafeIsNil(val) {
+ return nil
+ }
+ if (val.Kind() == reflect.Slice || val.Kind() == reflect.Array) &&
+ val.Type().Elem().Kind() == reflect.Uint8 {
+ arr := make([]byte, val.Len(), val.Len())
+ reflect.Copy(reflect.ValueOf(arr), val)
+ return C.CString(string(arr))
+ }
+ return C.CString(val.String())
+}
+
+//export c_std_map_get_str_obj
+func c_std_map_get_str_obj(m uint64, key string) uint64 {
+ obj, ok := GetObject(Handle(m))
+ if !ok {
+ return IH
+ }
+ mapval := reflect.ValueOf(obj)
+ if mapval.Kind() == reflect.Ptr {
+ mapval = mapval.Elem()
+ }
+ if mapval.Kind() != reflect.Map {
+ return IH
+ }
+ val := mapval.MapIndex(reflect.ValueOf(key))
+ if !val.IsValid() || SafeIsNil(val) {
+ return IH
+ }
+ val_handle := RegisterObject(val.Interface())
+ return uint64(val_handle)
+}
+
+//export c_std_map_get_obj_obj
+func c_std_map_get_obj_obj(m uint64, key uint64) uint64 {
+ obj, ok := GetObject(Handle(m))
+ if !ok {
+ return IH
+ }
+ mapval := reflect.ValueOf(obj)
+ if mapval.Kind() == reflect.Ptr {
+ mapval = mapval.Elem()
+ }
+ if mapval.Kind() != reflect.Map {
+ return IH
+ }
+ obj, ok = GetObject(Handle(key))
+ if !ok {
+ return IH
+ }
+ val := mapval.MapIndex(reflect.ValueOf(obj))
+ if !val.IsValid() || SafeIsNil(val) {
+ return IH
+ }
+ val_handle := RegisterObject(val.Interface())
+ return uint64(val_handle)
+}
+
+//export c_std_map_keys_str
+func c_std_map_keys_str(m uint64) *C.char {
+ obj, ok := GetObject(Handle(m))
+ if !ok {
+ return nil
+ }
+ mapval := reflect.ValueOf(obj)
+ if mapval.Kind() == reflect.Ptr {
+ mapval = mapval.Elem()
+ }
+ if mapval.Kind() != reflect.Map {
+ return nil
+ }
+ keys := mapval.MapKeys()
+ keys_str := make([]string, 0, len(keys))
+ for _, k := range keys {
+ keys_str = append(keys_str, k.String())
+ }
+ return C.CString(strings.Join(keys_str, "\xff"))
+}
+
+//export c_std_map_len
+func c_std_map_len(m uint64) int {
+ obj, ok := GetObject(Handle(m))
+ if !ok {
+ return -1
+ }
+ mapval := reflect.ValueOf(obj)
+ if mapval.Kind() == reflect.Ptr {
+ mapval = mapval.Elem()
+ }
+ if mapval.Kind() != reflect.Map {
+ return -1
+ }
+ return mapval.Len()
+}
+
+//export c_std_map_set_str
+func c_std_map_set_str(m uint64, key string, val uint64) {
+ obj, ok := GetObject(Handle(m))
+ if !ok {
+ return
+ }
+ mapval := reflect.ValueOf(obj)
+ if mapval.Kind() == reflect.Ptr {
+ mapval = mapval.Elem()
+ } else {
+ return
+ }
+ if mapval.Kind() != reflect.Map {
+ return
+ }
+ if val == IH {
+ mapval.SetMapIndex(reflect.ValueOf(key), reflect.Value{})
+ } else {
+ obj, ok := GetObject(Handle(val))
+ if !ok {
+ return
+ }
+ mapval.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(obj))
+ }
+}