gix_object/
data.rs

1//! Contains a borrowed Object bound to a buffer holding its decompressed data.
2
3use crate::{BlobRef, CommitRef, CommitRefIter, Data, Kind, ObjectRef, TagRef, TagRefIter, TreeRef, TreeRefIter};
4
5impl<'a> Data<'a> {
6    /// Constructs a new data object from `kind` and `data`.
7    pub fn new(kind: Kind, data: &'a [u8]) -> Data<'a> {
8        Data { kind, data }
9    }
10    /// Decodes the data in the backing slice into a [`ObjectRef`], allowing to access all of its data
11    /// conveniently. The cost of parsing an object is negligible.
12    ///
13    /// **Note** that [mutable, decoded objects][crate::Object] can be created from [`Data`]
14    /// using [`crate::ObjectRef::into_owned()`].
15    pub fn decode(&self) -> Result<ObjectRef<'a>, crate::decode::Error> {
16        Ok(match self.kind {
17            Kind::Tree => ObjectRef::Tree(TreeRef::from_bytes(self.data)?),
18            Kind::Blob => ObjectRef::Blob(BlobRef { data: self.data }),
19            Kind::Commit => ObjectRef::Commit(CommitRef::from_bytes(self.data)?),
20            Kind::Tag => ObjectRef::Tag(TagRef::from_bytes(self.data)?),
21        })
22    }
23
24    /// Returns this object as tree iterator to parse entries one at a time to avoid allocations, or
25    /// `None` if this is not a tree object.
26    pub fn try_into_tree_iter(self) -> Option<TreeRefIter<'a>> {
27        match self.kind {
28            Kind::Tree => Some(TreeRefIter::from_bytes(self.data)),
29            _ => None,
30        }
31    }
32
33    /// Returns this object as commit iterator to parse tokens one at a time to avoid allocations, or
34    /// `None` if this is not a commit object.
35    pub fn try_into_commit_iter(self) -> Option<CommitRefIter<'a>> {
36        match self.kind {
37            Kind::Commit => Some(CommitRefIter::from_bytes(self.data)),
38            _ => None,
39        }
40    }
41
42    /// Returns this object as tag iterator to parse tokens one at a time to avoid allocations, or
43    /// `None` if this is not a tag object.
44    pub fn try_into_tag_iter(self) -> Option<TagRefIter<'a>> {
45        match self.kind {
46            Kind::Tag => Some(TagRefIter::from_bytes(self.data)),
47            _ => None,
48        }
49    }
50}
51
52/// Types supporting object hash verification
53pub mod verify {
54
55    /// Returned by [`crate::Data::verify_checksum()`]
56    #[derive(Debug, thiserror::Error)]
57    #[allow(missing_docs)]
58    pub enum Error {
59        #[error("Object expected to have id {desired}, but actual id was {actual}")]
60        ChecksumMismatch {
61            desired: gix_hash::ObjectId,
62            actual: gix_hash::ObjectId,
63        },
64    }
65
66    impl crate::Data<'_> {
67        /// Compute the checksum of `self` and compare it with the `desired` hash.
68        /// If the hashes do not match, an [`Error`] is returned, containing the actual
69        /// hash of `self`.
70        pub fn verify_checksum(&self, desired: &gix_hash::oid) -> Result<(), Error> {
71            let actual_id = crate::compute_hash(desired.kind(), self.kind, self.data);
72            if desired != actual_id {
73                return Err(Error::ChecksumMismatch {
74                    desired: desired.into(),
75                    actual: actual_id,
76                });
77            }
78            Ok(())
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn size_of_object() {
89        #[cfg(target_pointer_width = "64")]
90        assert_eq!(std::mem::size_of::<Data<'_>>(), 24, "this shouldn't change unnoticed");
91        #[cfg(target_pointer_width = "32")]
92        assert_eq!(std::mem::size_of::<Data<'_>>(), 12, "this shouldn't change unnoticed");
93    }
94}