Skip to content

Commit 961a58c

Browse files
committed
object_writer: support different hashes
When we write an object, we'll need to know its hash in order to compute the proper value. Let's pass the caller the hash algorithm as well as the writer to write to. We don't need a generic set of options here because we're not likely to gain additional options that affect our parsing of the object. This is the first format change in many years, so we can defer adding more complex options until we need them.
1 parent 26a97bf commit 961a58c

File tree

3 files changed

+53
-26
lines changed

3 files changed

+53
-26
lines changed

object_db.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ func (d *ObjectDatabase) encodeBuffer(object Object, buf io.ReadWriter) (sha []b
294294
}
295295
defer d.cleanup(tmp)
296296

297-
to := NewObjectWriter(tmp)
297+
to := NewObjectWriter(tmp, d.Hasher())
298298
if _, err = to.WriteHeader(object.Type(), int64(cn)); err != nil {
299299
return nil, 0, err
300300
}

object_writer.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package gitobj
22

33
import (
44
"compress/zlib"
5-
"crypto/sha1"
65
"fmt"
76
"hash"
87
"io"
@@ -50,18 +49,20 @@ func (n *nopCloser) Close() error {
5049
}
5150

5251
// NewObjectWriter returns a new *ObjectWriter instance that drains incoming
53-
// writes into the io.Writer given, "w".
54-
func NewObjectWriter(w io.Writer) *ObjectWriter {
55-
return NewObjectWriteCloser(&nopCloser{w})
52+
// writes into the io.Writer given, "w". "hash" is a hash instance from the
53+
// ObjectDatabase'e Hash method.
54+
func NewObjectWriter(w io.Writer, hash hash.Hash) *ObjectWriter {
55+
return NewObjectWriteCloser(&nopCloser{w}, hash)
5656
}
5757

5858
// NewObjectWriter returns a new *ObjectWriter instance that drains incoming
59-
// writes into the io.Writer given, "w".
59+
// writes into the io.Writer given, "w". "sum" is a hash instance from the
60+
// ObjectDatabase'e Hash method.
6061
//
6162
// Upon closing, it calls the given Close() function of the io.WriteCloser.
62-
func NewObjectWriteCloser(w io.WriteCloser) *ObjectWriter {
63+
func NewObjectWriteCloser(w io.WriteCloser, sum hash.Hash) *ObjectWriter {
6364
zw := zlib.NewWriter(w)
64-
sum := sha1.New()
65+
sum.Reset()
6566

6667
return &ObjectWriter{
6768
w: io.MultiWriter(zw, sum),

object_writer_test.go

+44-18
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ package gitobj
33
import (
44
"bytes"
55
"compress/zlib"
6+
"crypto/sha1"
7+
"crypto/sha256"
68
"encoding/hex"
79
"errors"
10+
"hash"
811
"io"
912
"io/ioutil"
1013
"sync/atomic"
@@ -16,7 +19,7 @@ import (
1619
func TestObjectWriterWritesHeaders(t *testing.T) {
1720
var buf bytes.Buffer
1821

19-
w := NewObjectWriter(&buf)
22+
w := NewObjectWriter(&buf, sha1.New())
2023

2124
n, err := w.WriteHeader(BlobObjectType, 1)
2225
assert.Equal(t, 7, n)
@@ -35,25 +38,40 @@ func TestObjectWriterWritesHeaders(t *testing.T) {
3538
}
3639

3740
func TestObjectWriterWritesData(t *testing.T) {
38-
var buf bytes.Buffer
41+
testCases := []struct {
42+
h hash.Hash
43+
sha string
44+
}{
45+
{
46+
sha1.New(), "56a6051ca2b02b04ef92d5150c9ef600403cb1de",
47+
},
48+
{
49+
sha256.New(), "36456d9b87f21fc54ed5babf1222a9ab0fbbd0c4ad239a7933522d5e4447049c",
50+
},
51+
}
3952

40-
w := NewObjectWriter(&buf)
41-
w.WriteHeader(BlobObjectType, 1)
53+
for _, test := range testCases {
54+
var buf bytes.Buffer
4255

43-
n, err := w.Write([]byte{0x31})
44-
assert.Equal(t, 1, n)
45-
assert.Nil(t, err)
56+
w := NewObjectWriter(&buf, test.h)
57+
w.WriteHeader(BlobObjectType, 1)
4658

47-
assert.Nil(t, w.Close())
59+
n, err := w.Write([]byte{0x31})
60+
assert.Equal(t, 1, n)
61+
assert.Nil(t, err)
4862

49-
r, err := zlib.NewReader(&buf)
50-
assert.Nil(t, err)
63+
assert.Nil(t, w.Close())
5164

52-
all, err := ioutil.ReadAll(r)
53-
assert.Nil(t, err)
54-
assert.Equal(t, []byte("blob 1\x001"), all)
65+
r, err := zlib.NewReader(&buf)
66+
assert.Nil(t, err)
5567

56-
assert.Nil(t, r.Close())
68+
all, err := ioutil.ReadAll(r)
69+
assert.Nil(t, err)
70+
assert.Equal(t, []byte("blob 1\x001"), all)
71+
72+
assert.Nil(t, r.Close())
73+
assert.Equal(t, test.sha, hex.EncodeToString(w.Sha()))
74+
}
5775
}
5876

5977
func TestObjectWriterPanicsOnWritesWithoutHeader(t *testing.T) {
@@ -64,7 +82,7 @@ func TestObjectWriterPanicsOnWritesWithoutHeader(t *testing.T) {
6482
assert.Equal(t, "gitobj: cannot write data without header", err)
6583
}()
6684

67-
w := NewObjectWriter(new(bytes.Buffer))
85+
w := NewObjectWriter(new(bytes.Buffer), sha1.New())
6886
w.Write(nil)
6987
}
7088

@@ -76,19 +94,27 @@ func TestObjectWriterPanicsOnMultipleHeaderWrites(t *testing.T) {
7694
assert.Equal(t, "gitobj: cannot write headers more than once", err)
7795
}()
7896

79-
w := NewObjectWriter(new(bytes.Buffer))
97+
w := NewObjectWriter(new(bytes.Buffer), sha1.New())
8098
w.WriteHeader(BlobObjectType, 1)
8199
w.WriteHeader(TreeObjectType, 2)
82100
}
83101

84102
func TestObjectWriterKeepsTrackOfHash(t *testing.T) {
85-
w := NewObjectWriter(new(bytes.Buffer))
103+
w := NewObjectWriter(new(bytes.Buffer), sha1.New())
86104
n, err := w.WriteHeader(BlobObjectType, 1)
87105

88106
assert.Nil(t, err)
89107
assert.Equal(t, 7, n)
90108

91109
assert.Equal(t, "bb6ca78b66403a67c6281df142de5ef472186283", hex.EncodeToString(w.Sha()))
110+
111+
w = NewObjectWriter(new(bytes.Buffer), sha256.New())
112+
n, err = w.WriteHeader(BlobObjectType, 1)
113+
114+
assert.Nil(t, err)
115+
assert.Equal(t, 7, n)
116+
117+
assert.Equal(t, "3a68c454a6eb75cc55bda147a53756f0f581497eb80b9b67156fb8a8d3931cd7", hex.EncodeToString(w.Sha()))
92118
}
93119

94120
type WriteCloserFn struct {
@@ -109,7 +135,7 @@ func TestObjectWriterCallsClose(t *testing.T) {
109135
atomic.AddUint32(&calls, 1)
110136
return expected
111137
},
112-
})
138+
}, sha1.New())
113139

114140
got := w.Close()
115141

0 commit comments

Comments
 (0)