diff options
author | Neal H. Walfield <neal@pep.foundation> | 2021-04-02 12:09:40 +0200 |
---|---|---|
committer | Neal H. Walfield <neal@pep.foundation> | 2021-04-02 12:09:40 +0200 |
commit | 2522962e2aa6fb30546ccf2fcd9aa14115962d1b (patch) | |
tree | 1964cb72d4b3889e1eb3d23568d31507084780ea | |
parent | e990add46c9acd1616ab3f03c6bd90f2dc5b541d (diff) |
WIP: Add a smarter implementation of PartialEq for Cert.neal/cert-eq
- Don't derive PartialEq for Cert. Instead, use lightweight tests
to detect differences faster. If the lightweight tests pass, then
fallback to a byte comparison.
- See https://gitlab.com/sequoia-pgp/sequoia-octopus-librnp/-/issues/25
-rw-r--r-- | openpgp/src/cert.rs | 77 |
1 files changed, 76 insertions, 1 deletions
diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs index ebdf06c2..46d3972d 100644 --- a/openpgp/src/cert.rs +++ b/openpgp/src/cert.rs @@ -701,7 +701,7 @@ pub trait Preferences<'a>: seal::Sealed { /// # Ok(()) /// # } /// ``` -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct Cert { primary: PrimaryKeyBundle<key::PublicParts>, @@ -717,6 +717,81 @@ pub struct Cert { } assert_send_and_sync!(Cert); +impl PartialEq for Cert { + fn eq(&self, other: &Self) -> bool { + // Quick compare. + if self.fingerprint() != other.fingerprint() { + return false; + } + + // Quickly compare components. + let mut a_keys = self.keys().map(|k| { + (k.fingerprint(), + k.self_signatures.len(), + k.certifications.len(), + k.self_revocations.len(), + k.other_revocations.len()) + }); + let mut b_keys = other.keys().map(|k| { + (k.fingerprint(), + k.self_signatures.len(), + k.certifications.len(), + k.self_revocations.len(), + k.other_revocations.len()) + }); + + loop { + let a = a_keys.next(); + let b = b_keys.next(); + + if a != b { + return false; + } + if a.is_none() && b.is_none() { + break; + } + } + + let mut a_uids = self.userids().map(|u| { + (u.userid(), + u.self_signatures.len(), + u.certifications.len(), + u.self_revocations.len(), + u.other_revocations.len()) + }); + let mut b_uids = other.userids().map(|u| { + (u.userid(), + u.self_signatures.len(), + u.certifications.len(), + u.self_revocations.len(), + u.other_revocations.len()) + }); + + loop { + let a = a_uids.next(); + let b = b_uids.next(); + + if a != b { + return false; + } + if a.is_none() && b.is_none() { + break; + } + } + + // There's a chance that there equal. Try the expensive way. + use crate::serialize::Serialize; + + let mut a = Vec::with_capacity(4096); + let mut b = Vec::with_capacity(4096); + + self.serialize(&mut a).expect("test code"); + other.serialize(&mut b).expect("test code"); + + a == b + } +} + impl std::str::FromStr for Cert { type Err = anyhow::Error; |