aboutsummaryrefslogtreecommitdiff
path: root/object/store/packed/entry.go
blob: 908afad0dc443a5964ed0a4db38a2eb653979b44 (about) (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
package packed

import (
	"errors"
	"fmt"
	"io"

	"lindenii.org/go/furgit/internal/compress/zlib"
	"lindenii.org/go/furgit/internal/format/packfile"
	"lindenii.org/go/furgit/object/id"
	"lindenii.org/go/lgo/intconv"
)

var errPayloadOverlong = errors.New("entry payload longer than declared")

// entryHeaderAt parses the entry header at offset,
// returning it together with the entry's compressed payload view.
//
// The entry header only contains the inflated length,
// so payload slice extends to the end of the pack;
// the compressed data length is determined by the zlib stream end,
// not the slice length.
//
// Labels: Life-Parent, Mut-No.
func (pack *pack) entryHeaderAt(offset int, objectFormat id.ObjectFormat) (packfile.EntryHeader, []byte, error) {
	var zero packfile.EntryHeader

	pos := offset
	if pos < 0 || pos >= len(pack.data) {
		return zero, nil, fmt.Errorf("%w: pack %q: entry offset out of bounds", ErrMalformedPackedStore, pack.name)
	}

	header, err := packfile.ParseEntryHeader(pack.data[pos:], objectFormat.Size())
	if err != nil {
		return zero, nil, fmt.Errorf("%w: pack %q: %w", ErrMalformedPackedStore, pack.name, err)
	}

	return header, pack.data[pos+header.HeaderLen:], nil
}

// inflate decompresses one entry payload of expectedSize bytes,
// rejecting payloads whose inflated size differs.
//
// Labels: Life-Independent.
func inflate(payload []byte, expectedSize uint64) ([]byte, error) {
	size, err := intconv.Uint64ToInt(expectedSize)
	if err != nil {
		return nil, fmt.Errorf("declared size: %w", err)
	}

	zr, err := zlib.NewReaderBytes(payload)
	if err != nil {
		return nil, fmt.Errorf("inflating entry payload: %w", err)
	}

	defer func() { _ = zr.Close() }()

	out := make([]byte, size)

	_, err = io.ReadFull(zr, out)
	if err != nil {
		return nil, fmt.Errorf("inflating entry payload: %w", err)
	}

	var probe [1]byte

	n, err := zr.Read(probe[:])
	if n != 0 || !errors.Is(err, io.EOF) {
		return nil, errPayloadOverlong
	}

	return out, nil
}