From 5b30f0c466090923f4f821f05b94573f1336b633 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 23 Apr 2019 14:51:52 +0200 Subject: Split Encoder into smaller functions Signed-off-by: Filip Navara --- plumbing/format/commitgraph/commitgraph_test.go | 26 ++--- plumbing/format/commitgraph/encoder.go | 124 ++++++++++++++++-------- 2 files changed, 95 insertions(+), 55 deletions(-) (limited to 'plumbing') diff --git a/plumbing/format/commitgraph/commitgraph_test.go b/plumbing/format/commitgraph/commitgraph_test.go index 581415c..a8e4557 100644 --- a/plumbing/format/commitgraph/commitgraph_test.go +++ b/plumbing/format/commitgraph/commitgraph_test.go @@ -1,10 +1,11 @@ package commitgraph_test import ( - "testing" + "fmt" "io/ioutil" "os" "path" + "testing" "golang.org/x/exp/mmap" @@ -64,36 +65,37 @@ func testDecodeHelper(c *C, path string) { c.Assert(len(node.ParentIndexes), Equals, 3) c.Assert(len(node.ParentHashes), Equals, 3) c.Assert(node.ParentHashes[0].String(), Equals, "ce275064ad67d51e99f026084e20827901a8361c") - c.Assert(node.ParentHashes[1].String(), Equals, "bb13916df33ed23004c3ce9ed3b8487528e655c1") - c.Assert(node.ParentHashes[2].String(), Equals, "a45273fe2d63300e1962a9e26a6b15c276cd7082") + c.Assert(node.ParentHashes[1].String(), Equals, "bb13916df33ed23004c3ce9ed3b8487528e655c1") + c.Assert(node.ParentHashes[2].String(), Equals, "a45273fe2d63300e1962a9e26a6b15c276cd7082") // Check all hashes hashes := index.Hashes() c.Assert(len(hashes), Equals, 11) - c.Assert(hashes[0].String(), Equals, "03d2c021ff68954cf3ef0a36825e194a4b98f981") - c.Assert(hashes[10].String(), Equals, "e713b52d7e13807e87a002e812041f248db3f643") + c.Assert(hashes[0].String(), Equals, "03d2c021ff68954cf3ef0a36825e194a4b98f981") + c.Assert(hashes[10].String(), Equals, "e713b52d7e13807e87a002e812041f248db3f643") } func (s *CommitgraphSuite) TestDecode(c *C) { fixtures.ByTag("commit-graph").Test(c, func(f *fixtures.Fixture) { - dotgit := f.DotGit() + dotgit := f.DotGit() testDecodeHelper(c, path.Join(dotgit.Root(), "objects", "info", "commit-graph")) }) } func (s *CommitgraphSuite) TestReencode(c *C) { fixtures.ByTag("commit-graph").Test(c, func(f *fixtures.Fixture) { - dotgit := f.DotGit() + dotgit := f.DotGit() reader, err := mmap.Open(path.Join(dotgit.Root(), "objects", "info", "commit-graph")) c.Assert(err, IsNil) defer reader.Close() index, err := commitgraph.OpenFileIndex(reader) - c.Assert(err, IsNil) + c.Assert(err, IsNil) writer, err := ioutil.TempFile(dotgit.Root(), "commit-graph") c.Assert(err, IsNil) - tmpName := writer.Name() + tmpName := writer.Name() + fmt.Printf(tmpName) defer os.Remove(tmpName) encoder := commitgraph.NewEncoder(writer) err = encoder.Encode(index) @@ -106,12 +108,12 @@ func (s *CommitgraphSuite) TestReencode(c *C) { func (s *CommitgraphSuite) TestReencodeInMemory(c *C) { fixtures.ByTag("commit-graph").Test(c, func(f *fixtures.Fixture) { - dotgit := f.DotGit() + dotgit := f.DotGit() reader, err := mmap.Open(path.Join(dotgit.Root(), "objects", "info", "commit-graph")) c.Assert(err, IsNil) index, err := commitgraph.OpenFileIndex(reader) - c.Assert(err, IsNil) + c.Assert(err, IsNil) memoryIndex := commitgraph.NewMemoryIndex() for i, hash := range index.Hashes() { node, err := index.GetNodeByIndex(i) @@ -123,7 +125,7 @@ func (s *CommitgraphSuite) TestReencodeInMemory(c *C) { writer, err := ioutil.TempFile(dotgit.Root(), "commit-graph") c.Assert(err, IsNil) - tmpName := writer.Name() + tmpName := writer.Name() defer os.Remove(tmpName) encoder := commitgraph.NewEncoder(writer) err = encoder.Encode(memoryIndex) diff --git a/plumbing/format/commitgraph/encoder.go b/plumbing/format/commitgraph/encoder.go index aac4d0e..a4c5ad3 100644 --- a/plumbing/format/commitgraph/encoder.go +++ b/plumbing/format/commitgraph/encoder.go @@ -25,81 +25,118 @@ func NewEncoder(w io.Writer) *Encoder { func (e *Encoder) Encode(idx Index) error { var err error - // Get all the hashes in the memory index + // Get all the hashes in the input index hashes := idx.Hashes() + // Sort the inout and prepare helper structures we'll need for encoding + hashToIndex, fanout, largeEdgesCount := e.prepare(idx, hashes) + + chunkSignatures := [][]byte{oidFanoutSignature, oidLookupSignature, commitDataSignature} + chunkSizes := []uint64{4 * 256, uint64(len(hashes)) * 20, uint64(len(hashes)) * 36} + if largeEdgesCount > 0 { + chunkSignatures = append(chunkSignatures, largeEdgeListSignature) + chunkSizes = append(chunkSizes, uint64(largeEdgesCount)*4) + } + + if err = e.encodeFileHeader(len(chunkSignatures)); err != nil { + return err + } + if err = e.encodeChunkHeaders(chunkSignatures, chunkSizes); err != nil { + return err + } + if err = e.encodeFanout(fanout); err != nil { + return err + } + if err = e.encodeOidLookup(hashes); err != nil { + return err + } + if largeEdges, err := e.encodeCommitData(hashes, hashToIndex, idx); err == nil { + if err = e.encodeLargeEdges(largeEdges); err != nil { + return err + } + } + if err != nil { + return err + } + return e.encodeChecksum() +} + +func (e *Encoder) prepare(idx Index, hashes []plumbing.Hash) (hashToIndex map[plumbing.Hash]uint32, fanout []uint32, largeEdgesCount uint32) { // Sort the hashes and build our index plumbing.HashesSort(hashes) - hashToIndex := make(map[plumbing.Hash]uint32) - hashFirstToCount := make(map[byte]uint32) + hashToIndex = make(map[plumbing.Hash]uint32) + fanout = make([]uint32, 256) for i, hash := range hashes { hashToIndex[hash] = uint32(i) - hashFirstToCount[hash[0]]++ + fanout[hash[0]]++ + } + + // Convert the fanout to cumulative values + for i := 1; i <= 0xff; i++ { + fanout[i] += fanout[i-1] } // Find out if we will need large edge table - largeEdgesCount := 0 for i := 0; i < len(hashes); i++ { v, _ := idx.GetNodeByIndex(i) if len(v.ParentHashes) > 2 { - largeEdgesCount += len(v.ParentHashes) - 2 + largeEdgesCount += uint32(len(v.ParentHashes) - 2) break } } - chunks := [][]byte{oidFanoutSignature, oidLookupSignature, commitDataSignature} - chunkSizes := []uint64{4 * 256, uint64(len(hashes)) * 20, uint64(len(hashes)) * 36} - if largeEdgesCount > 0 { - chunks = append(chunks, largeEdgeListSignature) - chunkSizes = append(chunkSizes, uint64(largeEdgesCount)*4) - } + return +} - // Write header +func (e *Encoder) encodeFileHeader(chunkCount int) (err error) { if _, err = e.Write(commitFileSignature); err == nil { - _, err = e.Write([]byte{1, 1, byte(len(chunks)), 0}) - } - if err != nil { - return err + _, err = e.Write([]byte{1, 1, byte(chunkCount), 0}) } + return +} - // Write chunk headers - offset := uint64(8 + len(chunks)*12 + 12) - for i, signature := range chunks { +func (e *Encoder) encodeChunkHeaders(chunkSignatures [][]byte, chunkSizes []uint64) (err error) { + // 8 bytes of file header, 12 bytes for each chunk header and 12 byte for terminator + offset := uint64(8 + len(chunkSignatures)*12 + 12) + for i, signature := range chunkSignatures { if _, err = e.Write(signature); err == nil { err = binary.WriteUint64(e, offset) } if err != nil { - return err + return } offset += chunkSizes[i] } - if _, err = e.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); err != nil { - return err + if _, err = e.Write([]byte{0, 0, 0, 0}); err == nil { + err = binary.WriteUint64(e, offset) } + return +} - // Write fanout - var cumulative uint32 +func (e *Encoder) encodeFanout(fanout []uint32) (err error) { for i := 0; i <= 0xff; i++ { - if err = binary.WriteUint32(e, hashFirstToCount[byte(i)]+cumulative); err != nil { - return err + if err = binary.WriteUint32(e, fanout[i]); err != nil { + return } - cumulative += hashFirstToCount[byte(i)] } + return +} - // Write OID lookup +func (e *Encoder) encodeOidLookup(hashes []plumbing.Hash) (err error) { for _, hash := range hashes { if _, err = e.Write(hash[:]); err != nil { return err } } + return +} - // Write commit data - var largeEdges []uint32 +func (e *Encoder) encodeCommitData(hashes []plumbing.Hash, hashToIndex map[plumbing.Hash]uint32, idx Index) (largeEdges []uint32, err error) { for _, hash := range hashes { origIndex, _ := idx.GetIndexByHash(hash) commitData, _ := idx.GetNodeByIndex(origIndex) - if _, err := e.Write(commitData.TreeHash[:]); err != nil { - return err + if _, err = e.Write(commitData.TreeHash[:]); err != nil { + return } var parent1, parent2 uint32 @@ -125,27 +162,28 @@ func (e *Encoder) Encode(idx Index) error { err = binary.WriteUint32(e, parent2) } if err != nil { - return err + return } unixTime := uint64(commitData.When.Unix()) unixTime |= uint64(commitData.Generation) << 34 if err = binary.WriteUint64(e, unixTime); err != nil { - return err + return } } + return +} - // Write large edges if necessary +func (e *Encoder) encodeLargeEdges(largeEdges []uint32) (err error) { for _, parent := range largeEdges { if err = binary.WriteUint32(e, parent); err != nil { - return err + return } } + return +} - // Write checksum - if _, err := e.Write(e.hash.Sum(nil)[:20]); err != nil { - return err - } - - return nil +func (e *Encoder) encodeChecksum() error { + _, err := e.Write(e.hash.Sum(nil)[:20]) + return err } -- cgit