diff options
Diffstat (limited to 'format/packfile/ingest/rev_write.go')
| -rw-r--r-- | format/packfile/ingest/rev_write.go | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/format/packfile/ingest/rev_write.go b/format/packfile/ingest/rev_write.go new file mode 100644 index 00000000..f8c30c1b --- /dev/null +++ b/format/packfile/ingest/rev_write.go @@ -0,0 +1,138 @@ +package ingest + +import ( + "encoding/binary" + "slices" + + "codeberg.org/lindenii/furgit/internal/intconv" + "codeberg.org/lindenii/furgit/internal/progress" +) + +const ( + revMagic = 0x52494458 + revVersion = 1 +) + +// writeRev writes rev index for resolved records. +func writeRev(state *ingestState) error { + if !state.opts.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 + + writeProgressf(state, "writing reverse index header...\r") + binary.BigEndian.PutUint32(scratch[:4], revMagic) + + err = writeAndHash(state.revFile, hashImpl, scratch[:4]) + if err != nil { + return err + } + + binary.BigEndian.PutUint32(scratch[:4], revVersion) + + err = writeAndHash(state.revFile, hashImpl, scratch[:4]) + if err != nil { + return err + } + + binary.BigEndian.PutUint32(scratch[:4], state.algo.PackHashID()) + + err = writeAndHash(state.revFile, hashImpl, scratch[:4]) + if err != nil { + return err + } + + writeProgressf(state, "writing reverse index header: done.\n") + + entriesMeter := progress.New(progress.Options{ + Writer: state.opts.Progress, + Flush: state.opts.ProgressFlush, + Title: "writing reverse index entries", + Total: uint64(len(packOrder)), + }) + + var entriesDone uint64 + + for _, recordIdx := range packOrder { + recordPos, err := intconv.IntToUint32(recordToIdxPos[recordIdx]) + if err != nil { + return err + } + + binary.BigEndian.PutUint32(scratch[:4], recordPos) + + err = writeAndHash(state.revFile, hashImpl, scratch[:4]) + if err != nil { + return err + } + + entriesDone++ + entriesMeter.Set(entriesDone, 0) + } + + if entriesDone > 0 { + entriesMeter.Stop("done") + } + + writeProgressf(state, "writing reverse index trailer...\r") + + err = writeAndHash(state.revFile, hashImpl, state.packHash.Bytes()) + if err != nil { + return err + } + + revHash := hashImpl.Sum(nil) + + _, err = state.revFile.Write(revHash) + if err != nil { + return err + } + + err = state.revFile.Sync() + if err != nil { + return err + } + + writeProgressf(state, "writing reverse index trailer: done.\n") + + return nil +} + +// 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 +} |
