From da07dca4523ebd25c634152a62cae4a72eb5783f Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Tue, 21 Jun 2016 17:14:53 +0300 Subject: Wrap more objects with CGo --- cshared/README.md | 8 +- cshared/blame_cshared.go | 51 +++++++++++++ cshared/commit_cshared.go | 41 +++++++++- cshared/file_cshared.go | 67 +++++++++++++++++ cshared/objects.go | 7 ++ cshared/objects_cshared.go | 2 +- cshared/remote_cshared.go | 4 +- cshared/std_cshared.go | 2 +- cshared/tag_cshared.go | 183 +++++++++++++++++++++++++++++++++++++++++++++ cshared/tree_cshared.go | 142 +++++++++++++++++++++++++++++++++++ 10 files changed, 498 insertions(+), 9 deletions(-) create mode 100644 cshared/blame_cshared.go create mode 100644 cshared/file_cshared.go create mode 100644 cshared/tag_cshared.go create mode 100644 cshared/tree_cshared.go (limited to 'cshared') diff --git a/cshared/README.md b/cshared/README.md index 51e424e..ea32b4b 100644 --- a/cshared/README.md +++ b/cshared/README.md @@ -72,8 +72,8 @@ 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 +where you need to return `*C.char` from an unaddressable 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 +is using `reflect.Copy` to byte slice (copy), then `CBytes` (copy) and +finally another (copy) on the receiving side because the latter must be +`free()`-d. \ No newline at end of file diff --git a/cshared/blame_cshared.go b/cshared/blame_cshared.go new file mode 100644 index 0000000..2da2e42 --- /dev/null +++ b/cshared/blame_cshared.go @@ -0,0 +1,51 @@ +// +build ignore +package main + +import ( + "C" + + "gopkg.in/src-d/go-git.v3" +) + +//export c_Blame_get_Path +func c_Blame_get_Path(b uint64) *C.char { + obj, ok := GetObject(Handle(b)) + if !ok { + return nil + } + blame := obj.(*git.Blame) + return C.CString(blame.Path) +} + +//export c_Blame_get_Rev +func c_Blame_get_Rev(b uint64) *C.char { + obj, ok := GetObject(Handle(b)) + if !ok { + return nil + } + blame := obj.(*git.Blame) + return CBytes(blame.Rev[:]) +} + + +//export c_Blame_get_Lines_len +func c_Blame_get_Lines_len(b uint64) int { + obj, ok := GetObject(Handle(b)) + if !ok { + return 0 + } + blame := obj.(*git.Blame) + return len(blame.Lines) +} + +//export c_Blame_get_Lines_item +func c_Blame_get_Lines_item(b uint64, i int) { + obj, ok := GetObject(Handle(b)) + if !ok { + return + } + blame := obj.(*git.Blame) + line := blame.Lines[i] + _ = line +} + diff --git a/cshared/commit_cshared.go b/cshared/commit_cshared.go index 791660b..51b2c1a 100644 --- a/cshared/commit_cshared.go +++ b/cshared/commit_cshared.go @@ -4,6 +4,8 @@ package main import ( "C" "io" + "reflect" + "unsafe" "gopkg.in/src-d/go-git.v3" "gopkg.in/src-d/go-git.v3/core" @@ -16,7 +18,7 @@ func c_Commit_get_Hash(c uint64) *C.char { return nil } commit := obj.(*git.Commit) - return C.CString(string(commit.Hash[:])) + return CBytes(commit.Hash[:]) } //export c_Commit_get_Author @@ -142,6 +144,43 @@ func c_Commit_String(c uint64) *C.char { return C.CString(commit.String()) } +//export c_Commit_References +func c_Commit_References(c uint64, path string) (*C.char, int, int, *C.char) { + obj, ok := GetObject(Handle(c)) + if !ok { + return nil, 0, ErrorCodeNotFound, C.CString(MessageNotFound) + } + commit := obj.(*git.Commit) + refs, err := commit.References(CopyString(path)) + if err != nil { + return nil, 0, ErrorCodeInternal, C.CString(err.Error()) + } + handles := make([]uint64, len(refs)) + for i, c := range(refs) { + handles[i] = uint64(RegisterObject(c)) + } + size := 8 * len(handles) + dest := C.malloc(C.size_t(size)) + header := (*reflect.SliceHeader)(unsafe.Pointer(&handles)) + header.Len *= 8 + copy((*[1<<30]byte)(dest)[:], *(*[]byte)(unsafe.Pointer(header))) + return (*C.char)(dest), size / 8, ErrorCodeSuccess, nil +} + +//export c_Commit_Blame +func c_Commit_Blame(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) + blame, err := commit.Blame(CopyString(path)) + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(blame)), ErrorCodeSuccess, nil +} + //export c_NewCommitIter func c_NewCommitIter(r uint64, iter uint64) uint64 { obj, ok := GetObject(Handle(r)) diff --git a/cshared/file_cshared.go b/cshared/file_cshared.go new file mode 100644 index 0000000..7f7c917 --- /dev/null +++ b/cshared/file_cshared.go @@ -0,0 +1,67 @@ +package main + +import ( + "C" + + "gopkg.in/src-d/go-git.v3" +) + +//export c_File_get_Name +func c_File_get_Name(f uint64) *C.char { + obj, ok := GetObject(Handle(f)) + if !ok { + return nil + } + file := obj.(*git.File) + return C.CString(file.Name) +} + +//export c_File_get_Mode +func c_File_get_Mode(f uint64) uint32 { + obj, ok := GetObject(Handle(f)) + if !ok { + return 0 + } + file := obj.(*git.File) + return uint32(file.Mode) +} + +//export c_NewFileIter +func c_NewFileIter(r uint64, t uint64) uint64 { + obj, ok := GetObject(Handle(r)) + if !ok { + return IH + } + repo := obj.(*git.Repository) + obj, ok = GetObject(Handle(t)) + if !ok { + return IH + } + tree := obj.(*git.Tree) + iter := git.NewFileIter(repo, tree) + return uint64(RegisterObject(iter)) +} + +//export c_FileIter_Next +func c_FileIter_Next(i uint64) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(i)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + iter := obj.(*git.FileIter) + file, err := iter.Next() + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(file)), ErrorCodeSuccess, nil +} + +//export c_FileIter_Close +func c_FileIter_Close(i uint64) { + obj, ok := GetObject(Handle(i)) + if !ok { + return + } + iter := obj.(*git.FileIter) + iter.Close() +} diff --git a/cshared/objects.go b/cshared/objects.go index 0517449..ae3440d 100644 --- a/cshared/objects.go +++ b/cshared/objects.go @@ -140,6 +140,13 @@ func CopyString(str string) string { return string(buf) } +// https://github.com/golang/go/issues/14838 +func CBytes(bytes []byte) *C.char { + ptr := C.malloc(C.size_t(len(bytes))) + copy((*[1<<30]byte)(ptr)[:], bytes) + return (*C.char)(ptr) +} + func SafeIsNil(v reflect.Value) bool { defer func() { recover() }() return v.IsNil() diff --git a/cshared/objects_cshared.go b/cshared/objects_cshared.go index 5c794c3..9f14598 100644 --- a/cshared/objects_cshared.go +++ b/cshared/objects_cshared.go @@ -54,7 +54,7 @@ func c_Blob_get_Hash(b uint64) *C.char { return nil } blob := obj.(*git.Blob) - return C.CString(string(blob.Hash[:])) + return CBytes(blob.Hash[:]) } //export c_Blob_Size diff --git a/cshared/remote_cshared.go b/cshared/remote_cshared.go index 04a26de..9ae72f3 100644 --- a/cshared/remote_cshared.go +++ b/cshared/remote_cshared.go @@ -130,7 +130,7 @@ func c_Remote_Head(r uint64) (*C.char, int, *C.char) { if err != nil { return nil, ErrorCodeInternal, C.CString(err.Error()) } - return C.CString(string(hash[:])), ErrorCodeSuccess, nil + return CBytes(hash[:]), ErrorCodeSuccess, nil } //export c_Remote_Fetch @@ -177,7 +177,7 @@ func c_Remote_Ref(r uint64, refName string) (*C.char, int, *C.char) { if err != nil { return nil, ErrorCodeInternal, C.CString(err.Error()) } - return C.CString(string(hash[:])), ErrorCodeSuccess, nil + return CBytes(hash[:]), ErrorCodeSuccess, nil } //export c_Remote_Refs diff --git a/cshared/std_cshared.go b/cshared/std_cshared.go index 5df9a21..ca93993 100644 --- a/cshared/std_cshared.go +++ b/cshared/std_cshared.go @@ -28,7 +28,7 @@ func c_std_map_get_str_str(m uint64, key string) *C.char { 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 CBytes(arr) } return C.CString(val.String()) } diff --git a/cshared/tag_cshared.go b/cshared/tag_cshared.go new file mode 100644 index 0000000..2a8db65 --- /dev/null +++ b/cshared/tag_cshared.go @@ -0,0 +1,183 @@ +// +build ignore +package main + +import ( + "C" + + "gopkg.in/src-d/go-git.v3" + "gopkg.in/src-d/go-git.v3/core" +) + +func c_Tag_get_Hash(t uint64) *C.char { + obj, ok := GetObject(Handle(t)) + if !ok { + return nil + } + tag := obj.(*git.Tag) + return CBytes(tag.Hash[:]) +} + +func c_Tag_get_Name(t uint64) *C.char { + obj, ok := GetObject(Handle(t)) + if !ok { + return nil + } + tag := obj.(*git.Tag) + return C.CString(tag.Name) +} + +func c_Tag_get_Tagger(t uint64) uint64 { + obj, ok := GetObject(Handle(t)) + if !ok { + return IH + } + tag := obj.(*git.Tag) + return uint64(RegisterObject(&tag.Tagger)) +} + +func c_Tag_get_Message(t uint64) *C.char { + obj, ok := GetObject(Handle(t)) + if !ok { + return nil + } + tag := obj.(*git.Tag) + return C.CString(tag.Message) +} + +func c_Tag_get_TargetType(t uint64) int8 { + obj, ok := GetObject(Handle(t)) + if !ok { + return -1 + } + tag := obj.(*git.Tag) + return int8(tag.TargetType) +} + +func c_Tag_get_Target(t uint64) *C.char { + obj, ok := GetObject(Handle(t)) + if !ok { + return nil + } + tag := obj.(*git.Tag) + return CBytes(tag.Target[:]) +} + +//export c_Tag_Type +func c_Tag_Type(t uint64) int8 { + obj, ok := GetObject(Handle(t)) + if !ok { + return -1 + } + tag := obj.(*git.Tag) + return int8(tag.Type()) +} + +//export c_Tag_Decode +func c_Tag_Decode(o uint64) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(o)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + cobj := obj.(*core.Object) + tag := git.Tag{} + err := tag.Decode(*cobj) + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(&tag)), ErrorCodeSuccess, nil +} + +//export c_Tag_Commit +func c_Tag_Commit(t uint64) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(t)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + tag := obj.(*git.Tag) + commit, err := tag.Commit() + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(commit)), ErrorCodeSuccess, nil +} + +//export c_Tag_Tree +func c_Tag_Tree(t uint64) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(t)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + tag := obj.(*git.Tag) + tree, err := tag.Tree() + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(tree)), ErrorCodeSuccess, nil +} + +//export c_Tag_Blob +func c_Tag_Blob(t uint64) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(t)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + tag := obj.(*git.Tag) + blob, err := tag.Blob() + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(blob)), ErrorCodeSuccess, nil +} + +//export c_Tag_Object +func c_Tag_Object(t uint64) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(t)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + tag := obj.(*git.Tag) + object, err := tag.Object() + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(&object)), ErrorCodeSuccess, nil +} + +//export c_Tag_String +func c_Tag_String(t uint64) *C.char { + obj, ok := GetObject(Handle(t)) + if !ok { + return nil + } + tag := obj.(*git.Tag) + return C.CString(tag.String()) +} + +//export c_NewTagIter +func c_NewTagIter(r uint64, i uint64) uint64 { + obj, ok := GetObject(Handle(r)) + if !ok { + return IH + } + repo := obj.(*git.Repository) + obj, ok = GetObject(Handle(i)) + if !ok { + return IH + } + iter := obj.(*core.ObjectIter) + return uint64(RegisterObject(git.NewTagIter(repo, *iter))) +} + +//export c_TagIter_Next +func c_TagIter_Next(i uint64) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(i)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + tagiter := obj.(*git.TagIter) + tag, err := tagiter.Next() + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(tag)), ErrorCodeSuccess, nil +} diff --git a/cshared/tree_cshared.go b/cshared/tree_cshared.go new file mode 100644 index 0000000..0d4191c --- /dev/null +++ b/cshared/tree_cshared.go @@ -0,0 +1,142 @@ +// +build ignore +package main + +import ( + "C" + + "gopkg.in/src-d/go-git.v3" + "gopkg.in/src-d/go-git.v3/core" +) + +//export c_Tree_get_Entries_len +func c_Tree_get_Entries_len(t uint64) int { + obj, ok := GetObject(Handle(t)) + if !ok { + return 0 + } + tree := obj.(*git.Tree) + return len(tree.Entries) +} + +//export c_Tree_get_Entries_item +func c_Tree_get_Entries_item(t uint64, index int) (*C.char, uint32, *C.char) { + obj, ok := GetObject(Handle(t)) + if !ok { + return nil, 0, nil + } + tree := obj.(*git.Tree) + item := tree.Entries[index] + return C.CString(item.Name), uint32(item.Mode), CBytes(item.Hash[:]) +} + +//export c_Tree_get_Hash +func c_Tree_get_Hash(t uint64) *C.char { + obj, ok := GetObject(Handle(t)) + if !ok { + return nil + } + tree := obj.(*git.Tree) + return CBytes(tree.Hash[:]) +} + +func c_Tree_File(t uint64, path string) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(t)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + tree := obj.(*git.Tree) + file, err := tree.File(CopyString(path)) + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(file)), ErrorCodeSuccess, nil +} + +//export c_Tree_Type +func c_Tree_Type(t uint64) int8 { + obj, ok := GetObject(Handle(t)) + if !ok { + return -1 + } + tree := obj.(*git.Tree) + return int8(tree.Type()) +} + +//export c_Tree_Files +func c_Tree_Files(t uint64) uint64 { + obj, ok := GetObject(Handle(t)) + if !ok { + return IH + } + tree := obj.(*git.Tree) + iter := tree.Files() + return uint64(RegisterObject(iter)) +} + +//export c_Tree_Decode +func c_Tree_Decode(o uint64) (uint64, int, *C.char) { + obj, ok := GetObject(Handle(o)) + if !ok { + return IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + cobj := obj.(*core.Object) + tree := git.Tree{} + err := tree.Decode(*cobj) + if err != nil { + return IH, ErrorCodeInternal, C.CString(err.Error()) + } + return uint64(RegisterObject(&tree)), ErrorCodeSuccess, nil +} + +//export c_NewTreeWalker +func c_NewTreeWalker(r uint64, t uint64) uint64 { + obj, ok := GetObject(Handle(r)) + if !ok { + return IH + } + repo := obj.(*git.Repository) + obj, ok = GetObject(Handle(t)) + if !ok { + return IH + } + tree := obj.(*git.Tree) + walker := git.NewTreeWalker(repo, tree) + return uint64(RegisterObject(walker)) +} + +//export c_TreeWalker_Next +func c_TreeWalker_Next(tw uint64) (*C.char, *C.char, uint32, *C.char, + uint64, int, *C.char) { + obj, ok := GetObject(Handle(tw)) + if !ok { + return nil, nil, 0, nil, IH, ErrorCodeNotFound, C.CString(MessageNotFound) + } + walker := obj.(*git.TreeWalker) + name, entry, object, err := walker.Next() + if err != nil { + return nil, nil, 0, nil, IH, ErrorCodeInternal, C.CString(err.Error()) + } + return C.CString(name), C.CString(entry.Name), uint32(entry.Mode), + CBytes(entry.Hash[:]), uint64(RegisterObject(&object)), + ErrorCodeSuccess, nil +} + +//export c_TreeWalker_Tree +func c_TreeWalker_Tree(tw uint64) uint64 { + obj, ok := GetObject(Handle(tw)) + if !ok { + return IH + } + walker := obj.(*git.TreeWalker) + return uint64(RegisterObject(walker.Tree())) +} + +//export c_TreeWalker_Close +func c_TreeWalker_Close(tw uint64) { + obj, ok := GetObject(Handle(tw)) + if !ok { + return + } + walker := obj.(*git.TreeWalker) + walker.Close() +} \ No newline at end of file -- cgit