From 0a9e63317526b9efa3d74de5df0c990e2916c463 Mon Sep 17 00:00:00 2001 From: Sameer <11097096+sameer@users.noreply.github.com> Date: Tue, 18 Dec 2018 14:48:19 -0500 Subject: [PATCH] Cache RawData for Commit, Tag, & Tree, fixes #6 --- commit.go | 70 +++++++++++++++++++++++++++++------------------------ git_test.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tag.go | 36 ++++++++++++++++----------- tree.go | 31 +++++++++++++++--------- 4 files changed, 150 insertions(+), 57 deletions(-) diff --git a/commit.go b/commit.go index c0810c5..ebe2a2f 100644 --- a/commit.go +++ b/commit.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "strconv" + "sync" cid "github.com/ipfs/go-cid" node "github.com/ipfs/go-ipld-format" @@ -14,8 +15,8 @@ import ( type Commit struct { DataSize string `json:"-"` - GitTree cid.Cid `json:"tree"` - Parents []cid.Cid `json:"parents"` + GitTree cid.Cid `json:"tree"` + Parents []cid.Cid `json:"parents"` Message string `json:"message"` Author *PersonInfo `json:"author"` Committer *PersonInfo `json:"committer"` @@ -27,6 +28,9 @@ type Commit struct { Other []string `json:"other,omitempty"` cid cid.Cid + + rawData []byte + rawDataOnce sync.Once } type PersonInfo struct { @@ -80,7 +84,7 @@ func (pi *PersonInfo) resolve(p []string) (interface{}, []string, error) { } type MergeTag struct { - Object cid.Cid `json:"object"` + Object cid.Cid `json:"object"` Type string `json:"type"` Tag string `json:"tag"` Tagger *PersonInfo `json:"tagger"` @@ -118,34 +122,38 @@ func (c *Commit) Loggable() map[string]interface{} { } func (c *Commit) RawData() []byte { - buf := new(bytes.Buffer) - fmt.Fprintf(buf, "commit %s\x00", c.DataSize) - fmt.Fprintf(buf, "tree %s\n", hex.EncodeToString(cidToSha(c.GitTree))) - for _, p := range c.Parents { - fmt.Fprintf(buf, "parent %s\n", hex.EncodeToString(cidToSha(p))) - } - fmt.Fprintf(buf, "author %s\n", c.Author.String()) - fmt.Fprintf(buf, "committer %s\n", c.Committer.String()) - if len(c.Encoding) > 0 { - fmt.Fprintf(buf, "encoding %s\n", c.Encoding) - } - for _, mtag := range c.MergeTag { - fmt.Fprintf(buf, "mergetag object %s\n", hex.EncodeToString(cidToSha(mtag.Object))) - fmt.Fprintf(buf, " type %s\n", mtag.Type) - fmt.Fprintf(buf, " tag %s\n", mtag.Tag) - fmt.Fprintf(buf, " tagger %s\n \n", mtag.Tagger.String()) - fmt.Fprintf(buf, "%s", mtag.Text) - } - if c.Sig != nil { - fmt.Fprintln(buf, "gpgsig -----BEGIN PGP SIGNATURE-----") - fmt.Fprint(buf, c.Sig.Text) - fmt.Fprintln(buf, " -----END PGP SIGNATURE-----") - } - for _, line := range c.Other { - fmt.Fprintln(buf, line) - } - fmt.Fprintf(buf, "\n%s", c.Message) - return buf.Bytes() + c.rawDataOnce.Do(func() { + buf := new(bytes.Buffer) + fmt.Fprintf(buf, "commit %s\x00", c.DataSize) + fmt.Fprintf(buf, "tree %s\n", hex.EncodeToString(cidToSha(c.GitTree))) + for _, p := range c.Parents { + fmt.Fprintf(buf, "parent %s\n", hex.EncodeToString(cidToSha(p))) + } + fmt.Fprintf(buf, "author %s\n", c.Author.String()) + fmt.Fprintf(buf, "committer %s\n", c.Committer.String()) + if len(c.Encoding) > 0 { + fmt.Fprintf(buf, "encoding %s\n", c.Encoding) + } + for _, mtag := range c.MergeTag { + fmt.Fprintf(buf, "mergetag object %s\n", hex.EncodeToString(cidToSha(mtag.Object))) + fmt.Fprintf(buf, " type %s\n", mtag.Type) + fmt.Fprintf(buf, " tag %s\n", mtag.Tag) + fmt.Fprintf(buf, " tagger %s\n \n", mtag.Tagger.String()) + fmt.Fprintf(buf, "%s", mtag.Text) + } + if c.Sig != nil { + fmt.Fprintln(buf, "gpgsig -----BEGIN PGP SIGNATURE-----") + fmt.Fprint(buf, c.Sig.Text) + fmt.Fprintln(buf, " -----END PGP SIGNATURE-----") + } + for _, line := range c.Other { + fmt.Fprintln(buf, line) + } + fmt.Fprintf(buf, "\n%s", c.Message) + c.rawData = buf.Bytes() + }) + + return c.rawData } func (c *Commit) Resolve(path []string) (interface{}, []string, error) { diff --git a/git_test.go b/git_test.go index b0e0b7e..51a8031 100644 --- a/git_test.go +++ b/git_test.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + "reflect" "strings" "testing" @@ -171,6 +172,7 @@ func testNode(t *testing.T, nd node.Node) error { /*s, _ := commit.Size() assert.Equal(t, len(commit.RawData()), int(s))*/ //TODO: Known breakage + assert(t, reflect.DeepEqual(commit.RawData(), commit.RawData())) assert(t, commit.GitTree.Defined()) assert(t, commit.Links() != nil) assert(t, commit.Loggable()["type"] == "git_commit") @@ -228,6 +230,7 @@ func testNode(t *testing.T, nd node.Node) error { } assert(t, tag.Type == "commit" || tag.Type == "tree" || tag.Type == "blob" || tag.Type == "tag") + assert(t, reflect.DeepEqual(tag.RawData(), tag.RawData())) assert(t, tag.Object.Defined()) assert(t, tag.Loggable()["type"] == "git_tag") assert(t, tag.Tree("", -1) != nil) @@ -243,6 +246,7 @@ func testNode(t *testing.T, nd node.Node) error { t.Fatalf("Tree is not a tree") } + assert(t, reflect.DeepEqual(tree.RawData(), tree.RawData())) assert(t, tree.entries != nil) assert(t, tree.Tree("", 0) == nil) } @@ -345,3 +349,69 @@ func assert(t *testing.T, ok bool) { t.Fatal("Assertion failed") } } + +func BenchmarkRawData(b *testing.B) { + for i := 0; i < b.N; i++ { + err := filepath.Walk(".git/objects", func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + if info.IsDir() { + return nil + } + + parts := strings.Split(path, "/") + if dir := parts[len(parts)-2]; dir == "info" || dir == "pack" { + return nil + } + + fi, err := os.Open(path) + if err != nil { + return err + } + + thing, err := ParseCompressedObject(fi) + if err != nil { + return err + } + thing.RawData() + return nil + }) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkCid(b *testing.B) { + for i := 0; i < b.N; i++ { + err := filepath.Walk(".git/objects", func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + if info.IsDir() { + return nil + } + + parts := strings.Split(path, "/") + if dir := parts[len(parts)-2]; dir == "info" || dir == "pack" { + return nil + } + + fi, err := os.Open(path) + if err != nil { + return err + } + + thing, err := ParseCompressedObject(fi) + if err != nil { + return err + } + thing.Cid() + return nil + }) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/tag.go b/tag.go index 4bee947..0655c3d 100644 --- a/tag.go +++ b/tag.go @@ -3,15 +3,16 @@ package ipldgit import ( "bytes" "encoding/hex" + "errors" "fmt" + "sync" - "errors" cid "github.com/ipfs/go-cid" node "github.com/ipfs/go-ipld-format" ) type Tag struct { - Object cid.Cid `json:"object"` + Object cid.Cid `json:"object"` Type string `json:"type"` Tag string `json:"tag"` Tagger *PersonInfo `json:"tagger"` @@ -19,6 +20,9 @@ type Tag struct { dataSize string cid cid.Cid + + rawData []byte + rawDataOnce sync.Once } func (t *Tag) Cid() cid.Cid { @@ -41,18 +45,22 @@ func (t *Tag) Loggable() map[string]interface{} { } func (t *Tag) RawData() []byte { - buf := new(bytes.Buffer) - fmt.Fprintf(buf, "tag %s\x00", t.dataSize) - fmt.Fprintf(buf, "object %s\n", hex.EncodeToString(cidToSha(t.Object))) - fmt.Fprintf(buf, "type %s\n", t.Type) - fmt.Fprintf(buf, "tag %s\n", t.Tag) - if t.Tagger != nil { - fmt.Fprintf(buf, "tagger %s\n", t.Tagger.String()) - } - if t.Message != "" { - fmt.Fprintf(buf, "\n%s", t.Message) - } - return buf.Bytes() + t.rawDataOnce.Do(func() { + buf := new(bytes.Buffer) + fmt.Fprintf(buf, "tag %s\x00", t.dataSize) + fmt.Fprintf(buf, "object %s\n", hex.EncodeToString(cidToSha(t.Object))) + fmt.Fprintf(buf, "type %s\n", t.Type) + fmt.Fprintf(buf, "tag %s\n", t.Tag) + if t.Tagger != nil { + fmt.Fprintf(buf, "tagger %s\n", t.Tagger.String()) + } + if t.Message != "" { + fmt.Fprintf(buf, "\n%s", t.Message) + } + t.rawData = buf.Bytes() + }) + + return t.rawData } func (t *Tag) Resolve(path []string) (interface{}, []string, error) { diff --git a/tree.go b/tree.go index 5907383..fb49249 100644 --- a/tree.go +++ b/tree.go @@ -3,24 +3,27 @@ package ipldgit import ( "bytes" "encoding/json" + "errors" "fmt" "io" + "sync" - "errors" cid "github.com/ipfs/go-cid" node "github.com/ipfs/go-ipld-format" ) type Tree struct { - entries map[string]*TreeEntry - size int - order []string - cid cid.Cid + entries map[string]*TreeEntry + size int + order []string + cid cid.Cid + rawData []byte + rawDataOnce sync.Once } type TreeEntry struct { name string - Mode string `json:"mode"` + Mode string `json:"mode"` Hash cid.Cid `json:"hash"` } @@ -95,13 +98,17 @@ func (t *Tree) Loggable() map[string]interface{} { } func (t *Tree) RawData() []byte { - buf := new(bytes.Buffer) + t.rawDataOnce.Do(func() { + buf := new(bytes.Buffer) - fmt.Fprintf(buf, "tree %d\x00", t.size) - for _, s := range t.order { - t.entries[s].WriteTo(buf) - } - return buf.Bytes() + fmt.Fprintf(buf, "tree %d\x00", t.size) + for _, s := range t.order { + t.entries[s].WriteTo(buf) + } + t.rawData = buf.Bytes() + }) + + return t.rawData } func (t *Tree) Resolve(p []string) (interface{}, []string, error) {