From bea415417e87fbb403095e8cd3fb8512a1a97af8 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Fri, 10 Jun 2016 18:49:50 +0200 Subject: Add cshared files to allow building wrappers in other languages --- README.md | 8 ++ cshared/README.md | 79 +++++++++++++ cshared/auth_method_cshared.go | 192 ++++++++++++++++++++++++++++++++ cshared/commit_cshared.go | 178 +++++++++++++++++++++++++++++ cshared/objects.go | 182 ++++++++++++++++++++++++++++++ cshared/objects_cshared.go | 99 +++++++++++++++++ cshared/remote_cshared.go | 192 ++++++++++++++++++++++++++++++++ cshared/repository_cshared.go | 247 +++++++++++++++++++++++++++++++++++++++++ cshared/std_cshared.go | 143 ++++++++++++++++++++++++ 9 files changed, 1320 insertions(+) create mode 100644 cshared/README.md create mode 100644 cshared/auth_method_cshared.go create mode 100644 cshared/commit_cshared.go create mode 100644 cshared/objects.go create mode 100644 cshared/objects_cshared.go create mode 100644 cshared/remote_cshared.go create mode 100644 cshared/repository_cshared.go create mode 100644 cshared/std_cshared.go 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)) + } +} -- cgit