Welcome to mirror list, hosted at ThFree Co, Russian Federation.

trailer.go « gitio « git « internal - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: fd60ca8df8720338ce7ce31f4e5be90bea155f8b (plain)
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
package gitio

import (
	"fmt"
	"io"
)

// TrailerReader models the behavior of Git hashfiles where the last N
// bytes of the underlying reader are not part of the content.
// TrailerReader acts like an io.Reader but will always hold back the
// last N bytes. Once the underlying reader has reached EOF, the trailer
// (the last N bytes) can be retrieved with the Trailer() method.
type TrailerReader struct {
	r           io.Reader
	start, end  int
	trailerSize int
	buf         []byte
	atEOF       bool
}

// NewTrailerReader returns a new TrailerReader. The returned
// TrailerReader will never return the last trailerSize bytes of r; to
// get to those bytes, first read the TrailerReader to EOF and then call
// Trailer().
func NewTrailerReader(r io.Reader, trailerSize int) *TrailerReader {
	const bufSize = 8192
	if trailerSize >= bufSize {
		panic("trailerSize too large for TrailerReader")
	}

	return &TrailerReader{
		r:           r,
		trailerSize: trailerSize,
		buf:         make([]byte, bufSize),
	}
}

// Trailer yields the last trailerSize bytes of the underlying reader of
// tr. If the underlying reader has not reached EOF yet Trailer will
// return an error.
func (tr *TrailerReader) Trailer() ([]byte, error) {
	bufLen := tr.end - tr.start
	if !tr.atEOF || bufLen > tr.trailerSize {
		return nil, fmt.Errorf("cannot get trailer before reader has reached EOF")
	}

	if bufLen < tr.trailerSize {
		return nil, fmt.Errorf("not enough bytes to yield trailer")
	}

	return tr.buf[tr.end-tr.trailerSize : tr.end], nil
}

func (tr *TrailerReader) Read(p []byte) (int, error) {
	if bufLen := tr.end - tr.start; !tr.atEOF && bufLen <= tr.trailerSize {
		copy(tr.buf, tr.buf[tr.start:tr.end])
		tr.start = 0
		tr.end = bufLen

		n, err := tr.r.Read(tr.buf[tr.end:])
		if err != nil {
			if err != io.EOF {
				return 0, err
			}
			tr.atEOF = true
		}
		tr.end += n
	}

	if tr.end-tr.start <= tr.trailerSize {
		if tr.atEOF {
			return 0, io.EOF
		}
		return 0, nil
	}

	n := copy(p, tr.buf[tr.start:tr.end-tr.trailerSize])
	tr.start += n
	return n, nil
}