aboutsummaryrefslogtreecommitdiff
path: root/format/pack/ingest/rev_write.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-05 18:24:40 +0800
committerGravatar Runxi Yu2026-03-05 19:05:47 +0800
commit57f1818d547f2f1dca38033b4e29f62d89ef80f9 (patch)
tree88d55ac38e2427860bf380c8cce42fcb3bb1e9ee /format/pack/ingest/rev_write.go
parentinternal/compress/zlib: Use flate's compression consumed counter (diff)
signatureNo signature
format/pack/ingest: Init
Diffstat (limited to 'format/pack/ingest/rev_write.go')
-rw-r--r--format/pack/ingest/rev_write.go97
1 files changed, 97 insertions, 0 deletions
diff --git a/format/pack/ingest/rev_write.go b/format/pack/ingest/rev_write.go
new file mode 100644
index 00000000..cf95c782
--- /dev/null
+++ b/format/pack/ingest/rev_write.go
@@ -0,0 +1,97 @@
+package ingest
+
+import (
+ "encoding/binary"
+ "slices"
+
+ "codeberg.org/lindenii/furgit/objectid"
+)
+
+const (
+ revMagic = 0x52494458
+ revVersion = 1
+)
+
+// writeRev writes rev index for resolved records.
+func writeRev(state *ingestState) error {
+ if !state.writeRev {
+ return nil
+ }
+
+ idxOrder := buildIdxOrder(state)
+ recordToIdxPos := make([]int, len(state.records))
+ for pos, recordIdx := range idxOrder {
+ recordToIdxPos[recordIdx] = pos
+ }
+ packOrder := buildPackOrder(state)
+
+ hashImpl, err := state.algo.New()
+ if err != nil {
+ return err
+ }
+
+ var scratch [8]byte
+ binary.BigEndian.PutUint32(scratch[:4], revMagic)
+ if err := writeAndHash(state.revFile, hashImpl, scratch[:4]); err != nil {
+ return err
+ }
+ binary.BigEndian.PutUint32(scratch[:4], revVersion)
+ if err := writeAndHash(state.revFile, hashImpl, scratch[:4]); err != nil {
+ return err
+ }
+ binary.BigEndian.PutUint32(scratch[:4], hashID(state.algo))
+ if err := writeAndHash(state.revFile, hashImpl, scratch[:4]); err != nil {
+ return err
+ }
+
+ for _, recordIdx := range packOrder {
+ binary.BigEndian.PutUint32(scratch[:4], uint32(recordToIdxPos[recordIdx]))
+ if err := writeAndHash(state.revFile, hashImpl, scratch[:4]); err != nil {
+ return err
+ }
+ }
+
+ if err := writeAndHash(state.revFile, hashImpl, state.packHash.Bytes()); err != nil {
+ return err
+ }
+ revHash := hashImpl.Sum(nil)
+ if _, err := state.revFile.Write(revHash); err != nil {
+ return err
+ }
+
+ return state.revFile.Sync()
+}
+
+// buildPackOrder returns record indexes sorted by pack offset.
+func buildPackOrder(state *ingestState) []int {
+ out := make([]int, 0, len(state.records))
+ for idx := range state.records {
+ out = append(out, idx)
+ }
+ slices.SortFunc(out, func(a, b int) int {
+ offA := state.records[a].offset
+ offB := state.records[b].offset
+ switch {
+ case offA < offB:
+ return -1
+ case offA > offB:
+ return 1
+ default:
+ return 0
+ }
+ })
+
+ return out
+}
+
+// hashID converts object algorithm to pack hash-id encoding.
+func hashID(algo objectid.Algorithm) uint32 {
+ switch algo {
+ case objectid.AlgorithmSHA1:
+ return 1
+ case objectid.AlgorithmSHA256:
+ return 2
+ default:
+ return 0
+ }
+}