diff options
| author | 2026-03-05 18:24:40 +0800 | |
|---|---|---|
| committer | 2026-03-05 19:05:47 +0800 | |
| commit | 57f1818d547f2f1dca38033b4e29f62d89ef80f9 (patch) | |
| tree | 88d55ac38e2427860bf380c8cce42fcb3bb1e9ee /format/pack/ingest/rev_write.go | |
| parent | internal/compress/zlib: Use flate's compression consumed counter (diff) | |
| signature | No signature | |
format/pack/ingest: Init
Diffstat (limited to 'format/pack/ingest/rev_write.go')
| -rw-r--r-- | format/pack/ingest/rev_write.go | 97 |
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 + } +} |
