1use std::hash;
2
3use crate::{Kind, ObjectId, EMPTY_BLOB_SHA1, EMPTY_TREE_SHA1, SIZE_OF_SHA1_DIGEST};
4
5#[cfg(feature = "sha256")]
6use crate::{EMPTY_BLOB_SHA256, EMPTY_TREE_SHA256, SIZE_OF_SHA256_DIGEST};
7
8#[derive(PartialEq, Eq, Ord, PartialOrd)]
21#[repr(transparent)]
22#[allow(non_camel_case_types)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize))]
24pub struct oid {
25 bytes: [u8],
26}
27
28#[allow(clippy::derived_hash_with_manual_eq)]
34impl hash::Hash for oid {
35 fn hash<H: hash::Hasher>(&self, state: &mut H) {
36 state.write(self.as_bytes());
37 }
38}
39
40#[derive(PartialEq, Eq, Hash, Ord, PartialOrd)]
42pub struct HexDisplay<'a> {
43 inner: &'a oid,
44 hex_len: usize,
45}
46
47impl std::fmt::Display for HexDisplay<'_> {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 let mut hex = Kind::hex_buf();
50 let hex = self.inner.hex_to_buf(hex.as_mut());
51 let max_len = hex.len();
52 f.write_str(&hex[..self.hex_len.min(max_len)])
53 }
54}
55
56impl std::fmt::Debug for oid {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(
59 f,
60 "{}({})",
61 match self.kind() {
62 Kind::Sha1 => "Sha1",
63 #[cfg(feature = "sha256")]
64 Kind::Sha256 => "Sha256",
65 },
66 self.to_hex(),
67 )
68 }
69}
70
71#[allow(missing_docs)]
73#[derive(Debug, thiserror::Error)]
74pub enum Error {
75 #[error("Cannot instantiate git hash from a digest of length {0}")]
76 InvalidByteSliceLength(usize),
77}
78
79impl oid {
81 #[inline]
83 pub fn try_from_bytes(digest: &[u8]) -> Result<&Self, Error> {
84 match digest.len() {
85 SIZE_OF_SHA1_DIGEST => Ok(
86 #[allow(unsafe_code)]
87 unsafe {
88 &*(std::ptr::from_ref::<[u8]>(digest) as *const oid)
89 },
90 ),
91 #[cfg(feature = "sha256")]
92 SIZE_OF_SHA256_DIGEST => Ok(
93 #[allow(unsafe_code)]
94 unsafe {
95 &*(std::ptr::from_ref::<[u8]>(digest) as *const oid)
96 },
97 ),
98 len => Err(Error::InvalidByteSliceLength(len)),
99 }
100 }
101
102 pub fn from_bytes_unchecked(value: &[u8]) -> &Self {
105 Self::from_bytes(value)
106 }
107
108 pub(crate) fn from_bytes(value: &[u8]) -> &Self {
110 #[allow(unsafe_code)]
111 unsafe {
112 &*(std::ptr::from_ref::<[u8]>(value) as *const oid)
113 }
114 }
115}
116
117impl oid {
119 #[inline]
121 pub fn kind(&self) -> Kind {
122 Kind::from_len_in_bytes(self.bytes.len())
123 }
124
125 #[inline]
127 pub fn first_byte(&self) -> u8 {
128 self.bytes[0]
129 }
130
131 #[inline]
133 pub fn as_bytes(&self) -> &[u8] {
134 &self.bytes
135 }
136
137 #[inline]
139 pub fn to_hex_with_len(&self, len: usize) -> HexDisplay<'_> {
140 HexDisplay {
141 inner: self,
142 hex_len: len,
143 }
144 }
145
146 #[inline]
148 pub fn to_hex(&self) -> HexDisplay<'_> {
149 HexDisplay {
150 inner: self,
151 hex_len: self.bytes.len() * 2,
152 }
153 }
154
155 #[inline]
161 #[must_use]
162 pub fn hex_to_buf<'a>(&self, buf: &'a mut [u8]) -> &'a mut str {
163 let num_hex_bytes = self.bytes.len() * 2;
164 faster_hex::hex_encode(&self.bytes, &mut buf[..num_hex_bytes])
165 .expect("buffer size must be at least twice the hash digest size in bytes")
166 }
167
168 #[inline]
170 pub fn write_hex_to(&self, out: &mut dyn std::io::Write) -> std::io::Result<()> {
171 let mut hex = Kind::hex_buf();
172 let hex_len = self.hex_to_buf(&mut hex).len();
173 out.write_all(&hex[..hex_len])
174 }
175
176 #[inline]
178 #[doc(alias = "is_zero", alias = "git2")]
179 pub fn is_null(&self) -> bool {
180 match self.kind() {
181 Kind::Sha1 => &self.bytes == oid::null_sha1().as_bytes(),
182 #[cfg(feature = "sha256")]
183 Kind::Sha256 => &self.bytes == oid::null_sha256().as_bytes(),
184 }
185 }
186
187 #[inline]
189 pub fn is_empty_blob(&self) -> bool {
190 match self.kind() {
191 Kind::Sha1 => &self.bytes == oid::empty_blob_sha1().as_bytes(),
192 #[cfg(feature = "sha256")]
193 Kind::Sha256 => &self.bytes == oid::empty_blob_sha256().as_bytes(),
194 }
195 }
196
197 #[inline]
199 pub fn is_empty_tree(&self) -> bool {
200 match self.kind() {
201 Kind::Sha1 => &self.bytes == oid::empty_tree_sha1().as_bytes(),
202 #[cfg(feature = "sha256")]
203 Kind::Sha256 => &self.bytes == oid::empty_tree_sha256().as_bytes(),
204 }
205 }
206}
207
208impl oid {
210 #[inline]
212 pub(crate) fn null_sha1() -> &'static Self {
213 oid::from_bytes([0u8; SIZE_OF_SHA1_DIGEST].as_ref())
214 }
215
216 #[inline]
218 #[cfg(feature = "sha256")]
219 pub(crate) fn null_sha256() -> &'static Self {
220 oid::from_bytes([0u8; SIZE_OF_SHA256_DIGEST].as_ref())
221 }
222
223 #[inline]
225 pub(crate) fn empty_blob_sha1() -> &'static Self {
226 oid::from_bytes(EMPTY_BLOB_SHA1)
227 }
228
229 #[inline]
231 #[cfg(feature = "sha256")]
232 pub(crate) fn empty_blob_sha256() -> &'static Self {
233 oid::from_bytes(EMPTY_BLOB_SHA256)
234 }
235
236 #[inline]
238 pub(crate) fn empty_tree_sha1() -> &'static Self {
239 oid::from_bytes(EMPTY_TREE_SHA1)
240 }
241
242 #[inline]
244 #[cfg(feature = "sha256")]
245 pub(crate) fn empty_tree_sha256() -> &'static Self {
246 oid::from_bytes(EMPTY_TREE_SHA256)
247 }
248}
249
250impl AsRef<oid> for &oid {
251 fn as_ref(&self) -> &oid {
252 self
253 }
254}
255
256impl<'a> TryFrom<&'a [u8]> for &'a oid {
257 type Error = Error;
258
259 fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
260 oid::try_from_bytes(value)
261 }
262}
263
264impl ToOwned for oid {
265 type Owned = ObjectId;
266
267 fn to_owned(&self) -> Self::Owned {
268 match self.kind() {
269 Kind::Sha1 => ObjectId::Sha1(self.bytes.try_into().expect("no bug in hash detection")),
270 #[cfg(feature = "sha256")]
271 Kind::Sha256 => ObjectId::Sha256(self.bytes.try_into().expect("no bug in hash detection")),
272 }
273 }
274}
275
276impl<'a> From<&'a [u8; SIZE_OF_SHA1_DIGEST]> for &'a oid {
277 fn from(v: &'a [u8; SIZE_OF_SHA1_DIGEST]) -> Self {
278 oid::from_bytes(v.as_ref())
279 }
280}
281
282impl std::fmt::Display for &oid {
283 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284 let mut buf = Kind::hex_buf();
285 f.write_str(self.hex_to_buf(&mut buf))
286 }
287}
288
289impl PartialEq<ObjectId> for &oid {
290 fn eq(&self, other: &ObjectId) -> bool {
291 *self == other.as_ref()
292 }
293}
294
295#[cfg(feature = "serde")]
299impl<'de: 'a, 'a> serde::Deserialize<'de> for &'a oid {
300 fn deserialize<D>(deserializer: D) -> Result<Self, <D as serde::Deserializer<'de>>::Error>
301 where
302 D: serde::Deserializer<'de>,
303 {
304 struct __Visitor<'de: 'a, 'a> {
305 marker: std::marker::PhantomData<&'a oid>,
306 lifetime: std::marker::PhantomData<&'de ()>,
307 }
308 impl<'de: 'a, 'a> serde::de::Visitor<'de> for __Visitor<'de, 'a> {
309 type Value = &'a oid;
310 fn expecting(&self, __formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311 std::fmt::Formatter::write_str(__formatter, "tuple struct Digest")
312 }
313 #[inline]
314 fn visit_newtype_struct<__E>(self, __e: __E) -> std::result::Result<Self::Value, __E::Error>
315 where
316 __E: serde::Deserializer<'de>,
317 {
318 let __field0: &'a [u8] = match <&'a [u8] as serde::Deserialize>::deserialize(__e) {
319 Ok(__val) => __val,
320 Err(__err) => {
321 return Err(__err);
322 }
323 };
324 Ok(oid::try_from_bytes(__field0).expect("hash of known length"))
325 }
326 #[inline]
327 fn visit_seq<__A>(self, mut __seq: __A) -> std::result::Result<Self::Value, __A::Error>
328 where
329 __A: serde::de::SeqAccess<'de>,
330 {
331 let __field0 = match match serde::de::SeqAccess::next_element::<&'a [u8]>(&mut __seq) {
332 Ok(__val) => __val,
333 Err(__err) => {
334 return Err(__err);
335 }
336 } {
337 Some(__value) => __value,
338 None => {
339 return Err(serde::de::Error::invalid_length(
340 0usize,
341 &"tuple struct Digest with 1 element",
342 ));
343 }
344 };
345 Ok(oid::try_from_bytes(__field0).expect("hash of known length"))
346 }
347 }
348 serde::Deserializer::deserialize_newtype_struct(
349 deserializer,
350 "Digest",
351 __Visitor {
352 marker: std::marker::PhantomData::<&'a oid>,
353 lifetime: std::marker::PhantomData,
354 },
355 )
356 }
357}