Fast implementation of Git in pure Go
at master 113 lines 2.6 kB view raw
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}