aboutsummaryrefslogtreecommitdiff
path: root/format/pack/ingest/record_resolve.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-06 11:04:15 +0800
committerGravatar Runxi Yu2026-03-06 11:16:16 +0800
commit6945464a0438396de97b9bc28c1bd31c22456092 (patch)
tree94de06ad52d6fdff77c6d05dc5c415c65a8311e6 /format/pack/ingest/record_resolve.go
parentreachability: Split walk files (diff)
signatureNo signature
format/pack/ingest: Split files
Diffstat (limited to 'format/pack/ingest/record_resolve.go')
-rw-r--r--format/pack/ingest/record_resolve.go117
1 files changed, 117 insertions, 0 deletions
diff --git a/format/pack/ingest/record_resolve.go b/format/pack/ingest/record_resolve.go
new file mode 100644
index 00000000..6ef5e857
--- /dev/null
+++ b/format/pack/ingest/record_resolve.go
@@ -0,0 +1,117 @@
+package ingest
+
+import (
+ "fmt"
+
+ packfmt "codeberg.org/lindenii/furgit/format/pack"
+ "codeberg.org/lindenii/furgit/objecttype"
+)
+
+// resolveRecord resolves one record and returns canonical type/content.
+func resolveRecord(state *ingestState, idx int, visiting map[int]struct{}) (objecttype.Type, []byte, error) {
+ if idx < 0 || idx >= len(state.records) {
+ return objecttype.TypeInvalid, nil, fmt.Errorf("format/pack/ingest: record index out of bounds")
+ }
+
+ if _, ok := visiting[idx]; ok {
+ return objecttype.TypeInvalid, nil, &ErrDeltaCycle{Offset: state.records[idx].offset}
+ }
+
+ visiting[idx] = struct{}{}
+ defer delete(visiting, idx)
+
+ record := &state.records[idx]
+ if ty, content, ok := state.baseCache.get(idx); ok {
+ return ty, content, nil
+ }
+
+ if packfmt.IsBaseObjectType(record.packedType) {
+ ty, content, err := readBaseRecordContent(state, idx)
+ if err != nil {
+ return objecttype.TypeInvalid, nil, err
+ }
+
+ if record.resolved {
+ state.baseCache.add(idx, record.realType, content)
+
+ return record.realType, content, nil
+ }
+
+ id, err := hashCanonicalObject(state.algo, ty, content)
+ if err != nil {
+ return objecttype.TypeInvalid, nil, err
+ }
+
+ record.objectID = id
+ record.realType = ty
+ record.resolved = true
+ state.objectToRecord[id] = idx
+ state.baseCache.add(idx, ty, content)
+
+ return ty, content, nil
+ }
+
+ var (
+ baseType objecttype.Type
+ baseContent []byte
+ err error
+ )
+ switch record.packedType {
+ case objecttype.TypeOfsDelta:
+ baseIdx, ok := state.offsetToRecord[record.baseOffset]
+ if !ok {
+ return objecttype.TypeInvalid, nil, &ErrMalformedPackEntry{
+ Offset: record.offset,
+ Reason: "missing ofs-delta base entry",
+ }
+ }
+
+ baseType, baseContent, err = resolveRecord(state, baseIdx, visiting)
+ if err != nil {
+ return objecttype.TypeInvalid, nil, err
+ }
+ case objecttype.TypeRefDelta:
+ baseIdx, ok := state.objectToRecord[record.baseObject]
+ if ok {
+ baseType, baseContent, err = resolveRecord(state, baseIdx, visiting)
+ if err != nil {
+ return objecttype.TypeInvalid, nil, err
+ }
+ } else {
+ return objecttype.TypeInvalid, nil, errExternalThinBase
+ }
+ case objecttype.TypeInvalid,
+ objecttype.TypeCommit,
+ objecttype.TypeTree,
+ objecttype.TypeBlob,
+ objecttype.TypeTag,
+ objecttype.TypeFuture:
+ return objecttype.TypeInvalid, nil, &ErrMalformedPackEntry{
+ Offset: record.offset,
+ Reason: "unsupported delta type",
+ }
+ default:
+ return objecttype.TypeInvalid, nil, &ErrMalformedPackEntry{
+ Offset: record.offset,
+ Reason: "unsupported delta type",
+ }
+ }
+
+ ty, content, err := applyDeltaRecord(state, idx, baseType, baseContent)
+ if err != nil {
+ return objecttype.TypeInvalid, nil, err
+ }
+
+ id, err := hashCanonicalObject(state.algo, ty, content)
+ if err != nil {
+ return objecttype.TypeInvalid, nil, err
+ }
+
+ record.objectID = id
+ record.realType = ty
+ record.resolved = true
+ state.objectToRecord[id] = idx
+ state.baseCache.add(idx, ty, content)
+
+ return ty, content, nil
+}