aboutsummaryrefslogtreecommitdiff
path: root/obj.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2025-11-11 00:00:00 +0000
committerGravatar Runxi Yu2025-11-13 00:00:00 +0000
commit15855e3249754ab7dc07183c9383f8a8e8c26af2 (patch)
tree83b32bdd63f7e672152f07d89268e9b268d1f3f5 /obj.go
signature
Initial commit
Diffstat (limited to 'obj.go')
-rw-r--r--obj.go119
1 files changed, 119 insertions, 0 deletions
diff --git a/obj.go b/obj.go
new file mode 100644
index 00000000..5ce639f9
--- /dev/null
+++ b/obj.go
@@ -0,0 +1,119 @@
+package furgit
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strconv"
+)
+
+// ObjType mirrors Git's object type tags.
+type ObjType uint8
+
+const (
+ ObjInvalid ObjType = 0
+ ObjCommit ObjType = 1
+ ObjTree ObjType = 2
+ ObjBlob ObjType = 3
+ ObjTag ObjType = 4
+ ObjFuture ObjType = 5
+ ObjOfsDelta ObjType = 6
+ ObjRefDelta ObjType = 7
+)
+
+const (
+ objNameBlob = "blob"
+ objNameTree = "tree"
+ objNameCommit = "commit"
+ objNameTag = "tag"
+)
+
+// Object describes any Git object variant.
+type Object interface {
+ ObjType() ObjType
+}
+
+type objectBase struct {
+ Hash Hash
+}
+
+func computeRawHash(data []byte) Hash {
+ var id Hash
+ sum := newHash(data)
+ copy(id[:], sum[:])
+ return id
+}
+
+func headerForType(ty ObjType, body []byte) ([]byte, error) {
+ var tyStr string
+ switch ty {
+ case ObjBlob:
+ tyStr = objNameBlob
+ case ObjTree:
+ tyStr = objNameTree
+ case ObjCommit:
+ tyStr = objNameCommit
+ case ObjTag:
+ tyStr = objNameTag
+ case ObjInvalid, ObjFuture, ObjOfsDelta, ObjRefDelta:
+ return nil, fmt.Errorf("furgit: object: unsupported type %d", ty)
+ default:
+ return nil, fmt.Errorf("furgit: object: unsupported type %d", ty)
+ }
+ size := strconv.Itoa(len(body))
+ var buf bytes.Buffer
+ buf.Grow(len(tyStr) + len(size) + 1)
+ buf.WriteString(tyStr)
+ buf.WriteByte(' ')
+ buf.WriteString(size)
+ buf.WriteByte(0)
+ return buf.Bytes(), nil
+}
+
+func verifyRawObject(buf []byte, want Hash) bool {
+ return computeRawHash(buf) == want
+}
+
+func verifyTypedObject(ty ObjType, body []byte, want Hash) bool {
+ header, err := headerForType(ty, body)
+ if err != nil {
+ return false
+ }
+ raw := make([]byte, len(header)+len(body))
+ copy(raw, header)
+ copy(raw[len(header):], body)
+ return computeRawHash(raw) == want
+}
+
+func parseObjectBody(ty ObjType, id Hash, body []byte) (Object, error) {
+ switch ty {
+ case ObjBlob:
+ return parseBlob(id, body)
+ case ObjTree:
+ return parseTree(id, body)
+ case ObjCommit:
+ return parseCommit(id, body)
+ case ObjTag:
+ return parseTag(id, body)
+ case ObjInvalid, ObjFuture, ObjOfsDelta, ObjRefDelta:
+ return nil, fmt.Errorf("furgit: object: unsupported type %d", ty)
+ default:
+ return nil, fmt.Errorf("furgit: object: unknown type %d", ty)
+ }
+}
+
+// ReadObject resolves an ID by consulting loose then packed storage.
+func (repo *Repository) ReadObject(id Hash) (Object, error) {
+ obj, err := repo.looseRead(id)
+ if err == nil {
+ return obj, nil
+ }
+ if !errors.Is(err, ErrNotFound) {
+ return nil, err
+ }
+ obj, err = repo.packRead(id)
+ if errors.Is(err, ErrNotFound) {
+ return nil, ErrInvalidObject
+ }
+ return obj, err
+}