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.
213 lines
5.8 KiB
Rust
213 lines
5.8 KiB
Rust
use crate::infohash::v1::U160;
|
|
use crate::infohash::{InfoHashCapable, InfoHashFromBytesError, InfoHashFromHashError, InfoHashV1};
|
|
use hex::{FromHex, ToHex};
|
|
use rand::random;
|
|
use std::convert::TryInto;
|
|
use std::fmt::{Debug, Display, Formatter};
|
|
use std::iter;
|
|
use std::ops::{BitAnd, BitOr, BitXor};
|
|
|
|
/// A make shift u265 type which uses a 2 u128's to keep operations speedy
|
|
/// Only really meant for ordering, bit operations and indexing so no math ops are implemented
|
|
#[derive(PartialOrd, Ord, Eq, PartialEq, Copy, Clone, Hash)]
|
|
pub struct U256(pub(crate) u128, pub(crate) u128);
|
|
|
|
impl U256 {
|
|
pub fn random() -> U256 {
|
|
U256(random(), random())
|
|
}
|
|
}
|
|
|
|
pub type InfoHash = U256;
|
|
|
|
impl Debug for U256 {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
if !f.alternate() {
|
|
f.write_str(&self.encode_hex::<String>())
|
|
} else {
|
|
f.debug_tuple("U256").field(&self.0).field(&self.1).finish()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for U256 {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str(self.encode_hex::<String>().as_str())
|
|
}
|
|
}
|
|
|
|
impl From<&[u8; 32]> for InfoHash {
|
|
fn from(input: &[u8; 32]) -> Self {
|
|
U256(
|
|
u128::from_be_bytes(input[0..16].try_into().unwrap()),
|
|
u128::from_be_bytes(input[16..32].try_into().unwrap()),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<[u8; 32]> for InfoHash {
|
|
fn from(input: [u8; 32]) -> Self {
|
|
Self::from(&input)
|
|
}
|
|
}
|
|
|
|
impl InfoHashCapable for InfoHash {
|
|
fn version(&self) -> u32 {
|
|
2
|
|
}
|
|
|
|
fn to_bytes(&self) -> Box<[u8]> {
|
|
self.to_byte_array().into()
|
|
}
|
|
|
|
fn to_v1(&self) -> InfoHashV1 {
|
|
U160(self.0, (self.1 >> 96) as u32)
|
|
}
|
|
|
|
fn from_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<Self, InfoHashFromBytesError> {
|
|
let bytes_ref = bytes.as_ref();
|
|
|
|
if bytes_ref.len() != 32 {
|
|
return Err(InfoHashFromBytesError::InvalidLength {
|
|
actual: bytes_ref.len(),
|
|
expected: Some(32),
|
|
});
|
|
}
|
|
|
|
let byte_array: &[u8; 32] = bytes_ref.try_into().unwrap();
|
|
Ok(Self::from(byte_array))
|
|
}
|
|
}
|
|
|
|
impl InfoHash {
|
|
pub fn to_byte_array(&self) -> [u8; 32] {
|
|
let mut bytes = [0u8; 32];
|
|
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() != 64 {
|
|
return Err(InfoHashFromHashError::InvalidLength {
|
|
expected: Some(64),
|
|
actual: hex.as_ref().len(),
|
|
});
|
|
}
|
|
|
|
let decoded = hex::decode(hex_ref).map_err(InfoHashFromHashError::HexEncodingError)?;
|
|
Ok(U256::from_bytes(decoded).unwrap())
|
|
}
|
|
}
|
|
|
|
impl ToHex for U256 {
|
|
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 U256 {
|
|
type Output = U256;
|
|
|
|
fn bitxor(self, rhs: Self) -> Self::Output {
|
|
U256(self.0 ^ rhs.0, self.1 ^ rhs.1)
|
|
}
|
|
}
|
|
|
|
impl BitAnd for U256 {
|
|
type Output = U256;
|
|
|
|
fn bitand(self, rhs: Self) -> Self::Output {
|
|
U256(self.0 & rhs.0, self.1 & rhs.1)
|
|
}
|
|
}
|
|
|
|
impl BitOr for U256 {
|
|
type Output = U256;
|
|
|
|
fn bitor(self, rhs: Self) -> Self::Output {
|
|
U256(self.0 | rhs.0, self.1 | rhs.1)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use crate::infohash::v2::{InfoHash, U256};
|
|
use crate::infohash::InfoHashCapable;
|
|
use hex::{FromHex, ToHex};
|
|
|
|
#[test]
|
|
fn conversion() {
|
|
let info_hash_bytes: [u8; 32] = [
|
|
0xa3, 0x1f, 0xe9, 0x65, 0x6f, 0xc8, 0xd3, 0xa4, 0x59, 0xe6, 0x23, 0xdc, 0x82, 0x04,
|
|
0xe6, 0xd0, 0x26, 0x8f, 0x8d, 0xf5, 0x6d, 0x73, 0x4d, 0xac, 0x3c, 0xa3, 0x26, 0x2e,
|
|
0xdb, 0x5d, 0xb8, 0x83,
|
|
];
|
|
let info_hash = InfoHash::from_bytes(info_hash_bytes).expect("Failed to parse info hash");
|
|
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 = "a31fe9656fc8d3a459e623dc8204e6d0268f8df56d734dac3ca3262edb5db883";
|
|
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 bit_operations() {
|
|
let info_hash_alpha: InfoHash = [0u8; 32].into();
|
|
let info_hash_omega: InfoHash = [255u8; 32].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!(U256(1, 0) > U256(0, u128::MAX));
|
|
assert!(U256(0, 0) < U256(0, u128::MAX));
|
|
}
|
|
}
|