mercurial-types: allow encoding Sha1 as a Thrift structure

Summary: Will be used for Thrift envelopes.

Reviewed By: jsgf

Differential Revision: D7771214

fbshipit-source-id: 3f0dbf77793064f2606ebe34672629c4e49cc7fe
This commit is contained in:
Siddharth Agarwal 2018-04-27 13:58:11 -07:00 committed by Facebook Github Bot
parent c133d8cedf
commit d005c83751
4 changed files with 59 additions and 1 deletions

View File

@ -10,6 +10,7 @@ pub use failure::{Error, ResultExt};
pub enum ErrorKind {
#[fail(display = "invalid sha-1 input: {}", _0)] InvalidSha1Input(String),
#[fail(display = "invalid fragment list: {}", _0)] InvalidFragmentList(String),
#[fail(display = "invalid Thrift structure '{}': {}", _0, _1)] InvalidThrift(String, String),
}
pub type Result<T> = ::std::result::Result<T, Error>;

View File

@ -12,6 +12,8 @@ use quickcheck::{single_shrinker, Arbitrary, Gen};
use rust_crypto::digest::Digest;
use rust_crypto::sha1;
use thrift;
use errors::*;
pub const NULL: Sha1 = Sha1([0; 20]);
@ -40,6 +42,20 @@ impl Sha1 {
}
}
pub fn from_thrift(h: thrift::Sha1) -> Result<Self> {
// Currently this doesn't require consuming b, but hopefully with T26959816 this
// code will be able to convert a SmallVec directly into an array.
if h.0.len() != 20 {
bail_err!(ErrorKind::InvalidThrift(
"Sha1".into(),
format!("wrong length: expected 20, got {}", h.0.len())
));
}
let mut arr = [0u8; 20];
arr.copy_from_slice(&h.0[..]);
Ok(Sha1(arr))
}
/// Construct a `Sha1` from an array of 20 bytes.
#[inline]
pub const fn from_byte_array(arr: [u8; 20]) -> Sha1 {
@ -64,6 +80,12 @@ impl Sha1 {
AsciiString::from_ascii_unchecked(v)
}
}
pub fn into_thrift(self) -> thrift::Sha1 {
// This doesn't need to consume self today, but once T26959816 is implemented it
// should be possible to do that without copying.
thrift::Sha1(self.0.to_vec())
}
}
/// Context for incrementally computing a `Sha1` hash.
@ -163,10 +185,11 @@ impl Context {
#[cfg(test)]
mod test {
use super::{Sha1, NULL};
use quickcheck::TestResult;
use std::str::FromStr;
use super::*;
#[cfg_attr(rustfmt, rustfmt_skip)]
const NILHASH: Sha1 = Sha1([0xda, 0x39, 0xa3, 0xee,
0x5e, 0x6b, 0x4b, 0x0d,
@ -241,6 +264,24 @@ mod test {
};
}
#[test]
fn parse_thrift() {
let null_thrift = thrift::Sha1(vec![0; 20]);
assert_eq!(NULL, Sha1::from_thrift(null_thrift.clone()).unwrap());
assert_eq!(NULL.into_thrift(), null_thrift);
let nil_thrift = thrift::Sha1(NILHASH.0.to_vec());
assert_eq!(NILHASH, Sha1::from_thrift(nil_thrift.clone()).unwrap());
assert_eq!(NILHASH.into_thrift(), nil_thrift);
}
#[test]
fn parse_thrift_bad() {
Sha1::from_thrift(thrift::Sha1(vec![])).expect_err("unexpected OK - zero len");
Sha1::from_thrift(thrift::Sha1(vec![0; 19])).expect_err("unexpected OK - too short");
Sha1::from_thrift(thrift::Sha1(vec![0; 21])).expect_err("unexpected OK - too long");
}
quickcheck! {
fn parse_roundtrip(v: Vec<u8>) -> TestResult {
if v.len() != 20 {
@ -253,6 +294,13 @@ mod test {
TestResult::from_bool(h == sh)
}
fn thrift_roundtrip(h: Sha1) -> bool {
let v = h.into_thrift();
let sh = Sha1::from_thrift(v)
.expect("converting a valid Thrift structure should always work");
h == sh
}
fn to_hex_roundtrip(h: Sha1) -> bool {
let v = h.to_hex();
let sh = Sha1::from_ascii_str(&v).unwrap();

View File

@ -76,6 +76,7 @@ extern crate serde_derive;
extern crate futures_ext;
extern crate mononoke_types;
extern crate mononoke_types_thrift;
extern crate storage_types;
pub mod bdiff;
@ -114,6 +115,10 @@ pub use errors::{Error, ErrorKind};
#[cfg(test)]
mod test;
mod thrift {
pub use mononoke_types_thrift::*;
}
impl asyncmemo::Weight for RepositoryId {
fn get_weight(&self) -> usize {
std::mem::size_of::<RepositoryId>()

View File

@ -35,6 +35,10 @@ typedef Blake2 UnodeId (hs.newtype)
typedef Blake2 ChangesetId (hs.newtype)
typedef Blake2 ContentId (hs.newtype)
// mercurial_types defines Sha1, and it's most convenient to stick this in here.
// This can be moved away in the future if necessary.
typedef binary Sha1 (hs.newtype)
// A path in a repo is stored as a list of elements. This is so that the sort
// order of paths is the same as that of a tree traversal, so that deltas on
// manifests can be applied in a streaming way.