10
// You should have received a copy of the GNU General Public License along
11
// with this program; if not, see <https://www.gnu.org/licenses/>.
12
13
+use std::error::Error;
14
+use std::fmt::{self, Debug, Display};
15
+
16
pub const GIT_MAX_RAWSZ: usize = 32;
17
18
+/// An error indicating an invalid hash algorithm.
19
+///
20
+/// The contained `u32` is the same as the `algo` field in `ObjectID`.
21
+#[derive(Debug, Copy, Clone)]
22
+pub struct InvalidHashAlgorithm(pub u32);
23
+
24
+impl Display for InvalidHashAlgorithm {
25
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26
+ write!(f, "invalid hash algorithm {}", self.0)
27
+ }
28
+}
29
+
30
+impl Error for InvalidHashAlgorithm {}
31
+
32
/// A binary object ID.
33
#[repr(C)]
34
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
36
pub hash: [u8; GIT_MAX_RAWSZ],
37
pub algo: u32,
38
}
39
+
40
+#[allow(dead_code)]
41
+impl ObjectID {
42
+ pub fn as_slice(&self) -> Result<&[u8], InvalidHashAlgorithm> {
43
+ match HashAlgorithm::from_u32(self.algo) {
44
+ Some(algo) => Ok(&self.hash[0..algo.raw_len()]),
45
+ None => Err(InvalidHashAlgorithm(self.algo)),
46
+ }
47
+ }
48
+
49
+ pub fn as_mut_slice(&mut self) -> Result<&mut [u8], InvalidHashAlgorithm> {
50
+ match HashAlgorithm::from_u32(self.algo) {
51
+ Some(algo) => Ok(&mut self.hash[0..algo.raw_len()]),
52
+ None => Err(InvalidHashAlgorithm(self.algo)),
53
+ }
54
+ }
55
+}
56
+
57
+/// A hash algorithm,
58
+#[repr(C)]
59
+#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
60
+pub enum HashAlgorithm {
61
+ SHA1 = 1,
62
+ SHA256 = 2,
63
+}
64
+
65
+#[allow(dead_code)]
66
+impl HashAlgorithm {
67
+ const SHA1_NULL_OID: ObjectID = ObjectID {
68
+ hash: [0u8; 32],
69
+ algo: Self::SHA1 as u32,
70
+ };
71
+ const SHA256_NULL_OID: ObjectID = ObjectID {
72
+ hash: [0u8; 32],
73
+ algo: Self::SHA256 as u32,
74
+ };
75
+
76
+ const SHA1_EMPTY_TREE: ObjectID = ObjectID {
77
+ hash: *b"\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
78
+ algo: Self::SHA1 as u32,
79
+ };
80
+ const SHA256_EMPTY_TREE: ObjectID = ObjectID {
81
+ hash: *b"\x6e\xf1\x9b\x41\x22\x5c\x53\x69\xf1\xc1\x04\xd4\x5d\x8d\x85\xef\xa9\xb0\x57\xb5\x3b\x14\xb4\xb9\xb9\x39\xdd\x74\xde\xcc\x53\x21",
82
+ algo: Self::SHA256 as u32,
83
+ };
84
+
85
+ const SHA1_EMPTY_BLOB: ObjectID = ObjectID {
86
+ hash: *b"\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
87
+ algo: Self::SHA1 as u32,
88
+ };
89
+ const SHA256_EMPTY_BLOB: ObjectID = ObjectID {
90
+ hash: *b"\x47\x3a\x0f\x4c\x3b\xe8\xa9\x36\x81\xa2\x67\xe3\xb1\xe9\xa7\xdc\xda\x11\x85\x43\x6f\xe1\x41\xf7\x74\x91\x20\xa3\x03\x72\x18\x13",
91
+ algo: Self::SHA256 as u32,
92
+ };
93
+
94
+ /// Return a hash algorithm based on the internal integer ID used by Git.
95
+ ///
96
+ /// Returns `None` if the algorithm doesn't indicate a valid algorithm.
97
+ pub const fn from_u32(algo: u32) -> Option<HashAlgorithm> {
98
+ match algo {
99
+ 1 => Some(HashAlgorithm::SHA1),
100
+ 2 => Some(HashAlgorithm::SHA256),
101
+ _ => None,
102
+ }
103
+ }
104
+
105
+ /// Return a hash algorithm based on the internal integer ID used by Git.
106
+ ///
107
+ /// Returns `None` if the algorithm doesn't indicate a valid algorithm.
108
+ pub const fn from_format_id(algo: u32) -> Option<HashAlgorithm> {
109
+ match algo {
110
+ 0x73686131 => Some(HashAlgorithm::SHA1),
111
+ 0x73323536 => Some(HashAlgorithm::SHA256),
112
+ _ => None,
113
+ }
114
+ }
115
+
116
+ /// The name of this hash algorithm as a string suitable for the configuration file.
117
+ pub const fn name(self) -> &'static str {
118
+ match self {
119
+ HashAlgorithm::SHA1 => "sha1",
120
+ HashAlgorithm::SHA256 => "sha256",
121
+ }
122
+ }
123
+
124
+ /// The format ID of this algorithm for binary formats.
125
+ ///
126
+ /// Note that when writing this to a data format, it should be written in big-endian format
127
+ /// explicitly.
128
+ pub const fn format_id(self) -> u32 {
129
+ match self {
130
+ HashAlgorithm::SHA1 => 0x73686131,
131
+ HashAlgorithm::SHA256 => 0x73323536,
132
+ }
133
+ }
134
+
135
+ /// The length of binary object IDs in this algorithm in bytes.
136
+ pub const fn raw_len(self) -> usize {
137
+ match self {
138
+ HashAlgorithm::SHA1 => 20,
139
+ HashAlgorithm::SHA256 => 32,
140
+ }
141
+ }
142
+
143
+ /// The length of object IDs in this algorithm in hexadecimal characters.
144
+ pub const fn hex_len(self) -> usize {
145
+ self.raw_len() * 2
146
+ }
147
+
148
+ /// The number of bytes which is processed by one iteration of this algorithm's compression
149
+ /// function.
150
+ pub const fn block_size(self) -> usize {
151
+ match self {
152
+ HashAlgorithm::SHA1 => 64,
153
+ HashAlgorithm::SHA256 => 64,
154
+ }
155
+ }
156
+
157
+ /// The object ID representing the empty blob.
158
+ pub const fn empty_blob(self) -> &'static ObjectID {
159
+ match self {
160
+ HashAlgorithm::SHA1 => &Self::SHA1_EMPTY_BLOB,
161
+ HashAlgorithm::SHA256 => &Self::SHA256_EMPTY_BLOB,
162
+ }
163
+ }
164
+
165
+ /// The object ID representing the empty tree.
166
+ pub const fn empty_tree(self) -> &'static ObjectID {
167
+ match self {
168
+ HashAlgorithm::SHA1 => &Self::SHA1_EMPTY_TREE,
169
+ HashAlgorithm::SHA256 => &Self::SHA256_EMPTY_TREE,
170
+ }
171
+ }
172
+
173
+ /// The object ID which is all zeros.
174
+ pub const fn null_oid(self) -> &'static ObjectID {
175
+ match self {
176
+ HashAlgorithm::SHA1 => &Self::SHA1_NULL_OID,
177
+ HashAlgorithm::SHA256 => &Self::SHA256_NULL_OID,
178
+ }
179
+ }
180
+}