From 15855e3249754ab7dc07183c9383f8a8e8c26af2 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Tue, 11 Nov 2025 00:00:00 +0000 Subject: Initial commit --- obj.go | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 obj.go (limited to 'obj.go') 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 +} -- cgit v1.3.1-10-gc9f91