aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/format/packfile/fsobject.go
blob: a395d171ce4345f894deb4ea222f5f3ee5e00a9f (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
132
133
134
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
	largeObjectThreshold int64
}

// 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,
	largeObjectThreshold int64,
) *FSObject {
	return &FSObject{
		hash:                 hash,
		offset:               offset,
		size:                 contentSize,
		typ:                  finalType,
		index:                index,
		fs:                   fs,
		path:                 path,
		cache:                cache,
		largeObjectThreshold: largeObjectThreshold,
	}
}

// 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, o.largeObjectThreshold)
	if o.largeObjectThreshold > 0 && o.size > o.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()
}