git/odb: introduce *odb.Blob type
This commit is contained in:
parent
f498b7b180
commit
bcc36ba72f
64
git/odb/blob.go
Normal file
64
git/odb/blob.go
Normal file
@ -0,0 +1,64 @@
|
||||
package odb
|
||||
|
||||
import "io"
|
||||
|
||||
// Blob represents a Git object of type "blob".
|
||||
type Blob struct {
|
||||
// Size is the total uncompressed size of the blob's contents.
|
||||
Size int64
|
||||
// Contents is a reader that yields the uncompressed blob contents. It
|
||||
// may only be read once. It may or may not implement io.ReadSeeker.
|
||||
Contents io.Reader
|
||||
|
||||
// closeFn is a function that is called to free any resources held by
|
||||
// the Blob. In particular, this will close a file, if the Blob is
|
||||
// being read from a file on disk.
|
||||
closeFn func() error
|
||||
}
|
||||
|
||||
var _ Object = (*Blob)(nil)
|
||||
|
||||
// Type implements Object.ObjectType by returning the correct object type for
|
||||
// Blobs, BlobObjectType.
|
||||
func (b *Blob) Type() ObjectType { return BlobObjectType }
|
||||
|
||||
// Decode implements Object.Decode and decodes the uncompressed blob contents
|
||||
// being read. It returns the number of bytes that it consumed off of the
|
||||
// stream, which is always zero.
|
||||
//
|
||||
// If any error(s) was(were) encountered while reading the blob, that error will
|
||||
// be returned.
|
||||
func (b *Blob) Decode(r io.Reader, size int64) (n int, err error) {
|
||||
b.Size = size
|
||||
b.Contents = io.LimitReader(r, size)
|
||||
|
||||
b.closeFn = func() error {
|
||||
if closer, ok := r.(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Encode encodes the blob's contents to the given io.Writer, "w". If there was
|
||||
// any error copying the blob's contents, that error will be returned.
|
||||
//
|
||||
// Otherwise, the number of bytes written will be returned.
|
||||
func (b *Blob) Encode(to io.Writer) (n int, err error) {
|
||||
nn, err := io.Copy(to, b.Contents)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(nn), err
|
||||
}
|
||||
|
||||
// Closes closes any resources held by the open Blob, or returns nil if there
|
||||
// were no errors.
|
||||
func (b *Blob) Close() error {
|
||||
if b.closeFn == nil {
|
||||
return nil
|
||||
}
|
||||
return b.closeFn()
|
||||
}
|
74
git/odb/blob_test.go
Normal file
74
git/odb/blob_test.go
Normal file
@ -0,0 +1,74 @@
|
||||
package odb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBlobReturnsCorrectObjectType(t *testing.T) {
|
||||
assert.Equal(t, BlobObjectType, new(Blob).Type())
|
||||
}
|
||||
|
||||
func TestBlobEncoding(t *testing.T) {
|
||||
const contents = "Hello, world!\n"
|
||||
|
||||
b := &Blob{
|
||||
Size: int64(len(contents)),
|
||||
Contents: strings.NewReader(contents),
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if _, err := b.Encode(&buf); err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
assert.Equal(t, contents, (&buf).String())
|
||||
}
|
||||
|
||||
func TestBlobDecoding(t *testing.T) {
|
||||
const contents = "Hello, world!\n"
|
||||
from := strings.NewReader(contents)
|
||||
|
||||
b := new(Blob)
|
||||
n, err := b.Decode(from, int64(len(contents)))
|
||||
|
||||
assert.Equal(t, 0, n)
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.EqualValues(t, len(contents), b.Size)
|
||||
|
||||
got, err := ioutil.ReadAll(b.Contents)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, []byte(contents), got)
|
||||
}
|
||||
|
||||
func TestBlobCallCloseFn(t *testing.T) {
|
||||
var calls uint32
|
||||
|
||||
expected := errors.New("some close error")
|
||||
|
||||
b := &Blob{
|
||||
closeFn: func() error {
|
||||
atomic.AddUint32(&calls, 1)
|
||||
return expected
|
||||
},
|
||||
}
|
||||
|
||||
got := b.Close()
|
||||
|
||||
assert.Equal(t, expected, got)
|
||||
assert.EqualValues(t, 1, calls)
|
||||
}
|
||||
|
||||
func TestBlobCanCloseWithoutCloseFn(t *testing.T) {
|
||||
b := &Blob{
|
||||
closeFn: nil,
|
||||
}
|
||||
|
||||
assert.Nil(t, b.Close())
|
||||
}
|
Loading…
Reference in New Issue
Block a user