diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-hash/src/object_id.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-hash/src/object_id.rs')
-rw-r--r-- | vendor/gix-hash/src/object_id.rs | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/vendor/gix-hash/src/object_id.rs b/vendor/gix-hash/src/object_id.rs new file mode 100644 index 000000000..d295fc555 --- /dev/null +++ b/vendor/gix-hash/src/object_id.rs @@ -0,0 +1,229 @@ +use std::{ + borrow::Borrow, + convert::TryInto, + fmt, + hash::{Hash, Hasher}, + ops::Deref, +}; + +use crate::{borrowed::oid, Kind, SIZE_OF_SHA1_DIGEST}; + +/// An owned hash identifying objects, most commonly Sha1 +#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Copy)] +#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))] +pub enum ObjectId { + /// A SHA 1 hash digest + Sha1([u8; SIZE_OF_SHA1_DIGEST]), +} + +// False positive: https://github.com/rust-lang/rust-clippy/issues/2627 +// ignoring some fields while hashing is perfectly valid and just leads to +// increased HashCollisions. One Sha1 being a prefix of another Sha256 is +// extremely unlikely to begin with so it doesn't matter. +// This implementation matches the `Hash` implementation for `oid` +// and allows the usage of custom Hashers that only copy a truncated ShaHash +#[allow(clippy::derive_hash_xor_eq)] +impl Hash for ObjectId { + fn hash<H: Hasher>(&self, state: &mut H) { + state.write(self.as_slice()) + } +} + +#[allow(missing_docs)] +pub mod decode { + use std::str::FromStr; + + use crate::object_id::ObjectId; + + /// An error returned by [`ObjectId::from_hex()`][crate::ObjectId::from_hex()] + #[derive(Debug, thiserror::Error)] + #[allow(missing_docs)] + pub enum Error { + #[error("A hash sized {0} hexadecimal characters is invalid")] + InvalidHexEncodingLength(usize), + #[error("Invalid character {c} at position {index}")] + Invalid { c: char, index: usize }, + } + + /// Hash decoding + impl ObjectId { + /// Create an instance from a `buffer` of 40 bytes encoded with hexadecimal notation. + /// + /// Such a buffer can be obtained using [`oid::write_hex_to(buffer)`][super::oid::write_hex_to()] + pub fn from_hex(buffer: &[u8]) -> Result<ObjectId, Error> { + use hex::FromHex; + match buffer.len() { + 40 => Ok(ObjectId::Sha1(<[u8; 20]>::from_hex(buffer).map_err( + |err| match err { + hex::FromHexError::InvalidHexCharacter { c, index } => Error::Invalid { c, index }, + hex::FromHexError::OddLength | hex::FromHexError::InvalidStringLength => { + unreachable!("BUG: This is already checked") + } + }, + )?)), + len => Err(Error::InvalidHexEncodingLength(len)), + } + } + } + + impl FromStr for ObjectId { + type Err = Error; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + Self::from_hex(s.as_bytes()) + } + } +} + +/// Access and conversion +impl ObjectId { + /// Returns the kind of hash used in this `Id` + #[inline] + pub fn kind(&self) -> crate::Kind { + match self { + ObjectId::Sha1(_) => crate::Kind::Sha1, + } + } + /// Return the raw byte slice representing this hash + #[inline] + pub fn as_slice(&self) -> &[u8] { + match self { + Self::Sha1(b) => b.as_ref(), + } + } + /// Return the raw mutable byte slice representing this hash + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + match self { + Self::Sha1(b) => b.as_mut(), + } + } + + /// The hash of an empty blob + #[inline] + pub const fn empty_blob(hash: Kind) -> ObjectId { + match hash { + Kind::Sha1 => { + ObjectId::Sha1(*b"\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91") + } + } + } + + /// The hash of an empty tree + #[inline] + pub const fn empty_tree(hash: Kind) -> ObjectId { + match hash { + Kind::Sha1 => { + ObjectId::Sha1(*b"\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04") + } + } + } + + /// Returns true if this hash consists of all null bytes + #[inline] + pub fn is_null(&self) -> bool { + match self { + ObjectId::Sha1(digest) => &digest[..] == oid::null_sha1().as_bytes(), + } + } + + /// Returns an Digest representing a hash with whose memory is zeroed. + #[inline] + pub const fn null(kind: crate::Kind) -> ObjectId { + match kind { + crate::Kind::Sha1 => Self::null_sha1(), + } + } +} + +/// Sha1 hash specific methods +impl ObjectId { + /// Instantiate an Digest from 20 bytes of a Sha1 digest. + #[inline] + fn new_sha1(id: [u8; SIZE_OF_SHA1_DIGEST]) -> Self { + ObjectId::Sha1(id) + } + + /// Instantiate an Digest from a slice 20 borrowed bytes of a Sha1 digest. + /// + /// Panics of the slice doesn't have a length of 20. + #[inline] + pub(crate) fn from_20_bytes(b: &[u8]) -> ObjectId { + let mut id = [0; SIZE_OF_SHA1_DIGEST]; + id.copy_from_slice(b); + ObjectId::Sha1(id) + } + + /// Returns an Digest representing a Sha1 with whose memory is zeroed. + #[inline] + pub(crate) const fn null_sha1() -> ObjectId { + ObjectId::Sha1([0u8; 20]) + } +} + +impl std::fmt::Debug for ObjectId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ObjectId::Sha1(_hash) => f.write_str("Sha1(")?, + } + for b in self.as_bytes() { + write!(f, "{b:02x}")?; + } + f.write_str(")") + } +} + +impl From<[u8; SIZE_OF_SHA1_DIGEST]> for ObjectId { + fn from(v: [u8; 20]) -> Self { + Self::new_sha1(v) + } +} + +impl From<&[u8]> for ObjectId { + fn from(v: &[u8]) -> Self { + match v.len() { + 20 => Self::Sha1(v.try_into().expect("prior length validation")), + other => panic!("BUG: unsupported hash len: {other}"), + } + } +} + +impl From<&crate::oid> for ObjectId { + fn from(v: &oid) -> Self { + match v.kind() { + crate::Kind::Sha1 => ObjectId::from_20_bytes(v.as_bytes()), + } + } +} + +impl Deref for ObjectId { + type Target = oid; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl AsRef<crate::oid> for ObjectId { + fn as_ref(&self) -> &oid { + oid::from_bytes_unchecked(self.as_slice()) + } +} + +impl Borrow<crate::oid> for ObjectId { + fn borrow(&self) -> &oid { + self.as_ref() + } +} + +impl fmt::Display for ObjectId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.to_hex()) + } +} + +impl PartialEq<&crate::oid> for ObjectId { + fn eq(&self, other: &&oid) -> bool { + self.as_ref() == *other + } +} |