aboutsummaryrefslogtreecommitdiff
path: root/object/id
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-29 10:14:38 +0000
committerGravatar Runxi Yu2026-03-29 10:17:15 +0000
commit32f84b9d89d34ab74f8843f939f8d1d811d403ad (patch)
tree90786209ddd050ad2676a7d0404b78d8d278e1f2 /object/id
parentobject/{id,header,signature,type}: Update docs (diff)
signatureNo signature
object/id: Split files
Diffstat (limited to 'object/id')
-rw-r--r--object/id/algorithm.go12
-rw-r--r--object/id/algorithm_details.go16
-rw-r--r--object/id/algorithm_emptytree.go7
-rw-r--r--object/id/algorithm_hexlen.go6
-rw-r--r--object/id/algorithm_new.go13
-rw-r--r--object/id/algorithm_packhashid.go8
-rw-r--r--object/id/algorithm_parse.go8
-rw-r--r--object/id/algorithm_size.go6
-rw-r--r--object/id/algorithm_string.go11
-rw-r--r--object/id/algorithm_sum.go6
-rw-r--r--object/id/algorithm_supported.go7
-rw-r--r--object/id/algorithm_tables.go63
-rw-r--r--object/id/algorithms.go150
-rw-r--r--object/id/max_size.go6
-rw-r--r--object/id/objectid.go103
-rw-r--r--object/id/objectid_algorithm.go6
-rw-r--r--object/id/objectid_byte.go20
-rw-r--r--object/id/objectid_compare.go9
-rw-r--r--object/id/objectid_frombytes.go20
-rw-r--r--object/id/objectid_parse.go32
-rw-r--r--object/id/objectid_string.go10
-rw-r--r--object/id/objectid_test.go10
-rw-r--r--object/id/objectid_zero.go11
23 files changed, 283 insertions, 257 deletions
diff --git a/object/id/algorithm.go b/object/id/algorithm.go
new file mode 100644
index 00000000..a695889c
--- /dev/null
+++ b/object/id/algorithm.go
@@ -0,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
+)
diff --git a/object/id/algorithm_details.go b/object/id/algorithm_details.go
new file mode 100644
index 00000000..fbdc3c78
--- /dev/null
+++ b/object/id/algorithm_details.go
@@ -0,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]
+}
diff --git a/object/id/algorithm_emptytree.go b/object/id/algorithm_emptytree.go
new file mode 100644
index 00000000..32f57385
--- /dev/null
+++ b/object/id/algorithm_emptytree.go
@@ -0,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
+}
diff --git a/object/id/algorithm_hexlen.go b/object/id/algorithm_hexlen.go
new file mode 100644
index 00000000..2b7fa0fa
--- /dev/null
+++ b/object/id/algorithm_hexlen.go
@@ -0,0 +1,6 @@
+package objectid
+
+// HexLen returns the encoded hexadecimal length.
+func (algo Algorithm) HexLen() int {
+ return algo.Size() * 2
+}
diff --git a/object/id/algorithm_new.go b/object/id/algorithm_new.go
new file mode 100644
index 00000000..8abbaeda
--- /dev/null
+++ b/object/id/algorithm_new.go
@@ -0,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
+}
diff --git a/object/id/algorithm_packhashid.go b/object/id/algorithm_packhashid.go
new file mode 100644
index 00000000..93c0f61b
--- /dev/null
+++ b/object/id/algorithm_packhashid.go
@@ -0,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
+}
diff --git a/object/id/algorithm_parse.go b/object/id/algorithm_parse.go
new file mode 100644
index 00000000..d5fb0c64
--- /dev/null
+++ b/object/id/algorithm_parse.go
@@ -0,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
+}
diff --git a/object/id/algorithm_size.go b/object/id/algorithm_size.go
new file mode 100644
index 00000000..104bfeb2
--- /dev/null
+++ b/object/id/algorithm_size.go
@@ -0,0 +1,6 @@
+package objectid
+
+// Size returns the hash size in bytes.
+func (algo Algorithm) Size() int {
+ return algo.info().size
+}
diff --git a/object/id/algorithm_string.go b/object/id/algorithm_string.go
new file mode 100644
index 00000000..410ee8a3
--- /dev/null
+++ b/object/id/algorithm_string.go
@@ -0,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
+}
diff --git a/object/id/algorithm_sum.go b/object/id/algorithm_sum.go
new file mode 100644
index 00000000..26ad2ff6
--- /dev/null
+++ b/object/id/algorithm_sum.go
@@ -0,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)
+}
diff --git a/object/id/algorithm_supported.go b/object/id/algorithm_supported.go
new file mode 100644
index 00000000..1f61e771
--- /dev/null
+++ b/object/id/algorithm_supported.go
@@ -0,0 +1,7 @@
+package objectid
+
+// SupportedAlgorithms returns all object ID algorithms supported by furgit.
+// Do not mutate.
+func SupportedAlgorithms() []Algorithm {
+ return supportedAlgorithms
+}
diff --git a/object/id/algorithm_tables.go b/object/id/algorithm_tables.go
new file mode 100644
index 00000000..86e1341e
--- /dev/null
+++ b/object/id/algorithm_tables.go
@@ -0,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)
+ }
+}
diff --git a/object/id/algorithms.go b/object/id/algorithms.go
deleted file mode 100644
index f3540c42..00000000
--- a/object/id/algorithms.go
+++ /dev/null
@@ -1,150 +0,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]
-}
diff --git a/object/id/max_size.go b/object/id/max_size.go
new file mode 100644
index 00000000..d2a64a10
--- /dev/null
+++ b/object/id/max_size.go
@@ -0,0 +1,6 @@
+package objectid
+
+import "crypto/sha256"
+
+// maxObjectIDSize MUST be >= the largest supported algorithm size.
+const maxObjectIDSize = sha256.Size
diff --git a/object/id/objectid.go b/object/id/objectid.go
index 301eb8e6..33a54225 100644
--- a/object/id/objectid.go
+++ b/object/id/objectid.go
@@ -1,12 +1,6 @@
package objectid
-import (
- //#nosec G505
-
- "bytes"
- "encoding/hex"
- "fmt"
-)
+//#nosec G505
// ObjectID represents a Git object ID.
//
@@ -15,98 +9,3 @@ 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
-}
diff --git a/object/id/objectid_algorithm.go b/object/id/objectid_algorithm.go
new file mode 100644
index 00000000..cb694b7c
--- /dev/null
+++ b/object/id/objectid_algorithm.go
@@ -0,0 +1,6 @@
+package objectid
+
+// Algorithm returns the object ID's hash algorithm.
+func (id ObjectID) Algorithm() Algorithm {
+ return id.algo
+}
diff --git a/object/id/objectid_byte.go b/object/id/objectid_byte.go
new file mode 100644
index 00000000..8384ff45
--- /dev/null
+++ b/object/id/objectid_byte.go
@@ -0,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]
+}
diff --git a/object/id/objectid_compare.go b/object/id/objectid_compare.go
new file mode 100644
index 00000000..a40bcc89
--- /dev/null
+++ b/object/id/objectid_compare.go
@@ -0,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())
+}
diff --git a/object/id/objectid_frombytes.go b/object/id/objectid_frombytes.go
new file mode 100644
index 00000000..ea8dacfe
--- /dev/null
+++ b/object/id/objectid_frombytes.go
@@ -0,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
+}
diff --git a/object/id/objectid_parse.go b/object/id/objectid_parse.go
new file mode 100644
index 00000000..e6cbb641
--- /dev/null
+++ b/object/id/objectid_parse.go
@@ -0,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
+}
diff --git a/object/id/objectid_string.go b/object/id/objectid_string.go
new file mode 100644
index 00000000..36a7177d
--- /dev/null
+++ b/object/id/objectid_string.go
@@ -0,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])
+}
diff --git a/object/id/objectid_test.go b/object/id/objectid_test.go
index dc25832b..9d179fb5 100644
--- a/object/id/objectid_test.go
+++ b/object/id/objectid_test.go
@@ -44,7 +44,7 @@ func TestParseHexRoundtrip(t *testing.T) {
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 @@ func TestRawBytesAliasesStorage(t *testing.T) {
}
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 @@ func TestAlgorithmSum(t *testing.T) {
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")
}
diff --git a/object/id/objectid_zero.go b/object/id/objectid_zero.go
new file mode 100644
index 00000000..9cc3d205
--- /dev/null
+++ b/object/id/objectid_zero.go
@@ -0,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
+}