aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/format/packfile/fsobject.go
blob: 4aa3c8e01fcd3a5826cd60395caac10f9ea54990 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package packfile

import (
	"io"

	billy "github.com/go-git/go-billy/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/cache"
	"github.com/go-git/go-git/v5/plumbing/format/idxfile"
	"github.com/go-git/go-git/v5/utils/ioutil"
)

// FSObject is an object from the packfile on the filesystem.
type FSObject struct {
	hash   plumbing.Hash
	h      *ObjectHeader
	offset int64
	size   int64
	typ    plumbing.ObjectType
	index  idxfile.Index
	fs     billy.Filesystem
	path   string
	cache  cache.Object
}

// NewFSObject creates a new filesystem object.
func NewFSObject(
	hash plumbing.Hash,
	finalType plumbing.ObjectType,
	offset int64,
	contentSize int64,
	index idxfile.Index,
	fs billy.Filesystem,
	path string,
	cache cache.Object,
) *FSObject {
	return &FSObject{
		hash:   hash,
		offset: offset,
		size:   contentSize,
		typ:    finalType,
		index:  index,
		fs:     fs,
		path:   path,
		cache:  cache,
	}
}

// Reader implements the plumbing.EncodedObject interface.
func (o *FSObject) Reader() (io.ReadCloser, error) {
	obj, ok := o.cache.Get(o.hash)
	if ok && obj != o {
		reader, err := obj.Reader()
		if err != nil {
			return nil, err
		}

		return reader, nil
	}

	f, err := o.fs.Open(o.path)
	if err != nil {
		return nil, err
	}

	p := NewPackfileWithCache(o.index, nil, f, o.cache)
	if o.size > LargeObjectThreshold {
		// We have a big object
		h, err := p.objectHeaderAtOffset(o.offset)
		if err != nil {
			return nil, err
		}

		r, err := p.getReaderDirect(h)
		if err != nil {
			_ = f.Close()
			return nil, err
		}
		return ioutil.NewReadCloserWithCloser(r, f.Close), nil
	}
	r, err := p.getObjectContent(o.offset)
	if err != nil {
		_ = f.Close()
		return nil, err
	}

	if err := f.Close(); err != nil {
		return nil, err
	}

	return r, nil
}

// SetSize implements the plumbing.EncodedObject interface. This method
// is a noop.
func (o *FSObject) SetSize(int64) {}

// SetType implements the plumbing.EncodedObject interface. This method is
// a noop.
func (o *FSObject) SetType(plumbing.ObjectType) {}

// Hash implements the plumbing.EncodedObject interface.
func (o *FSObject) Hash() plumbing.Hash { return o.hash }

// Size implements the plumbing.EncodedObject interface.
func (o *FSObject) Size() int64 { return o.size }

// Type implements the plumbing.EncodedObject interface.
func (o *FSObject) Type() plumbing.ObjectType {
	return o.typ
}

// Writer implements the plumbing.EncodedObject interface. This method always
// returns a nil writer.
func (o *FSObject) Writer() (io.WriteCloser, error) {
	return nil, nil
}

type objectReader struct {
	io.ReadCloser
	f billy.File
}

func (r *objectReader) Close() error {
	if err := r.ReadCloser.Close(); err != nil {
		_ = r.f.Close()
		return err
	}

	return r.f.Close()
}