use std::ops::BitXor;
use error::{LengthError, LengthResult, LengthErrorKind};
mod builder;
pub use sha::builder::ShaHashBuilder;
pub const SHA_HASH_LEN: usize = 20;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct ShaHash {
hash: [u8; SHA_HASH_LEN],
}
impl ShaHash {
pub fn from_bytes(bytes: &[u8]) -> ShaHash {
ShaHashBuilder::new().add_bytes(bytes).build()
}
pub fn from_hash(hash: &[u8]) -> LengthResult<ShaHash> {
if hash.len() != SHA_HASH_LEN {
Err(LengthError::new(LengthErrorKind::LengthExpected, SHA_HASH_LEN))
} else {
let mut my_hash = [0u8; SHA_HASH_LEN];
my_hash.iter_mut().zip(hash.iter()).map(|(dst, src)| *dst = *src).count();
Ok(ShaHash { hash: my_hash })
}
}
pub fn bits<'a>(&'a self) -> Bits<'a> {
Bits::new(&self.hash)
}
pub fn len() -> usize {
SHA_HASH_LEN
}
}
impl AsRef<[u8]> for ShaHash {
fn as_ref(&self) -> &[u8] {
&self.hash
}
}
impl Into<[u8; SHA_HASH_LEN]> for ShaHash {
fn into(self) -> [u8; SHA_HASH_LEN] {
self.hash
}
}
impl From<[u8; SHA_HASH_LEN]> for ShaHash {
fn from(sha_hash: [u8; SHA_HASH_LEN]) -> ShaHash {
ShaHash { hash: sha_hash }
}
}
impl PartialEq<[u8]> for ShaHash {
fn eq(&self, other: &[u8]) -> bool {
let is_equal = other.len() == self.hash.len();
self.hash.iter().zip(other.iter()).fold(is_equal, |prev, (h, o)| prev && h == o)
}
}
impl BitXor<ShaHash> for ShaHash {
type Output = ShaHash;
fn bitxor(mut self, rhs: ShaHash) -> ShaHash {
for (src, dst) in rhs.hash.iter().zip(self.hash.iter_mut()) {
*dst = *src ^ *dst;
}
self
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum XorRep {
Diff,
Same,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct XorBits<'a> {
bits: Bits<'a>,
}
impl<'a> Iterator for XorBits<'a> {
type Item = XorRep;
fn next(&mut self) -> Option<XorRep> {
self.bits.next().map(|n| {
match n {
BitRep::Set => XorRep::Diff,
BitRep::Unset => XorRep::Same,
}
})
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum BitRep {
Set,
Unset,
}
impl PartialEq<XorRep> for BitRep {
fn eq(&self, other: &XorRep) -> bool {
match (self, other) {
(&BitRep::Set, &XorRep::Diff) => true,
(&BitRep::Unset, &XorRep::Same) => true,
_ => false,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Bits<'a> {
bytes: &'a [u8],
bit_pos: usize,
}
impl<'a> Bits<'a> {
fn new(bytes: &'a [u8]) -> Bits<'a> {
Bits {
bytes: bytes,
bit_pos: 0,
}
}
}
impl<'a> Iterator for Bits<'a> {
type Item = BitRep;
fn next(&mut self) -> Option<BitRep> {
if self.bit_pos < self.bytes.len() * 8 {
let byte_index = self.bit_pos / 8;
let bit_offset = 7 - (self.bit_pos % 8);
let bit_value = self.bytes[byte_index] >> bit_offset;
self.bit_pos += 1;
Some(bit_value).map(|x| if x == 1 {
BitRep::Set
} else {
BitRep::Unset
})
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::{ShaHash, XorRep};
#[test]
fn positive_no_leading_zeroes() {
let zero_bits = ShaHash::from([0u8; super::SHA_HASH_LEN]);
let one_bits = ShaHash::from([255u8; super::SHA_HASH_LEN]);
let xor_hash = zero_bits ^ one_bits;
let leading_zeroes = xor_hash.bits().take_while(|&n| n == XorRep::Same).count();
assert!(leading_zeroes == 0);
}
#[test]
fn positive_all_leading_zeroes() {
let first_one_bits = ShaHash::from([255u8; super::SHA_HASH_LEN]);
let second_one_bits = ShaHash::from([255u8; super::SHA_HASH_LEN]);
let xor_hash = first_one_bits ^ second_one_bits;
let leading_zeroes = xor_hash.bits().take_while(|&n| n == XorRep::Same).count();
assert!(leading_zeroes == (super::SHA_HASH_LEN * 8));
}
#[test]
fn positive_one_leading_zero() {
let zero_bits = ShaHash::from([0u8; super::SHA_HASH_LEN]);
let mut bytes = [255u8; super::SHA_HASH_LEN];
bytes[0] = 127;
let mostly_one_bits = ShaHash::from(bytes);
let xor_hash = zero_bits ^ mostly_one_bits;
let leading_zeroes = xor_hash.bits().take_while(|&n| n == XorRep::Same).count();
assert!(leading_zeroes == 1);
}
#[test]
fn positive_one_trailing_zero() {
let zero_bits = ShaHash::from([0u8; super::SHA_HASH_LEN]);
let mut bytes = [255u8; super::SHA_HASH_LEN];
bytes[super::SHA_HASH_LEN - 1] = 254;
let mostly_zero_bits = ShaHash::from(bytes);
let xor_hash = zero_bits ^ mostly_zero_bits;
let leading_zeroes = xor_hash.bits().take_while(|&n| n == XorRep::Same).count();
assert!(leading_zeroes == 0);
}
#[test]
#[should_panic]
fn negative_from_hash_too_long() {
let bits = [0u8; super::SHA_HASH_LEN + 1];
ShaHash::from_hash(&bits).unwrap();
}
#[test]
#[should_panic]
fn negative_from_hash_too_short() {
let bits = [0u8; super::SHA_HASH_LEN - 1];
ShaHash::from_hash(&bits).unwrap();
}
}