Fast implementation of Git in pure Go
1// Package objectid provides utilities around object IDs and hash algorithms.
2package objectid
3
4import (
5 //#nosec G505
6
7 "bytes"
8 "encoding/hex"
9 "fmt"
10)
11
12// ObjectID represents a Git object ID.
13//
14//nolint:recvcheck
15type ObjectID struct {
16 algo Algorithm
17 data [maxObjectIDSize]byte
18}
19
20// Algorithm returns the object ID's hash algorithm.
21func (id ObjectID) Algorithm() Algorithm {
22 return id.algo
23}
24
25// Size returns the object ID size in bytes.
26func (id ObjectID) Size() int {
27 return id.algo.Size()
28}
29
30// String returns the canonical hex representation.
31func (id ObjectID) String() string {
32 size := id.Size()
33
34 return hex.EncodeToString(id.data[:size])
35}
36
37// Bytes returns a copy of the object ID bytes.
38func (id ObjectID) Bytes() []byte {
39 size := id.Size()
40
41 return append([]byte(nil), id.data[:size]...)
42}
43
44// RawBytes returns a direct byte slice view of the object ID bytes.
45//
46// The returned slice aliases the object ID's internal storage. Callers MUST
47// treat it as read-only and MUST NOT modify its contents.
48//
49// Use Bytes when an independent copy is required.
50func (id *ObjectID) RawBytes() []byte {
51 size := id.Size()
52
53 return id.data[:size:size]
54}
55
56// Compare lexicographically compares two object IDs by their canonical byte
57// representation.
58func Compare(left, right ObjectID) int {
59 return bytes.Compare(left.RawBytes(), right.RawBytes())
60}
61
62// Zero returns the all-zero object ID for the specified algorithm.
63func Zero(algo Algorithm) ObjectID {
64 id, err := FromBytes(algo, make([]byte, algo.Size()))
65 if err != nil {
66 panic(err)
67 }
68
69 return id
70}
71
72// ParseHex parses an object ID from hex for the specified algorithm.
73func ParseHex(algo Algorithm, s string) (ObjectID, error) {
74 var id ObjectID
75 if algo.Size() == 0 {
76 return id, ErrInvalidAlgorithm
77 }
78
79 if len(s)%2 != 0 {
80 return id, fmt.Errorf("%w: odd hex length %d", ErrInvalidObjectID, len(s))
81 }
82
83 if len(s) != algo.HexLen() {
84 return id, fmt.Errorf("%w: got %d chars, expected %d", ErrInvalidObjectID, len(s), algo.HexLen())
85 }
86
87 decoded, err := hex.DecodeString(s)
88 if err != nil {
89 return id, fmt.Errorf("%w: decode: %w", ErrInvalidObjectID, err)
90 }
91
92 copy(id.data[:], decoded)
93 id.algo = algo
94
95 return id, nil
96}
97
98// FromBytes builds an object ID from raw bytes for the specified algorithm.
99func FromBytes(algo Algorithm, b []byte) (ObjectID, error) {
100 var id ObjectID
101 if algo.Size() == 0 {
102 return id, ErrInvalidAlgorithm
103 }
104
105 if len(b) != algo.Size() {
106 return id, fmt.Errorf("%w: got %d bytes, expected %d", ErrInvalidObjectID, len(b), algo.Size())
107 }
108
109 copy(id.data[:], b)
110 id.algo = algo
111
112 return id, nil
113}