Fast implementation of Git in pure Go
1package objectid
2
3import (
4 "crypto/sha1" //#nosec gosec
5 "crypto/sha256"
6 "hash"
7)
8
9// maxObjectIDSize MUST be >= the largest supported algorithm size.
10const maxObjectIDSize = sha256.Size
11
12// Algorithm identifies the hash algorithm used for Git object IDs.
13type Algorithm uint8
14
15const (
16 AlgorithmUnknown Algorithm = iota
17 AlgorithmSHA1
18 AlgorithmSHA256
19)
20
21type algorithmDetails struct {
22 name string
23 size int
24 packHashID uint32
25 sum func([]byte) ObjectID
26 new func() hash.Hash
27}
28
29//nolint:gochecknoglobals
30var algorithmTable = [...]algorithmDetails{
31 AlgorithmUnknown: {},
32 AlgorithmSHA1: {
33 name: "sha1",
34 size: sha1.Size,
35 packHashID: 1,
36 sum: func(data []byte) ObjectID {
37 sum := sha1.Sum(data) //#nosec G401
38
39 var id ObjectID
40 copy(id.data[:], sum[:])
41 id.algo = AlgorithmSHA1
42
43 return id
44 },
45 new: sha1.New,
46 },
47 AlgorithmSHA256: {
48 name: "sha256",
49 size: sha256.Size,
50 packHashID: 2,
51 sum: func(data []byte) ObjectID {
52 sum := sha256.Sum256(data)
53
54 var id ObjectID
55 copy(id.data[:], sum[:])
56 id.algo = AlgorithmSHA256
57
58 return id
59 },
60 new: sha256.New,
61 },
62}
63
64var (
65 //nolint:gochecknoglobals
66 algorithmByName = map[string]Algorithm{}
67 //nolint:gochecknoglobals
68 supportedAlgorithms []Algorithm
69)
70
71func init() { //nolint:gochecknoinits
72 for algo := Algorithm(0); int(algo) < len(algorithmTable); algo++ {
73 info := algorithmTable[algo]
74 if info.name == "" {
75 continue
76 }
77
78 algorithmByName[info.name] = algo
79 supportedAlgorithms = append(supportedAlgorithms, algo)
80 }
81}
82
83// SupportedAlgorithms returns all object ID algorithms supported by furgit.
84// Do not mutate.
85func SupportedAlgorithms() []Algorithm {
86 return supportedAlgorithms
87}
88
89// ParseAlgorithm parses a canonical algorithm name (e.g. "sha1", "sha256").
90func ParseAlgorithm(s string) (Algorithm, bool) {
91 algo, ok := algorithmByName[s]
92
93 return algo, ok
94}
95
96// Size returns the hash size in bytes.
97func (algo Algorithm) Size() int {
98 return algo.info().size
99}
100
101// String returns the canonical algorithm name.
102func (algo Algorithm) String() string {
103 inf := algo.info()
104 if inf.name == "" {
105 return "unknown"
106 }
107
108 return inf.name
109}
110
111// HexLen returns the encoded hexadecimal length.
112func (algo Algorithm) HexLen() int {
113 return algo.Size() * 2
114}
115
116// PackHashID returns the Git pack/rev hash-id encoding for this algorithm.
117//
118// Unknown algorithms return 0.
119func (algo Algorithm) PackHashID() uint32 {
120 return algo.info().packHashID
121}
122
123// Sum computes an object ID from raw data using the selected algorithm.
124func (algo Algorithm) Sum(data []byte) ObjectID {
125 return algo.info().sum(data)
126}
127
128// New returns a new hash.Hash for this algorithm.
129func (algo Algorithm) New() (hash.Hash, error) {
130 newFn := algo.info().new
131 if newFn == nil {
132 return nil, ErrInvalidAlgorithm
133 }
134
135 return newFn(), nil
136}
137
138func (algo Algorithm) info() algorithmDetails {
139 return algorithmTable[algo]
140}