You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
355 lines
9.8 KiB
Rust
355 lines
9.8 KiB
Rust
use crate::infohash::{InfoHashCapable, InfoHashFromBytesError, InfoHashFromHashError, InfoHashV1};
|
|
use bendy::decoding::{Error as DecodingError, FromBencode, Object};
|
|
use bendy::encoding::{Error as EncodingError, SingleItemEncoder, ToBencode};
|
|
use hex::{FromHex, ToHex};
|
|
use rand::random;
|
|
use serde::de::Error;
|
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
use std::convert::{TryFrom, TryInto};
|
|
use std::fmt::{Debug, Display, Formatter};
|
|
use std::iter;
|
|
use std::ops::{Add, BitAnd, BitOr, BitXor, Rem, Sub};
|
|
|
|
/// A make shift u160 type which uses a single u128 and u32 to keep operations speedy
|
|
/// Only really meant for ordering, bit operations and indexing
|
|
#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Default)]
|
|
pub struct U160(pub(crate) u128, pub(crate) u32);
|
|
|
|
impl Serialize for U160 {
|
|
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
serializer.collect_str(&self)
|
|
}
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for U160 {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
String::deserialize(deserializer)
|
|
.and_then(|x| U160::from_hex(x).map_err(|err| D::Error::custom(err.to_string())))
|
|
}
|
|
}
|
|
|
|
impl U160 {
|
|
pub const MAX: U160 = U160(u128::MAX, u32::MAX);
|
|
pub const MIN: U160 = U160(u128::MIN, u32::MIN);
|
|
pub const ONE: U160 = U160(0, 1);
|
|
pub const ZERO: U160 = U160(0, 0);
|
|
|
|
pub fn leading_zeroes(&self) -> u8 {
|
|
let mut leading_zeroes = self.0.leading_zeros() as u8;
|
|
if leading_zeroes == 128 {
|
|
leading_zeroes += self.1.leading_zeros() as u8;
|
|
}
|
|
|
|
leading_zeroes
|
|
}
|
|
}
|
|
|
|
impl ToBencode for U160 {
|
|
const MAX_DEPTH: usize = 0;
|
|
|
|
fn encode(&self, encoder: SingleItemEncoder) -> Result<(), EncodingError> {
|
|
encoder.emit_bytes(&self.to_byte_array()[..])
|
|
}
|
|
}
|
|
|
|
impl FromBencode for U160 {
|
|
fn decode_bencode_object(object: Object) -> Result<Self, DecodingError>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
U160::from_bytes(object.try_into_bytes()?).map_err(|x| DecodingError::malformed_content(x))
|
|
}
|
|
}
|
|
|
|
impl<'obj, 'ser> TryFrom<Object<'obj, 'ser>> for U160 {
|
|
type Error = DecodingError;
|
|
|
|
fn try_from(value: Object<'obj, 'ser>) -> Result<Self, Self::Error> {
|
|
U160::decode_bencode_object(value)
|
|
}
|
|
}
|
|
|
|
impl Add for U160 {
|
|
type Output = U160;
|
|
|
|
fn add(self, rhs: Self) -> Self::Output {
|
|
let (lower, overflow) = self.1.overflowing_add(rhs.1);
|
|
U160(overflow as u128 + self.0 + rhs.0, lower)
|
|
}
|
|
}
|
|
|
|
impl Sub for U160 {
|
|
type Output = U160;
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
|
let (lower, overflow) = self.1.overflowing_sub(rhs.1);
|
|
U160((self.0 - rhs.0) - overflow as u128, lower)
|
|
}
|
|
}
|
|
|
|
impl U160 {
|
|
pub fn random() -> U160 {
|
|
U160(random(), random())
|
|
}
|
|
|
|
pub fn half(&self) -> U160 {
|
|
U160(
|
|
self.0 / 2,
|
|
(self.1 / 2)
|
|
+ if self.0.rem(2) == 1 {
|
|
0b10000000_00000000_00000000_00000000
|
|
} else {
|
|
0
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
pub type InfoHash = U160;
|
|
|
|
impl Debug for U160 {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
if !f.alternate() {
|
|
f.write_str(&self.encode_hex::<String>())
|
|
} else {
|
|
f.debug_tuple("U160").field(&self.0).field(&self.1).finish()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for U160 {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str(self.encode_hex::<String>().as_str())
|
|
}
|
|
}
|
|
|
|
impl From<&[u8; 20]> for InfoHash {
|
|
fn from(input: &[u8; 20]) -> Self {
|
|
U160(
|
|
u128::from_be_bytes(input[0..16].try_into().unwrap()),
|
|
u32::from_be_bytes(input[16..20].try_into().unwrap()),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<[u8; 20]> for InfoHash {
|
|
fn from(input: [u8; 20]) -> Self {
|
|
Self::from(&input)
|
|
}
|
|
}
|
|
|
|
impl InfoHashCapable for InfoHash {
|
|
fn version(&self) -> u32 {
|
|
1
|
|
}
|
|
|
|
fn to_bytes(&self) -> Box<[u8]> {
|
|
self.to_byte_array().into()
|
|
}
|
|
|
|
fn to_v1(&self) -> InfoHashV1 {
|
|
*self
|
|
}
|
|
|
|
fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, InfoHashFromBytesError> {
|
|
let bytes_ref = bytes.as_ref();
|
|
|
|
if bytes_ref.len() != 20 {
|
|
return Err(InfoHashFromBytesError::InvalidLength {
|
|
actual: bytes_ref.len(),
|
|
expected: Some(20),
|
|
});
|
|
}
|
|
|
|
let byte_array: &[u8; 20] = bytes_ref.try_into().unwrap();
|
|
Ok(Self::from(byte_array))
|
|
}
|
|
}
|
|
|
|
impl InfoHash {
|
|
pub fn to_byte_array(&self) -> [u8; 20] {
|
|
let mut bytes = [0u8; 20];
|
|
bytes[0..16].copy_from_slice(&self.0.to_be_bytes());
|
|
bytes[16..].copy_from_slice(&self.1.to_be_bytes());
|
|
bytes
|
|
}
|
|
}
|
|
|
|
impl FromHex for InfoHash {
|
|
type Error = InfoHashFromHashError;
|
|
|
|
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
|
let hex_ref = hex.as_ref();
|
|
if hex_ref.len() != 40 {
|
|
return Err(InfoHashFromHashError::InvalidLength {
|
|
expected: Some(40),
|
|
actual: hex.as_ref().len(),
|
|
});
|
|
}
|
|
|
|
let decoded = hex::decode(hex_ref).map_err(InfoHashFromHashError::HexEncodingError)?;
|
|
Ok(U160::from_bytes(decoded).unwrap())
|
|
}
|
|
}
|
|
|
|
impl ToHex for InfoHash {
|
|
fn encode_hex<T: iter::FromIterator<char>>(&self) -> T {
|
|
self.to_byte_array().encode_hex()
|
|
}
|
|
|
|
fn encode_hex_upper<T: iter::FromIterator<char>>(&self) -> T {
|
|
self.to_byte_array().encode_hex_upper()
|
|
}
|
|
}
|
|
|
|
impl BitXor for U160 {
|
|
type Output = U160;
|
|
|
|
fn bitxor(self, rhs: Self) -> Self::Output {
|
|
U160(self.0 ^ rhs.0, self.1 ^ rhs.1)
|
|
}
|
|
}
|
|
|
|
impl BitAnd for U160 {
|
|
type Output = U160;
|
|
|
|
fn bitand(self, rhs: Self) -> Self::Output {
|
|
U160(self.0 & rhs.0, self.1 & rhs.1)
|
|
}
|
|
}
|
|
|
|
impl BitOr for U160 {
|
|
type Output = U160;
|
|
|
|
fn bitor(self, rhs: Self) -> Self::Output {
|
|
U160(self.0 | rhs.0, self.1 | rhs.1)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use crate::infohash::v1::{InfoHash, U160};
|
|
use crate::infohash::{InfoHashCapable, InfoHashFromBytesError, InfoHashFromHashError};
|
|
use hex::{FromHex, ToHex};
|
|
|
|
#[test]
|
|
fn conversion() {
|
|
let info_hash_bytes: [u8; 20] = [
|
|
0x0a, 0xfe, 0x96, 0xa8, 0x62, 0x37, 0x8e, 0x7f, 0x69, 0x9d, 0x54, 0x6b, 0x7b, 0x8f,
|
|
0xe6, 0xfc, 0x47, 0xd0, 0xe2, 0x4a,
|
|
];
|
|
let info_hash = InfoHash::from_bytes(info_hash_bytes).expect("Failed to parse info hash");
|
|
assert_eq!(1, info_hash.version());
|
|
assert_eq!(info_hash_bytes, info_hash.to_byte_array(), "Failed to assert that info hash could be converted to byte format and back without changing")
|
|
}
|
|
|
|
#[test]
|
|
fn hex_conversion() {
|
|
let info_hash_hex = "0afe96a862378e7f699d546b7b8fe6fc47d0e24a";
|
|
let info_hash = InfoHash::from_hex(info_hash_hex).expect("Failed to parse info hash");
|
|
assert_eq!(info_hash_hex, info_hash.encode_hex::<String>().as_str(), "Failed to assert that info hash could be converted to hex format and back without changing")
|
|
}
|
|
|
|
#[test]
|
|
fn formatting() {
|
|
let info_hash_hex = "0afe96a862378e7f699d546b7b8fe6fc47d0e24a";
|
|
let info_hash = InfoHash::from_hex(info_hash_hex).expect("Failed to parse info hash");
|
|
assert_eq!(
|
|
"0afe96a862378e7f699d546b7b8fe6fc47d0e24a",
|
|
format!("{}", info_hash)
|
|
);
|
|
|
|
assert_eq!(
|
|
"0afe96a862378e7f699d546b7b8fe6fc47d0e24a",
|
|
format!("{:?}", info_hash)
|
|
);
|
|
|
|
assert_eq!(
|
|
"U160(
|
|
14614179062085549902615142429809960700,
|
|
1204871754,
|
|
)",
|
|
format!("{:#?}", info_hash)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn bit_operations() {
|
|
let info_hash_alpha: InfoHash = [0u8; 20].into();
|
|
let info_hash_omega: InfoHash = [255u8; 20].into();
|
|
assert_eq!(
|
|
info_hash_omega ^ info_hash_omega,
|
|
info_hash_alpha,
|
|
"XOR has invalid result"
|
|
);
|
|
|
|
assert_eq!(
|
|
info_hash_alpha ^ info_hash_omega,
|
|
info_hash_omega,
|
|
"XOR has invalid result"
|
|
);
|
|
|
|
assert_eq!(
|
|
info_hash_alpha | info_hash_omega,
|
|
info_hash_omega,
|
|
"OR has invalid result"
|
|
);
|
|
|
|
assert_eq!(
|
|
info_hash_alpha | info_hash_alpha,
|
|
info_hash_alpha,
|
|
"OR has invalid result"
|
|
);
|
|
|
|
assert_eq!(
|
|
info_hash_alpha & info_hash_omega,
|
|
info_hash_alpha,
|
|
"OR has invalid result"
|
|
);
|
|
|
|
assert_eq!(
|
|
info_hash_omega & info_hash_omega,
|
|
info_hash_omega,
|
|
"OR has invalid result"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ordering() {
|
|
assert!(U160(1, 0) > U160(0, u32::MAX));
|
|
assert!(U160(0, 0) < U160(0, u32::MAX));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_length() {
|
|
let info_hash_hex = "0afe96a862378e7f699d546b7b8fe6fc47d0e24a3";
|
|
let info_hash = InfoHash::from_hex(info_hash_hex).expect_err("No error thrown");
|
|
|
|
if let InfoHashFromHashError::InvalidLength { expected, actual } = info_hash {
|
|
assert_eq!(expected, Some(40));
|
|
assert_eq!(actual, 41);
|
|
} else {
|
|
panic!("Should've thrown invalid length");
|
|
}
|
|
|
|
let info_hash_hex = hex::decode("0afe96a862378e7f699d546b7b8fe6fc47d0e24aaa").unwrap();
|
|
let info_hash = InfoHash::from_bytes(info_hash_hex).expect_err("No error thrown");
|
|
|
|
let InfoHashFromBytesError::InvalidLength { expected, actual } = info_hash;
|
|
assert_eq!(expected, Some(20));
|
|
assert_eq!(actual, 21);
|
|
}
|
|
|
|
#[test]
|
|
fn u160_math() {
|
|
assert_eq!(U160::ZERO + U160::ONE, U160::ONE);
|
|
assert_eq!(U160(1, 0) - U160::ONE, U160(0, u32::MAX))
|
|
}
|
|
}
|