Skip to content

Commit 39dda21

Browse files
committed
storage: implement a wrapper for multiple storage implementations
Some storage backends need to wrap multiple storage implementations, such as the file backend: we need to read from both loose objects and packs. In order to make these two backends appear to be one unified implementation, add a wrapper implementation that reads from whichever implementation provides the object. Implement automatic decompression, since we may end up with some implementations which decompress automatically and others which do not.
1 parent 0128a8e commit 39dda21

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package storage
2+
3+
import (
4+
"compress/zlib"
5+
"io"
6+
)
7+
8+
// decompressingReadCloser wraps zlib.NewReader to ensure that both the zlib
9+
// reader and its underlying type are closed.
10+
type decompressingReadCloser struct {
11+
r io.ReadCloser
12+
zr io.ReadCloser
13+
}
14+
15+
// newDecompressingReadCloser creates a new wrapped zlib reader
16+
func newDecompressingReadCloser(r io.ReadCloser) (io.ReadCloser, error) {
17+
zr, err := zlib.NewReader(r)
18+
if err != nil {
19+
return nil, err
20+
}
21+
return &decompressingReadCloser{r: r, zr: zr}, nil
22+
}
23+
24+
// Read implements io.ReadCloser.
25+
func (d *decompressingReadCloser) Read(b []byte) (int, error) {
26+
return d.zr.Read(b)
27+
}
28+
29+
// Close implements io.ReadCloser.
30+
func (d *decompressingReadCloser) Close() error {
31+
if err := d.zr.Close(); err != nil {
32+
return err
33+
}
34+
return d.r.Close()
35+
}

storage/multi_storage.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package storage
2+
3+
import (
4+
"io"
5+
6+
"github.com/git-lfs/gitobj/errors"
7+
)
8+
9+
// Storage implements an interface for reading, but not writing, objects in an
10+
// object database.
11+
type multiStorage struct {
12+
impls []Storage
13+
}
14+
15+
func MultiStorage(args ...Storage) Storage {
16+
return &multiStorage{impls: args}
17+
}
18+
19+
// Open returns a handle on an existing object keyed by the given object
20+
// ID. It returns an error if that file does not already exist.
21+
func (m *multiStorage) Open(oid []byte) (f io.ReadCloser, err error) {
22+
for _, s := range m.impls {
23+
f, err := s.Open(oid)
24+
if err != nil {
25+
if errors.IsNoSuchObject(err) {
26+
continue
27+
}
28+
return nil, err
29+
}
30+
if s.IsCompressed() {
31+
return newDecompressingReadCloser(f)
32+
}
33+
return f, nil
34+
}
35+
return nil, errors.NoSuchObject(oid)
36+
}
37+
38+
// Close closes the filesystem, after which no more operations are
39+
// allowed.
40+
func (m *multiStorage) Close() error {
41+
for _, s := range m.impls {
42+
if err := s.Close(); err != nil {
43+
return err
44+
}
45+
}
46+
return nil
47+
}
48+
49+
// Compressed indicates whether data read from this storage source will
50+
// be zlib-compressed.
51+
func (m *multiStorage) IsCompressed() bool {
52+
// To ensure we can read from any Storage type, we automatically
53+
// decompress items if they need it.
54+
return false
55+
}

0 commit comments

Comments
 (0)