aboutsummaryrefslogtreecommitdiffstats
path: root/formats/packfile/stream.go
blob: 34ffd2fce2aa572b27931972a6d6d9e5b064eea0 (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
package packfile

import (
	"io"

	"gopkg.in/src-d/go-git.v4/core"
)

// Stream implements ReadRecaller for the io.Reader of a packfile.  This
// implementation keeps all remembered objects referenced in maps for
// quick access.
type Stream struct {
	io.Reader
	count          int64
	offsetToObject map[int64]core.Object
	hashToObject   map[core.Hash]core.Object
}

// NewStream returns a new Stream that reads form r.
func NewStream(r io.Reader) *Stream {
	return &Stream{
		Reader:         r,
		count:          0,
		hashToObject:   make(map[core.Hash]core.Object, 0),
		offsetToObject: make(map[int64]core.Object, 0),
	}
}

// Read reads up to len(p) bytes into p.
func (r *Stream) Read(p []byte) (n int, err error) {
	n, err = r.Reader.Read(p)
	r.count += int64(n)

	return
}

// ReadByte reads a byte.
func (r *Stream) ReadByte() (byte, error) {
	var p [1]byte
	_, err := r.Reader.Read(p[:])
	r.count++

	return p[0], err
}

// Offset returns the number of bytes read.
func (r *Stream) Offset() (int64, error) {
	return r.count, nil
}

// Remember stores references to the passed object to be used later by
// RecalByHash and RecallByOffset. It receives the object and the offset
// of its object entry in the packfile.
func (r *Stream) Remember(o int64, obj core.Object) error {
	h := obj.Hash()
	if _, ok := r.hashToObject[h]; ok {
		return ErrDuplicatedObject.AddDetails("with hash %s", h)
	}
	r.hashToObject[h] = obj

	if _, ok := r.offsetToObject[o]; ok {
		return ErrDuplicatedObject.AddDetails("with offset %d", o)
	}
	r.offsetToObject[o] = obj

	return nil
}

// ForgetAll forgets all previously remembered objects.
func (r *Stream) ForgetAll() {
	r.hashToObject = make(map[core.Hash]core.Object)
	r.offsetToObject = make(map[int64]core.Object)
}

// RecallByHash returns an object that has been previously Remember-ed by
// its hash.
func (r *Stream) RecallByHash(h core.Hash) (core.Object, error) {
	obj, ok := r.hashToObject[h]
	if !ok {
		return nil, ErrCannotRecall.AddDetails("by hash %s", h)
	}

	return obj, nil
}

// RecallByOffset returns an object that has been previously Remember-ed by
// the offset of its object entry in the packfile.
func (r *Stream) RecallByOffset(o int64) (core.Object, error) {
	obj, ok := r.offsetToObject[o]
	if !ok {
		return nil, ErrCannotRecall.AddDetails("no object found at offset %d", o)
	}

	return obj, nil
}