···99)
10101111func layerLookup(layer *layer, oid objectid.ObjectID) (uint32, bool) {
1212- hashSize := oid.Size()
1212+ hashSize := oid.Algorithm().Size()
1313 first := int(oid.RawBytes()[0])
14141515 var lo uint32
+1-1
object/commit/serialize.go
···1313func (commit *Commit) SerializeWithoutHeader() ([]byte, error) {
1414 var buf bytes.Buffer
15151616- if commit.Tree.Size() == 0 {
1616+ if commit.Tree.Algorithm().Size() == 0 {
1717 return nil, errors.New("object: commit: missing tree id")
1818 }
1919
+12
object/id/algorithm.go
···11+package objectid
22+33+//#nosec gosec
44+55+// Algorithm identifies the hash algorithm used for Git object IDs.
66+type Algorithm uint8
77+88+const (
99+ AlgorithmUnknown Algorithm = iota
1010+ AlgorithmSHA1
1111+ AlgorithmSHA256
1212+)
+16
object/id/algorithm_details.go
···11+package objectid
22+33+import "hash"
44+55+type algorithmDetails struct {
66+ name string
77+ size int
88+ packHashID uint32
99+ sum func([]byte) ObjectID
1010+ new func() hash.Hash
1111+ emptyTree ObjectID
1212+}
1313+1414+func (algo Algorithm) info() algorithmDetails {
1515+ return algorithmTable[algo]
1616+}
+7
object/id/algorithm_emptytree.go
···11+package objectid
22+33+// EmptyTree returns the object ID of an empty tree ("tree 0\x00") for this
44+// algorithm.
55+func (algo Algorithm) EmptyTree() ObjectID {
66+ return algo.info().emptyTree
77+}
···11+package objectid
22+33+// Sum computes an object ID from raw data using the selected algorithm.
44+func (algo Algorithm) Sum(data []byte) ObjectID {
55+ return algo.info().sum(data)
66+}
+7
object/id/algorithm_supported.go
···11+package objectid
22+33+// SupportedAlgorithms returns all object ID algorithms supported by furgit.
44+// Do not mutate.
55+func SupportedAlgorithms() []Algorithm {
66+ return supportedAlgorithms
77+}
···11-package objectid
22-33-import (
44- "crypto/sha1" //#nosec gosec
55- "crypto/sha256"
66- "hash"
77-)
88-99-// maxObjectIDSize MUST be >= the largest supported algorithm size.
1010-const maxObjectIDSize = sha256.Size
1111-1212-// Algorithm identifies the hash algorithm used for Git object IDs.
1313-type Algorithm uint8
1414-1515-const (
1616- AlgorithmUnknown Algorithm = iota
1717- AlgorithmSHA1
1818- AlgorithmSHA256
1919-)
2020-2121-type algorithmDetails struct {
2222- name string
2323- size int
2424- packHashID uint32
2525- sum func([]byte) ObjectID
2626- new func() hash.Hash
2727- emptyTree ObjectID
2828-}
2929-3030-//nolint:gochecknoglobals
3131-var algorithmTable = [...]algorithmDetails{
3232- AlgorithmUnknown: {},
3333- AlgorithmSHA1: {
3434- name: "sha1",
3535- size: sha1.Size,
3636- packHashID: 1,
3737- sum: func(data []byte) ObjectID {
3838- sum := sha1.Sum(data) //#nosec G401
3939-4040- var id ObjectID
4141- copy(id.data[:], sum[:])
4242- id.algo = AlgorithmSHA1
4343-4444- return id
4545- },
4646- new: sha1.New,
4747- },
4848- AlgorithmSHA256: {
4949- name: "sha256",
5050- size: sha256.Size,
5151- packHashID: 2,
5252- sum: func(data []byte) ObjectID {
5353- sum := sha256.Sum256(data)
5454-5555- var id ObjectID
5656- copy(id.data[:], sum[:])
5757- id.algo = AlgorithmSHA256
5858-5959- return id
6060- },
6161- new: sha256.New,
6262- },
6363-}
6464-6565-var (
6666- //nolint:gochecknoglobals
6767- algorithmByName = map[string]Algorithm{}
6868- //nolint:gochecknoglobals
6969- supportedAlgorithms []Algorithm
7070-)
7171-7272-func init() { //nolint:gochecknoinits
7373- emptyTreeInput := []byte("tree 0\x00")
7474-7575- for algo := Algorithm(0); int(algo) < len(algorithmTable); algo++ {
7676- info := &algorithmTable[algo]
7777- if info.name == "" {
7878- continue
7979- }
8080-8181- info.emptyTree = info.sum(emptyTreeInput)
8282- algorithmByName[info.name] = algo
8383- supportedAlgorithms = append(supportedAlgorithms, algo)
8484- }
8585-}
8686-8787-// SupportedAlgorithms returns all object ID algorithms supported by furgit.
8888-// Do not mutate.
8989-func SupportedAlgorithms() []Algorithm {
9090- return supportedAlgorithms
9191-}
9292-9393-// ParseAlgorithm parses a canonical algorithm name (e.g. "sha1", "sha256").
9494-func ParseAlgorithm(s string) (Algorithm, bool) {
9595- algo, ok := algorithmByName[s]
9696-9797- return algo, ok
9898-}
9999-100100-// Size returns the hash size in bytes.
101101-func (algo Algorithm) Size() int {
102102- return algo.info().size
103103-}
104104-105105-// String returns the canonical algorithm name.
106106-func (algo Algorithm) String() string {
107107- inf := algo.info()
108108- if inf.name == "" {
109109- return "unknown"
110110- }
111111-112112- return inf.name
113113-}
114114-115115-// HexLen returns the encoded hexadecimal length.
116116-func (algo Algorithm) HexLen() int {
117117- return algo.Size() * 2
118118-}
119119-120120-// PackHashID returns the Git pack/rev hash-id encoding for this algorithm.
121121-//
122122-// Unknown algorithms return 0.
123123-func (algo Algorithm) PackHashID() uint32 {
124124- return algo.info().packHashID
125125-}
126126-127127-// Sum computes an object ID from raw data using the selected algorithm.
128128-func (algo Algorithm) Sum(data []byte) ObjectID {
129129- return algo.info().sum(data)
130130-}
131131-132132-// New returns a new hash.Hash for this algorithm.
133133-func (algo Algorithm) New() (hash.Hash, error) {
134134- newFn := algo.info().new
135135- if newFn == nil {
136136- return nil, ErrInvalidAlgorithm
137137- }
138138-139139- return newFn(), nil
140140-}
141141-142142-// EmptyTree returns the object ID of an empty tree ("tree 0\x00") for this
143143-// algorithm.
144144-func (algo Algorithm) EmptyTree() ObjectID {
145145- return algo.info().emptyTree
146146-}
147147-148148-func (algo Algorithm) info() algorithmDetails {
149149- return algorithmTable[algo]
150150-}
+6
object/id/max_size.go
···11+package objectid
22+33+import "crypto/sha256"
44+55+// maxObjectIDSize MUST be >= the largest supported algorithm size.
66+const maxObjectIDSize = sha256.Size
+1-102
object/id/objectid.go
···11package objectid
2233-import (
44- //#nosec G505
55-66- "bytes"
77- "encoding/hex"
88- "fmt"
99-)
33+//#nosec G505
104115// ObjectID represents a Git object ID.
126//
···159 algo Algorithm
1610 data [maxObjectIDSize]byte
1711}
1818-1919-// Algorithm returns the object ID's hash algorithm.
2020-func (id ObjectID) Algorithm() Algorithm {
2121- return id.algo
2222-}
2323-2424-// Size returns the object ID size in bytes.
2525-func (id ObjectID) Size() int {
2626- return id.algo.Size()
2727-}
2828-2929-// String returns the canonical hex representation.
3030-func (id ObjectID) String() string {
3131- size := id.Size()
3232-3333- return hex.EncodeToString(id.data[:size])
3434-}
3535-3636-// Bytes returns a copy of the object ID bytes.
3737-func (id ObjectID) Bytes() []byte {
3838- size := id.Size()
3939-4040- return append([]byte(nil), id.data[:size]...)
4141-}
4242-4343-// RawBytes returns a direct byte slice view of the object ID bytes.
4444-//
4545-// The returned slice aliases the object ID's internal storage. Callers MUST
4646-// treat it as read-only and MUST NOT modify its contents.
4747-//
4848-// Use Bytes when an independent copy is required.
4949-func (id *ObjectID) RawBytes() []byte {
5050- size := id.Size()
5151-5252- return id.data[:size:size]
5353-}
5454-5555-// Compare lexicographically compares two object IDs by their canonical byte
5656-// representation.
5757-func Compare(left, right ObjectID) int {
5858- return bytes.Compare(left.RawBytes(), right.RawBytes())
5959-}
6060-6161-// Zero returns the all-zero object ID for the specified algorithm.
6262-func Zero(algo Algorithm) ObjectID {
6363- id, err := FromBytes(algo, make([]byte, algo.Size()))
6464- if err != nil {
6565- panic(err)
6666- }
6767-6868- return id
6969-}
7070-7171-// ParseHex parses an object ID from hex for the specified algorithm.
7272-func ParseHex(algo Algorithm, s string) (ObjectID, error) {
7373- var id ObjectID
7474- if algo.Size() == 0 {
7575- return id, ErrInvalidAlgorithm
7676- }
7777-7878- if len(s)%2 != 0 {
7979- return id, fmt.Errorf("%w: odd hex length %d", ErrInvalidObjectID, len(s))
8080- }
8181-8282- if len(s) != algo.HexLen() {
8383- return id, fmt.Errorf("%w: got %d chars, expected %d", ErrInvalidObjectID, len(s), algo.HexLen())
8484- }
8585-8686- decoded, err := hex.DecodeString(s)
8787- if err != nil {
8888- return id, fmt.Errorf("%w: decode: %w", ErrInvalidObjectID, err)
8989- }
9090-9191- copy(id.data[:], decoded)
9292- id.algo = algo
9393-9494- return id, nil
9595-}
9696-9797-// FromBytes builds an object ID from raw bytes for the specified algorithm.
9898-func FromBytes(algo Algorithm, b []byte) (ObjectID, error) {
9999- var id ObjectID
100100- if algo.Size() == 0 {
101101- return id, ErrInvalidAlgorithm
102102- }
103103-104104- if len(b) != algo.Size() {
105105- return id, fmt.Errorf("%w: got %d bytes, expected %d", ErrInvalidObjectID, len(b), algo.Size())
106106- }
107107-108108- copy(id.data[:], b)
109109- id.algo = algo
110110-111111- return id, nil
112112-}
+6
object/id/objectid_algorithm.go
···11+package objectid
22+33+// Algorithm returns the object ID's hash algorithm.
44+func (id ObjectID) Algorithm() Algorithm {
55+ return id.algo
66+}
+20
object/id/objectid_byte.go
···11+package objectid
22+33+// Bytes returns a copy of the object ID bytes.
44+func (id ObjectID) Bytes() []byte {
55+ size := id.Algorithm().Size()
66+77+ return append([]byte(nil), id.data[:size]...)
88+}
99+1010+// RawBytes returns a direct byte slice view of the object ID bytes.
1111+//
1212+// The returned slice aliases the object ID's internal storage. Callers MUST
1313+// treat it as read-only and MUST NOT modify its contents.
1414+//
1515+// Use Bytes when an independent copy is required.
1616+func (id *ObjectID) RawBytes() []byte {
1717+ size := id.Algorithm().Size()
1818+1919+ return id.data[:size:size]
2020+}
+9
object/id/objectid_compare.go
···11+package objectid
22+33+import "bytes"
44+55+// Compare lexicographically compares two object IDs by their canonical byte
66+// representation.
77+func Compare(left, right ObjectID) int {
88+ return bytes.Compare(left.RawBytes(), right.RawBytes())
99+}
+20
object/id/objectid_frombytes.go
···11+package objectid
22+33+import "fmt"
44+55+// FromBytes builds an object ID from raw bytes for the specified algorithm.
66+func FromBytes(algo Algorithm, b []byte) (ObjectID, error) {
77+ var id ObjectID
88+ if algo.Size() == 0 {
99+ return id, ErrInvalidAlgorithm
1010+ }
1111+1212+ if len(b) != algo.Size() {
1313+ return id, fmt.Errorf("%w: got %d bytes, expected %d", ErrInvalidObjectID, len(b), algo.Size())
1414+ }
1515+1616+ copy(id.data[:], b)
1717+ id.algo = algo
1818+1919+ return id, nil
2020+}
+32
object/id/objectid_parse.go
···11+package objectid
22+33+import (
44+ "encoding/hex"
55+ "fmt"
66+)
77+88+// ParseHex parses an object ID from hex for the specified algorithm.
99+func ParseHex(algo Algorithm, s string) (ObjectID, error) {
1010+ var id ObjectID
1111+ if algo.Size() == 0 {
1212+ return id, ErrInvalidAlgorithm
1313+ }
1414+1515+ if len(s)%2 != 0 {
1616+ return id, fmt.Errorf("%w: odd hex length %d", ErrInvalidObjectID, len(s))
1717+ }
1818+1919+ if len(s) != algo.HexLen() {
2020+ return id, fmt.Errorf("%w: got %d chars, expected %d", ErrInvalidObjectID, len(s), algo.HexLen())
2121+ }
2222+2323+ decoded, err := hex.DecodeString(s)
2424+ if err != nil {
2525+ return id, fmt.Errorf("%w: decode: %w", ErrInvalidObjectID, err)
2626+ }
2727+2828+ copy(id.data[:], decoded)
2929+ id.algo = algo
3030+3131+ return id, nil
3232+}