aboutsummaryrefslogtreecommitdiff
path: root/format/pack/ingest/rewrite_header_trailer.go
blob: c5f858decb722483cb9d674a45057c67b639fb7f (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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package ingest

import (
	"encoding/binary"
	"io"

	"codeberg.org/lindenii/furgit/internal/intconv"
	"codeberg.org/lindenii/furgit/objectid"
)

// rewritePackHeaderAndTrailer rewrites object count and trailer hash using ReadAt/WriteAt.
func rewritePackHeaderAndTrailer(state *ingestState) error {
	var countRaw [4]byte

	recordCountUint32, err := intconv.IntToUint32(len(state.records))
	if err != nil {
		return err
	}

	binary.BigEndian.PutUint32(countRaw[:], recordCountUint32)

	_, err = state.packFile.WriteAt(countRaw[:], 8)
	if err != nil {
		return err
	}

	info, err := state.packFile.Stat()
	if err != nil {
		return err
	}

	endWithoutTrailer := info.Size()

	hashImpl, err := state.algo.New()
	if err != nil {
		return err
	}

	var (
		buf [128 << 10]byte
		pos int64
	)
	for pos < endWithoutTrailer {
		want := int64(len(buf))

		remaining := endWithoutTrailer - pos
		if remaining < want {
			want = remaining
		}

		n, err := state.packFile.ReadAt(buf[:want], pos)
		if err != nil && err != io.EOF {
			return err
		}

		if n == 0 {
			return io.ErrUnexpectedEOF
		}

		_, _ = hashImpl.Write(buf[:n])
		pos += int64(n)
	}

	sum := hashImpl.Sum(nil)

	_, err = state.packFile.WriteAt(sum, endWithoutTrailer)
	if err != nil {
		return err
	}

	packHash, err := objectid.FromBytes(state.algo, sum)
	if err != nil {
		return err
	}

	state.packHash = packHash
	state.objectCountHeader = recordCountUint32

	sumLenInt64 := int64(len(sum))

	newConsumed, err := intconv.Int64ToUint64(endWithoutTrailer + sumLenInt64)
	if err != nil {
		return err
	}

	state.stream.consumed = newConsumed

	return nil
}