shithub: furgit

Download patch

ref: 32f84b9d89d34ab74f8843f939f8d1d811d403ad
parent: e4afe122f6e0c3159969402e7a3ec47f56d36abd
author: Runxi Yu <runxiyu@umich.edu>
date: Sun Mar 29 06:14:38 EDT 2026

object/id: Split files

--- a/format/commitgraph/read/layer_lookup.go
+++ b/format/commitgraph/read/layer_lookup.go
@@ -9,7 +9,7 @@
 )
 
 func layerLookup(layer *layer, oid objectid.ObjectID) (uint32, bool) {
-	hashSize := oid.Size()
+	hashSize := oid.Algorithm().Size()
 	first := int(oid.RawBytes()[0])
 
 	var lo uint32
--- a/object/commit/serialize.go
+++ b/object/commit/serialize.go
@@ -13,7 +13,7 @@
 func (commit *Commit) SerializeWithoutHeader() ([]byte, error) {
 	var buf bytes.Buffer
 
-	if commit.Tree.Size() == 0 {
+	if commit.Tree.Algorithm().Size() == 0 {
 		return nil, errors.New("object: commit: missing tree id")
 	}
 
--- /dev/null
+++ b/object/id/algorithm.go
@@ -1,0 +1,12 @@
+package objectid
+
+//#nosec gosec
+
+// Algorithm identifies the hash algorithm used for Git object IDs.
+type Algorithm uint8
+
+const (
+	AlgorithmUnknown Algorithm = iota
+	AlgorithmSHA1
+	AlgorithmSHA256
+)
--- /dev/null
+++ b/object/id/algorithm_details.go
@@ -1,0 +1,16 @@
+package objectid
+
+import "hash"
+
+type algorithmDetails struct {
+	name       string
+	size       int
+	packHashID uint32
+	sum        func([]byte) ObjectID
+	new        func() hash.Hash
+	emptyTree  ObjectID
+}
+
+func (algo Algorithm) info() algorithmDetails {
+	return algorithmTable[algo]
+}
--- /dev/null
+++ b/object/id/algorithm_emptytree.go
@@ -1,0 +1,7 @@
+package objectid
+
+// EmptyTree returns the object ID of an empty tree ("tree 0\x00") for this
+// algorithm.
+func (algo Algorithm) EmptyTree() ObjectID {
+	return algo.info().emptyTree
+}
--- /dev/null
+++ b/object/id/algorithm_hexlen.go
@@ -1,0 +1,6 @@
+package objectid
+
+// HexLen returns the encoded hexadecimal length.
+func (algo Algorithm) HexLen() int {
+	return algo.Size() * 2
+}
--- /dev/null
+++ b/object/id/algorithm_new.go
@@ -1,0 +1,13 @@
+package objectid
+
+import "hash"
+
+// New returns a new hash.Hash for this algorithm.
+func (algo Algorithm) New() (hash.Hash, error) {
+	newFn := algo.info().new
+	if newFn == nil {
+		return nil, ErrInvalidAlgorithm
+	}
+
+	return newFn(), nil
+}
--- /dev/null
+++ b/object/id/algorithm_packhashid.go
@@ -1,0 +1,8 @@
+package objectid
+
+// PackHashID returns the Git pack/rev hash-id encoding for this algorithm.
+//
+// Unknown algorithms return 0.
+func (algo Algorithm) PackHashID() uint32 {
+	return algo.info().packHashID
+}
--- /dev/null
+++ b/object/id/algorithm_parse.go
@@ -1,0 +1,8 @@
+package objectid
+
+// ParseAlgorithm parses a canonical algorithm name (e.g. "sha1", "sha256").
+func ParseAlgorithm(s string) (Algorithm, bool) {
+	algo, ok := algorithmByName[s]
+
+	return algo, ok
+}
--- /dev/null
+++ b/object/id/algorithm_size.go
@@ -1,0 +1,6 @@
+package objectid
+
+// Size returns the hash size in bytes.
+func (algo Algorithm) Size() int {
+	return algo.info().size
+}
--- /dev/null
+++ b/object/id/algorithm_string.go
@@ -1,0 +1,11 @@
+package objectid
+
+// String returns the canonical algorithm name.
+func (algo Algorithm) String() string {
+	inf := algo.info()
+	if inf.name == "" {
+		return "unknown"
+	}
+
+	return inf.name
+}
--- /dev/null
+++ b/object/id/algorithm_sum.go
@@ -1,0 +1,6 @@
+package objectid
+
+// Sum computes an object ID from raw data using the selected algorithm.
+func (algo Algorithm) Sum(data []byte) ObjectID {
+	return algo.info().sum(data)
+}
--- /dev/null
+++ b/object/id/algorithm_supported.go
@@ -1,0 +1,7 @@
+package objectid
+
+// SupportedAlgorithms returns all object ID algorithms supported by furgit.
+// Do not mutate.
+func SupportedAlgorithms() []Algorithm {
+	return supportedAlgorithms
+}
--- /dev/null
+++ b/object/id/algorithm_tables.go
@@ -1,0 +1,63 @@
+package objectid
+
+import (
+	"crypto/sha1"
+	"crypto/sha256"
+)
+
+//nolint:gochecknoglobals
+var algorithmTable = [...]algorithmDetails{
+	AlgorithmUnknown: {},
+	AlgorithmSHA1: {
+		name:       "sha1",
+		size:       sha1.Size,
+		packHashID: 1,
+		sum: func(data []byte) ObjectID {
+			sum := sha1.Sum(data) //#nosec G401
+
+			var id ObjectID
+			copy(id.data[:], sum[:])
+			id.algo = AlgorithmSHA1
+
+			return id
+		},
+		new: sha1.New,
+	},
+	AlgorithmSHA256: {
+		name:       "sha256",
+		size:       sha256.Size,
+		packHashID: 2,
+		sum: func(data []byte) ObjectID {
+			sum := sha256.Sum256(data)
+
+			var id ObjectID
+			copy(id.data[:], sum[:])
+			id.algo = AlgorithmSHA256
+
+			return id
+		},
+		new: sha256.New,
+	},
+}
+
+var (
+	//nolint:gochecknoglobals
+	algorithmByName = map[string]Algorithm{}
+	//nolint:gochecknoglobals
+	supportedAlgorithms []Algorithm
+)
+
+func init() { //nolint:gochecknoinits
+	emptyTreeInput := []byte("tree 0\x00")
+
+	for algo := Algorithm(0); int(algo) < len(algorithmTable); algo++ {
+		info := &algorithmTable[algo]
+		if info.name == "" {
+			continue
+		}
+
+		info.emptyTree = info.sum(emptyTreeInput)
+		algorithmByName[info.name] = algo
+		supportedAlgorithms = append(supportedAlgorithms, algo)
+	}
+}
--- a/object/id/algorithms.go
+++ /dev/null
@@ -1,150 +1,0 @@
-package objectid
-
-import (
-	"crypto/sha1" //#nosec gosec
-	"crypto/sha256"
-	"hash"
-)
-
-// maxObjectIDSize MUST be >= the largest supported algorithm size.
-const maxObjectIDSize = sha256.Size
-
-// Algorithm identifies the hash algorithm used for Git object IDs.
-type Algorithm uint8
-
-const (
-	AlgorithmUnknown Algorithm = iota
-	AlgorithmSHA1
-	AlgorithmSHA256
-)
-
-type algorithmDetails struct {
-	name       string
-	size       int
-	packHashID uint32
-	sum        func([]byte) ObjectID
-	new        func() hash.Hash
-	emptyTree  ObjectID
-}
-
-//nolint:gochecknoglobals
-var algorithmTable = [...]algorithmDetails{
-	AlgorithmUnknown: {},
-	AlgorithmSHA1: {
-		name:       "sha1",
-		size:       sha1.Size,
-		packHashID: 1,
-		sum: func(data []byte) ObjectID {
-			sum := sha1.Sum(data) //#nosec G401
-
-			var id ObjectID
-			copy(id.data[:], sum[:])
-			id.algo = AlgorithmSHA1
-
-			return id
-		},
-		new: sha1.New,
-	},
-	AlgorithmSHA256: {
-		name:       "sha256",
-		size:       sha256.Size,
-		packHashID: 2,
-		sum: func(data []byte) ObjectID {
-			sum := sha256.Sum256(data)
-
-			var id ObjectID
-			copy(id.data[:], sum[:])
-			id.algo = AlgorithmSHA256
-
-			return id
-		},
-		new: sha256.New,
-	},
-}
-
-var (
-	//nolint:gochecknoglobals
-	algorithmByName = map[string]Algorithm{}
-	//nolint:gochecknoglobals
-	supportedAlgorithms []Algorithm
-)
-
-func init() { //nolint:gochecknoinits
-	emptyTreeInput := []byte("tree 0\x00")
-
-	for algo := Algorithm(0); int(algo) < len(algorithmTable); algo++ {
-		info := &algorithmTable[algo]
-		if info.name == "" {
-			continue
-		}
-
-		info.emptyTree = info.sum(emptyTreeInput)
-		algorithmByName[info.name] = algo
-		supportedAlgorithms = append(supportedAlgorithms, algo)
-	}
-}
-
-// SupportedAlgorithms returns all object ID algorithms supported by furgit.
-// Do not mutate.
-func SupportedAlgorithms() []Algorithm {
-	return supportedAlgorithms
-}
-
-// ParseAlgorithm parses a canonical algorithm name (e.g. "sha1", "sha256").
-func ParseAlgorithm(s string) (Algorithm, bool) {
-	algo, ok := algorithmByName[s]
-
-	return algo, ok
-}
-
-// Size returns the hash size in bytes.
-func (algo Algorithm) Size() int {
-	return algo.info().size
-}
-
-// String returns the canonical algorithm name.
-func (algo Algorithm) String() string {
-	inf := algo.info()
-	if inf.name == "" {
-		return "unknown"
-	}
-
-	return inf.name
-}
-
-// HexLen returns the encoded hexadecimal length.
-func (algo Algorithm) HexLen() int {
-	return algo.Size() * 2
-}
-
-// PackHashID returns the Git pack/rev hash-id encoding for this algorithm.
-//
-// Unknown algorithms return 0.
-func (algo Algorithm) PackHashID() uint32 {
-	return algo.info().packHashID
-}
-
-// Sum computes an object ID from raw data using the selected algorithm.
-func (algo Algorithm) Sum(data []byte) ObjectID {
-	return algo.info().sum(data)
-}
-
-// New returns a new hash.Hash for this algorithm.
-func (algo Algorithm) New() (hash.Hash, error) {
-	newFn := algo.info().new
-	if newFn == nil {
-		return nil, ErrInvalidAlgorithm
-	}
-
-	return newFn(), nil
-}
-
-// EmptyTree returns the object ID of an empty tree ("tree 0\x00") for this
-// algorithm.
-func (algo Algorithm) EmptyTree() ObjectID {
-	return algo.info().emptyTree
-}
-
-func (algo Algorithm) info() algorithmDetails {
-	return algorithmTable[algo]
-}
--- /dev/null
+++ b/object/id/max_size.go
@@ -1,0 +1,6 @@
+package objectid
+
+import "crypto/sha256"
+
+// maxObjectIDSize MUST be >= the largest supported algorithm size.
+const maxObjectIDSize = sha256.Size
--- a/object/id/objectid.go
+++ b/object/id/objectid.go
@@ -1,13 +1,7 @@
 package objectid
 
-import (
-	//#nosec G505
+//#nosec G505
 
-	"bytes"
-	"encoding/hex"
-	"fmt"
-)
-
 // ObjectID represents a Git object ID.
 //
 //nolint:recvcheck
@@ -14,99 +8,4 @@
 type ObjectID struct {
 	algo Algorithm
 	data [maxObjectIDSize]byte
-}
-
-// Algorithm returns the object ID's hash algorithm.
-func (id ObjectID) Algorithm() Algorithm {
-	return id.algo
-}
-
-// Size returns the object ID size in bytes.
-func (id ObjectID) Size() int {
-	return id.algo.Size()
-}
-
-// String returns the canonical hex representation.
-func (id ObjectID) String() string {
-	size := id.Size()
-
-	return hex.EncodeToString(id.data[:size])
-}
-
-// Bytes returns a copy of the object ID bytes.
-func (id ObjectID) Bytes() []byte {
-	size := id.Size()
-
-	return append([]byte(nil), id.data[:size]...)
-}
-
-// RawBytes returns a direct byte slice view of the object ID bytes.
-//
-// The returned slice aliases the object ID's internal storage. Callers MUST
-// treat it as read-only and MUST NOT modify its contents.
-//
-// Use Bytes when an independent copy is required.
-func (id *ObjectID) RawBytes() []byte {
-	size := id.Size()
-
-	return id.data[:size:size]
-}
-
-// Compare lexicographically compares two object IDs by their canonical byte
-// representation.
-func Compare(left, right ObjectID) int {
-	return bytes.Compare(left.RawBytes(), right.RawBytes())
-}
-
-// Zero returns the all-zero object ID for the specified algorithm.
-func Zero(algo Algorithm) ObjectID {
-	id, err := FromBytes(algo, make([]byte, algo.Size()))
-	if err != nil {
-		panic(err)
-	}
-
-	return id
-}
-
-// ParseHex parses an object ID from hex for the specified algorithm.
-func ParseHex(algo Algorithm, s string) (ObjectID, error) {
-	var id ObjectID
-	if algo.Size() == 0 {
-		return id, ErrInvalidAlgorithm
-	}
-
-	if len(s)%2 != 0 {
-		return id, fmt.Errorf("%w: odd hex length %d", ErrInvalidObjectID, len(s))
-	}
-
-	if len(s) != algo.HexLen() {
-		return id, fmt.Errorf("%w: got %d chars, expected %d", ErrInvalidObjectID, len(s), algo.HexLen())
-	}
-
-	decoded, err := hex.DecodeString(s)
-	if err != nil {
-		return id, fmt.Errorf("%w: decode: %w", ErrInvalidObjectID, err)
-	}
-
-	copy(id.data[:], decoded)
-	id.algo = algo
-
-	return id, nil
-}
-
-// FromBytes builds an object ID from raw bytes for the specified algorithm.
-func FromBytes(algo Algorithm, b []byte) (ObjectID, error) {
-	var id ObjectID
-	if algo.Size() == 0 {
-		return id, ErrInvalidAlgorithm
-	}
-
-	if len(b) != algo.Size() {
-		return id, fmt.Errorf("%w: got %d bytes, expected %d", ErrInvalidObjectID, len(b), algo.Size())
-	}
-
-	copy(id.data[:], b)
-	id.algo = algo
-
-	return id, nil
 }
--- /dev/null
+++ b/object/id/objectid_algorithm.go
@@ -1,0 +1,6 @@
+package objectid
+
+// Algorithm returns the object ID's hash algorithm.
+func (id ObjectID) Algorithm() Algorithm {
+	return id.algo
+}
--- /dev/null
+++ b/object/id/objectid_byte.go
@@ -1,0 +1,20 @@
+package objectid
+
+// Bytes returns a copy of the object ID bytes.
+func (id ObjectID) Bytes() []byte {
+	size := id.Algorithm().Size()
+
+	return append([]byte(nil), id.data[:size]...)
+}
+
+// RawBytes returns a direct byte slice view of the object ID bytes.
+//
+// The returned slice aliases the object ID's internal storage. Callers MUST
+// treat it as read-only and MUST NOT modify its contents.
+//
+// Use Bytes when an independent copy is required.
+func (id *ObjectID) RawBytes() []byte {
+	size := id.Algorithm().Size()
+
+	return id.data[:size:size]
+}
--- /dev/null
+++ b/object/id/objectid_compare.go
@@ -1,0 +1,9 @@
+package objectid
+
+import "bytes"
+
+// Compare lexicographically compares two object IDs by their canonical byte
+// representation.
+func Compare(left, right ObjectID) int {
+	return bytes.Compare(left.RawBytes(), right.RawBytes())
+}
--- /dev/null
+++ b/object/id/objectid_frombytes.go
@@ -1,0 +1,20 @@
+package objectid
+
+import "fmt"
+
+// FromBytes builds an object ID from raw bytes for the specified algorithm.
+func FromBytes(algo Algorithm, b []byte) (ObjectID, error) {
+	var id ObjectID
+	if algo.Size() == 0 {
+		return id, ErrInvalidAlgorithm
+	}
+
+	if len(b) != algo.Size() {
+		return id, fmt.Errorf("%w: got %d bytes, expected %d", ErrInvalidObjectID, len(b), algo.Size())
+	}
+
+	copy(id.data[:], b)
+	id.algo = algo
+
+	return id, nil
+}
--- /dev/null
+++ b/object/id/objectid_parse.go
@@ -1,0 +1,32 @@
+package objectid
+
+import (
+	"encoding/hex"
+	"fmt"
+)
+
+// ParseHex parses an object ID from hex for the specified algorithm.
+func ParseHex(algo Algorithm, s string) (ObjectID, error) {
+	var id ObjectID
+	if algo.Size() == 0 {
+		return id, ErrInvalidAlgorithm
+	}
+
+	if len(s)%2 != 0 {
+		return id, fmt.Errorf("%w: odd hex length %d", ErrInvalidObjectID, len(s))
+	}
+
+	if len(s) != algo.HexLen() {
+		return id, fmt.Errorf("%w: got %d chars, expected %d", ErrInvalidObjectID, len(s), algo.HexLen())
+	}
+
+	decoded, err := hex.DecodeString(s)
+	if err != nil {
+		return id, fmt.Errorf("%w: decode: %w", ErrInvalidObjectID, err)
+	}
+
+	copy(id.data[:], decoded)
+	id.algo = algo
+
+	return id, nil
+}
--- /dev/null
+++ b/object/id/objectid_string.go
@@ -1,0 +1,10 @@
+package objectid
+
+import "encoding/hex"
+
+// String returns the canonical hex representation.
+func (id ObjectID) String() string {
+	size := id.Algorithm().Size()
+
+	return hex.EncodeToString(id.data[:size])
+}
--- a/object/id/objectid_test.go
+++ b/object/id/objectid_test.go
@@ -44,7 +44,7 @@
 				t.Fatalf("String() = %q, want %q", got, hex)
 			}
 
-			if got := id.Size(); got != algo.Size() {
+			if got := id.Algorithm().Size(); got != algo.Size() {
 				t.Fatalf("Size() = %d, want %d", got, algo.Size())
 			}
 
@@ -148,8 +148,8 @@
 		}
 
 		b := id.RawBytes()
-		if len(b) != id.Size() {
-			t.Fatalf("RawBytes len = %d, want %d", len(b), id.Size())
+		if len(b) != id.Algorithm().Size() {
+			t.Fatalf("RawBytes len = %d, want %d", len(b), id.Algorithm().Size())
 		}
 
 		if cap(b) != len(b) {
@@ -169,12 +169,12 @@
 	t.Parallel()
 
 	id1 := objectid.AlgorithmSHA1.Sum([]byte("hello"))
-	if id1.Algorithm() != objectid.AlgorithmSHA1 || id1.Size() != objectid.AlgorithmSHA1.Size() {
+	if id1.Algorithm() != objectid.AlgorithmSHA1 || id1.Algorithm().Size() != objectid.AlgorithmSHA1.Size() {
 		t.Fatalf("sha1 sum produced invalid object id")
 	}
 
 	id2 := objectid.AlgorithmSHA256.Sum([]byte("hello"))
-	if id2.Algorithm() != objectid.AlgorithmSHA256 || id2.Size() != objectid.AlgorithmSHA256.Size() {
+	if id2.Algorithm() != objectid.AlgorithmSHA256 || id2.Algorithm().Size() != objectid.AlgorithmSHA256.Size() {
 		t.Fatalf("sha256 sum produced invalid object id")
 	}
 
--- /dev/null
+++ b/object/id/objectid_zero.go
@@ -1,0 +1,11 @@
+package objectid
+
+// Zero returns the all-zero object ID for the specified algorithm.
+func Zero(algo Algorithm) ObjectID {
+	id, err := FromBytes(algo, make([]byte, algo.Size()))
+	if err != nil {
+		panic(err)
+	}
+
+	return id
+}
--- a/object/tag/serialize.go
+++ b/object/tag/serialize.go
@@ -11,7 +11,7 @@
 
 // SerializeWithoutHeader renders the raw tag body bytes.
 func (tag *Tag) SerializeWithoutHeader() ([]byte, error) {
-	if tag.Target.Size() == 0 {
+	if tag.Target.Algorithm().Size() == 0 {
 		return nil, errors.New("object: tag: missing target id")
 	}
 
--- a/object/tree/serialize.go
+++ b/object/tree/serialize.go
@@ -14,7 +14,7 @@
 
 	for _, entry := range tree.Entries {
 		mode := strconv.FormatUint(uint64(entry.Mode), 8)
-		bodyLen += len(mode) + 1 + len(entry.Name) + 1 + entry.ID.Size()
+		bodyLen += len(mode) + 1 + len(entry.Name) + 1 + entry.ID.Algorithm().Size()
 	}
 
 	body := make([]byte, bodyLen)
--- a/ref/store/files/resolve_list_test.go
+++ b/ref/store/files/resolve_list_test.go
@@ -236,7 +236,7 @@
 				t.Fatalf("Resolve(tag) type = %T, want ref.Detached", tagRef)
 			}
 
-			if tagDet.ID.Size() == 0 {
+			if tagDet.ID.Algorithm().Size() == 0 {
 				t.Fatal("Resolve(tag) returned zero object id")
 			}
 		})
--- a/ref/store/files/update_validate.go
+++ b/ref/store/files/update_validate.go
@@ -21,7 +21,7 @@
 			return wrapUpdateError(op.name, &refstore.InvalidNameError{Err: err})
 		}
 
-		if op.newID.Size() == 0 {
+		if op.newID.Algorithm().Size() == 0 {
 			return wrapUpdateError(op.name, &refstore.InvalidValueError{Err: objectid.ErrInvalidAlgorithm})
 		}
 	case updateDelete, updateVerify:
@@ -30,7 +30,7 @@
 			return wrapUpdateError(op.name, &refstore.InvalidNameError{Err: err})
 		}
 
-		if op.oldID.Size() == 0 {
+		if op.oldID.Algorithm().Size() == 0 {
 			return wrapUpdateError(op.name, &refstore.InvalidValueError{Err: objectid.ErrInvalidAlgorithm})
 		}
 	case updateCreateSymbolic, updateReplaceSymbolic:
--