diff --git a/src/protocol/common.rs b/src/protocol/common.rs index 5d69ed0e1..f34894d38 100644 --- a/src/protocol/common.rs +++ b/src/protocol/common.rs @@ -1,3 +1,4 @@ +use std::hash::Hash; use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes}; use serde::{Deserialize, Serialize}; @@ -241,3 +242,27 @@ impl Serialize for PeerId { obj.serialize(serializer) } } + +pub trait ToBytesVec { + fn to_bytes_vec(&self) -> Vec; +} + +impl ToBytesVec for std::net::IpAddr { + fn to_bytes_vec(&self) -> Vec { + match self { + std::net::IpAddr::V4(ip) => ip.octets().to_vec(), + std::net::IpAddr::V6(ip) => ip.octets().to_vec(), + } + } +} + +impl ToBytesVec for std::net::SocketAddr { + fn to_bytes_vec(&self) -> Vec { + [ + self.ip().to_bytes_vec().as_slice(), + self.port().to_be_bytes().as_slice() + ].concat() + } +} + + diff --git a/src/udp/connection/client_id.rs b/src/udp/connection/client_id.rs index 00b6ef43e..0d1f893f5 100644 --- a/src/udp/connection/client_id.rs +++ b/src/udp/connection/client_id.rs @@ -1,18 +1,20 @@ //! ClientId is a unique ID for the UDP tracker client. //! Currently implemented with a hash of the IP and port. -use std::net::{SocketAddr, IpAddr}; +use std::net::{SocketAddr}; -use blake3::OUT_LEN; +use blake3::{Hash, OUT_LEN}; +use crate::ToBytesVec; #[derive(PartialEq, Debug, Copy, Clone)] pub struct ClientId { - value: [u8; 4], + pub value: [u8; 4], } impl ClientId { /// It generates the ID from the socket address (IP + port) pub fn from_socket_address(remote_address: &SocketAddr) -> Self { let unique_socket_id = generate_id_for_socket_address(remote_address); + ClientId { value: unique_socket_id } @@ -23,49 +25,23 @@ impl ClientId { let mut client_id = ClientId { value: [0u8; 4] }; + client_id.value.copy_from_slice(slice); client_id } - - pub fn to_bytes(&self) -> [u8; 4] { - self.value - } } /// It generates an unique ID for a socket address (IP + port) fn generate_id_for_socket_address(remote_address: &SocketAddr) -> [u8; 4] { - let socket_addr_as_bytes: Vec = convert_socket_address_into_bytes(remote_address); - - let hashed_socket_addr = hash(&socket_addr_as_bytes); + let remote_address_as_bytes = remote_address.to_bytes_vec(); - let remote_id = get_first_four_bytes_from(&hashed_socket_addr); + let hashed_socket_addr = hash(&remote_address_as_bytes); - remote_id + get_first_four_bytes_from(hashed_socket_addr.as_bytes()) } -fn convert_socket_address_into_bytes(socket_addr: &SocketAddr) -> Vec { - let bytes: Vec = [ - convert_ip_into_bytes(socket_addr.ip()).as_slice(), - convert_port_into_bytes(socket_addr.port()).as_slice(), - ].concat(); - bytes -} - -fn convert_ip_into_bytes(ip_addr: IpAddr) -> Vec { - match ip_addr { - IpAddr::V4(ip) => ip.octets().to_vec(), - IpAddr::V6(ip) => ip.octets().to_vec(), - } -} - -fn convert_port_into_bytes(port: u16) -> [u8; 2] { - port.to_be_bytes() -} - -fn hash(bytes: &[u8]) -> [u8; OUT_LEN]{ - let hash = blake3::hash(bytes); - let bytes = hash.as_bytes().clone(); - bytes +fn hash(bytes: &[u8]) -> Hash { + blake3::hash(bytes) } fn get_first_four_bytes_from(bytes: &[u8; OUT_LEN]) -> [u8; 4] { @@ -78,8 +54,7 @@ fn get_first_four_bytes_from(bytes: &[u8; OUT_LEN]) -> [u8; 4] { #[cfg(test)] mod tests { use std::net::{IpAddr, Ipv4Addr, SocketAddr, Ipv6Addr}; - - use crate::udp::connection::client_id::convert_ip_into_bytes; + use crate::ToBytesVec; use super::ClientId; @@ -95,7 +70,7 @@ mod tests { fn client_id_should_be_unique_for_clients_with_different_ip() { let client_1_socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); let client_2_with_different_socket_ip = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 8080); - + assert_ne!(ClientId::from_socket_address(&client_1_socket_addr), ClientId::from_socket_address(&client_2_with_different_socket_ip)); } @@ -103,14 +78,14 @@ mod tests { fn client_id_should_be_unique_for_clients_with_different_port() { let client_1_socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080); let client_2_with_different_socket_port = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081); - + assert_ne!(ClientId::from_socket_address(&client_1_socket_addr), ClientId::from_socket_address(&client_2_with_different_socket_port)); } #[test] fn ipv4_address_should_be_converted_to_a_byte_vector() { let ip_address = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); - let bytes = convert_ip_into_bytes(ip_address); + let bytes = ip_address.to_bytes_vec(); assert_eq!(bytes, vec![127, 0, 0, 1]); // 4 bytes } @@ -118,8 +93,8 @@ mod tests { #[test] fn ipv6_address_should_be_converted_to_a_byte_vector() { let ip_address = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); - let bytes = convert_ip_into_bytes(ip_address); + let bytes = ip_address.to_bytes_vec(); assert_eq!(bytes, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); // 16 bytes - } -} \ No newline at end of file + } +} diff --git a/src/udp/connection/connection_id_data.rs b/src/udp/connection/connection_id_data.rs index 7164e0cad..63ff439b5 100644 --- a/src/udp/connection/connection_id_data.rs +++ b/src/udp/connection/connection_id_data.rs @@ -1,92 +1,77 @@ -use super::{client_id::ClientId, timestamp_32::Timestamp32}; +use crate::udp::connection::client_id::ClientId; +use crate::udp::connection::timestamp_32::Timestamp32; /// The data stored inside the connection id #[derive(PartialEq, Debug, Copy, Clone)] -pub struct ConnectionIdData { - pub client_id: ClientId, - pub expiration_timestamp: Timestamp32 -} +pub struct ConnectionIdData(pub [u8; 8]); impl ConnectionIdData { - pub fn from_bytes(bytes: &[u8; 8]) -> Self { - let client_id = Self::extract_client_id(bytes); - let expiration_timestamp = Self::extract_timestamp(bytes); - Self { - client_id, - expiration_timestamp - } + pub fn from_bytes(bytes: &[u8]) -> Self { + let mut sized_bytes_arr = [0u8; 8]; + + sized_bytes_arr.copy_from_slice(&bytes[..8]); + + Self(sized_bytes_arr) + } + + pub fn from_client_id_and_timestamp(client_id: ClientId, timestamp: Timestamp32) -> Self { + let bytes_vec = [client_id.value, timestamp.0.to_le_bytes()].concat(); + + Self::from_bytes(&bytes_vec) + } + + pub fn as_bytes(&self) -> &[u8; 8] { + &self.0 + } + + pub fn client_id(&self) -> &[u8] { + &self.0[..4] } - pub fn to_bytes(&self) -> [u8; 8] { - let connection_id: Vec = [ - self.client_id.to_bytes().as_slice(), - self.expiration_timestamp.to_le_bytes().as_slice(), - ].concat(); - - let connection_as_array: [u8; 8] = connection_id.try_into().unwrap(); - - connection_as_array + pub fn timestamp(&self) -> u32 { + u32::from_le_bytes([self.0[4], self.0[5], self.0[6], self.0[7]]) } - fn extract_timestamp(decrypted_connection_id: &[u8; 8]) -> Timestamp32 { - let timestamp_bytes = &decrypted_connection_id[4..]; - let timestamp = Timestamp32::from_le_bytes(timestamp_bytes); - timestamp + pub fn timestamp_as_bytes(&self) -> &[u8] { + &self.0[4..] } - - fn extract_client_id(decrypted_connection_id: &[u8; 8]) -> ClientId { - ClientId::from_slice(&decrypted_connection_id[..4]) - } } #[cfg(test)] mod tests { - use crate::udp::connection::{connection_id_data::ConnectionIdData, client_id::ClientId}; - + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + use crate::protocol::clock::current_timestamp; + use crate::udp::connection::{connection_id_data::ConnectionIdData}; + use crate::udp::connection::client_id::ClientId; + use crate::udp::connection::timestamp_32::Timestamp32; #[test] - fn it_contains_a_client_id() { + fn it_should_be_instantiated_from_a_client_id_and_timestamp32() { + let client_id = ClientId::from_socket_address(&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081)); - let connection_id = ConnectionIdData { - client_id: ClientId::from_slice(&[0u8; 4]), - expiration_timestamp: 0u32.into(), - }; + let expiration_timestamp: Timestamp32 = (current_timestamp() + 120).into(); - assert_eq!(connection_id.client_id, ClientId::from_slice(&[0u8; 4])); - } - - #[test] - fn it_contains_an_expiration_timestamp() { + let connection_id = ConnectionIdData::from_client_id_and_timestamp(client_id, expiration_timestamp); - let connection_id = ConnectionIdData { - client_id: ClientId::from_slice(&[0u8; 4]), - expiration_timestamp: 0u32.into(), - }; - - assert_eq!(connection_id.expiration_timestamp, 0u32.into()); + assert_eq!(connection_id.client_id(), client_id.value.as_slice()); + assert_eq!(connection_id.timestamp(), expiration_timestamp.0) } #[test] - fn it_should_be_converted_to_a_byte_array() { + fn it_should_be_instantiated_from_a_byte_array() { + let bytes = [0, 0, 0, 0, 255, 255, 255, 255]; - let connection_id = ConnectionIdData { - client_id: ClientId::from_slice(&[0u8; 4]), - expiration_timestamp: (u32::MAX).into(), - }; + let connection_id = ConnectionIdData::from_bytes(&bytes); - assert_eq!(connection_id.to_bytes(), [0, 0, 0, 0, 255, 255, 255, 255]); + assert_eq!(connection_id.as_bytes(), &bytes); } #[test] - fn it_should_be_instantiated_from_a_byte_array() { - - let connection_id = ConnectionIdData::from_bytes(&[0, 0, 0, 0, 255, 255, 255, 255]); + fn it_should_have_a_timestamp_that_equals_u32max() { + let bytes = [0, 0, 0, 0, 255, 255, 255, 255]; - let expected_connection_id = ConnectionIdData { - client_id: ClientId::from_slice(&[0, 0, 0, 0]), - expiration_timestamp: (u32::MAX).into(), - }; + let connection_id = ConnectionIdData::from_bytes(&bytes); - assert_eq!(connection_id, expected_connection_id); + assert_eq!(connection_id.timestamp(), u32::MAX); } } diff --git a/src/udp/connection/connection_id_issuer.rs b/src/udp/connection/connection_id_issuer.rs index d1cf8501a..dd37f71d0 100644 --- a/src/udp/connection/connection_id_issuer.rs +++ b/src/udp/connection/connection_id_issuer.rs @@ -8,7 +8,7 @@ pub trait ConnectionIdIssuer { type Error; fn new_connection_id(&self, remote_address: &SocketAddr, current_timestamp: Timestamp64) -> ConnectionId; - + fn verify_connection_id(&self, connection_id: ConnectionId, remote_address: &SocketAddr, current_timestamp: Timestamp64) -> Result<(), Self::Error>; } @@ -22,7 +22,7 @@ impl ConnectionIdIssuer for EncryptedConnectionIdIssuer { fn new_connection_id(&self, remote_address: &SocketAddr, current_timestamp: Timestamp64) -> ConnectionId { - let connection_id_data = self.generate_connection_id_data(&remote_address, current_timestamp); + let connection_id_data = self.generate_connection_id_data(remote_address, current_timestamp); let encrypted_connection_id_data = self.encrypt_connection_id_data(&connection_id_data); @@ -35,7 +35,7 @@ impl ConnectionIdIssuer for EncryptedConnectionIdIssuer { let connection_id_data = self.decrypt_connection_id_data(&encrypted_connection_id_data); - self.guard_that_current_client_id_matches_client_id_in_connection_id(&connection_id_data, &remote_address)?; + self.guard_that_current_client_id_matches_client_id_in_connection_id(&connection_id_data, remote_address)?; self.guard_that_connection_id_has_not_expired(&connection_id_data, current_timestamp)?; @@ -54,59 +54,50 @@ impl EncryptedConnectionIdIssuer { fn generate_connection_id_data(&self, remote_address: &SocketAddr, current_timestamp: Timestamp64) -> ConnectionIdData { let client_id = ClientId::from_socket_address(remote_address); - let expiration_timestamp: Timestamp32 = (current_timestamp + 120).try_into().unwrap(); - - let connection_id_data = ConnectionIdData { - client_id, - expiration_timestamp - }; + let expiration_timestamp: Timestamp32 = (current_timestamp + 120).into(); - connection_id_data + ConnectionIdData::from_client_id_and_timestamp(client_id, expiration_timestamp) } fn unpack_encrypted_connection_id_data(&self, connection_id: ConnectionId) -> EncryptedConnectionIdData { let encrypted_raw_data: EncryptedConnectionIdData = connection_id.0.into(); encrypted_raw_data - } + } fn decrypt_connection_id_data(&self, encrypted_connection_id_data: &EncryptedConnectionIdData) -> ConnectionIdData { let decrypted_raw_data = self.cypher.decrypt(&encrypted_connection_id_data.bytes); - let connection_id_data = ConnectionIdData::from_bytes(&decrypted_raw_data); - - connection_id_data + ConnectionIdData::from_bytes(&decrypted_raw_data) } fn guard_that_current_client_id_matches_client_id_in_connection_id(&self, connection_id_data: &ConnectionIdData, remote_address: &SocketAddr) -> Result<(), &'static str> { let current_client_id = ClientId::from_socket_address(remote_address); - if connection_id_data.client_id != current_client_id { + if connection_id_data.client_id() != current_client_id.value.as_slice() { return Err("Invalid client id: current client id does not match client in connection id"); } Ok(()) } fn guard_that_connection_id_has_not_expired(&self, connection_id_data: &ConnectionIdData, current_timestamp:Timestamp64) -> Result<(), &'static str> { - if current_timestamp > connection_id_data.expiration_timestamp.into() { + if current_timestamp > connection_id_data.timestamp() as Timestamp64 { return Err("Expired connection id") } Ok(()) } fn encrypt_connection_id_data(&self, connection_id_data: &ConnectionIdData) -> EncryptedConnectionIdData { - let decrypted_raw_data = connection_id_data.to_bytes(); - - let encrypted_raw_data = self.cypher.encrypt(&decrypted_raw_data); + let decrypted_raw_data = connection_id_data.as_bytes(); - let encrypted_connection_id_data = EncryptedConnectionIdData::from_encrypted_bytes(&encrypted_raw_data); + let encrypted_raw_data = self.cypher.encrypt(decrypted_raw_data); - encrypted_connection_id_data + EncryptedConnectionIdData::from_encrypted_bytes(encrypted_raw_data) } } #[cfg(test)] mod tests { use crate::udp::connection::{secret::Secret, connection_id_issuer::{EncryptedConnectionIdIssuer, ConnectionIdIssuer}}; - + use std::{net::{SocketAddr, IpAddr, Ipv4Addr}}; fn cypher_secret_for_testing() -> Secret { @@ -114,8 +105,7 @@ mod tests { } fn new_issuer() -> EncryptedConnectionIdIssuer { - let issuer = EncryptedConnectionIdIssuer::new(cypher_secret_for_testing()); - issuer + EncryptedConnectionIdIssuer::new(cypher_secret_for_testing()) } #[test] @@ -146,7 +136,7 @@ mod tests { let after_more_than_two_minutes = now + (2*60) + 1; assert_eq!(issuer.verify_connection_id(connection_id, &client_addr, after_more_than_two_minutes), Err("Expired connection id")); - } + } #[test] fn it_should_change_for_the_same_client_ip_and_port_after_two_minutes() { @@ -167,8 +157,8 @@ mod tests { #[test] fn it_should_be_different_for_each_client_at_the_same_time_if_they_use_a_different_ip() { - let client_1_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 0001); - let client_2_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0001); + let client_1_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 1); + let client_2_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 1); let now = 946684800u64; @@ -182,8 +172,8 @@ mod tests { #[test] fn it_should_be_different_for_each_client_at_the_same_time_if_they_use_a_different_port() { - let client_1_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0001); - let client_2_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0002); + let client_1_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 1); + let client_2_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 2); let now = 946684800u64; @@ -200,14 +190,14 @@ mod tests { let issuer = new_issuer(); // Generate connection id for a given client - let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0001); + let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 1); let now = 946684800u64; let connection_id = issuer.new_connection_id(&client_addr, now); // Verify the connection id with a different client address - let different_client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 0002); + let different_client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 2); let result = issuer.verify_connection_id(connection_id, &different_client_addr, now); assert!(result.is_err()); - } -} \ No newline at end of file + } +} diff --git a/src/udp/connection/cypher.rs b/src/udp/connection/cypher.rs index 17d65c14d..f83175dc3 100644 --- a/src/udp/connection/cypher.rs +++ b/src/udp/connection/cypher.rs @@ -14,7 +14,7 @@ pub struct BlowfishCypher { impl BlowfishCypher { pub fn new(secret: Secret) -> Self { - let blowfish = Blowfish::new(&secret.to_bytes()); + let blowfish = Blowfish::new(&secret.into_bytes()); BlowfishCypher { blowfish } @@ -58,4 +58,4 @@ mod tests { assert_eq!(decrypted_text, text); } -} \ No newline at end of file +} diff --git a/src/udp/connection/encrypted_connection_id_data.rs b/src/udp/connection/encrypted_connection_id_data.rs index f32702883..290c4dbb5 100644 --- a/src/udp/connection/encrypted_connection_id_data.rs +++ b/src/udp/connection/encrypted_connection_id_data.rs @@ -4,8 +4,8 @@ pub struct EncryptedConnectionIdData { } impl EncryptedConnectionIdData { - pub fn from_encrypted_bytes(encrypted_bytes: &[u8; 8]) -> Self { - Self { bytes: encrypted_bytes.clone() } + pub fn from_encrypted_bytes(encrypted_bytes: [u8; 8]) -> Self { + Self { bytes: encrypted_bytes } } } @@ -29,7 +29,7 @@ mod tests { #[test] fn it_should_be_generated_from_the_encrypted_connection_id_data() { - let encrypted_data = EncryptedConnectionIdData::from_encrypted_bytes(&[0u8; 8]); + let encrypted_data = EncryptedConnectionIdData::from_encrypted_bytes([0u8; 8]); assert_eq!(encrypted_data, EncryptedConnectionIdData { bytes: [0u8; 8]}); } @@ -37,7 +37,7 @@ mod tests { #[test] fn it_should_be_converted_into_a_i64() { - let encrypted_data: i64 = EncryptedConnectionIdData::from_encrypted_bytes(&[0u8; 8]).into(); + let encrypted_data: i64 = EncryptedConnectionIdData::from_encrypted_bytes([0u8; 8]).into(); assert_eq!(encrypted_data, 0i64); } @@ -49,4 +49,5 @@ mod tests { assert_eq!(encrypted_data, EncryptedConnectionIdData { bytes: [0u8; 8]}); } -} \ No newline at end of file +} + diff --git a/src/udp/connection/secret.rs b/src/udp/connection/secret.rs index 4b7d0482f..cea3f2fc9 100644 --- a/src/udp/connection/secret.rs +++ b/src/udp/connection/secret.rs @@ -4,12 +4,11 @@ pub struct Secret([u8; 32]); impl Secret { - pub fn new(bytes: [u8; 32]) -> Self { Secret(bytes) } - pub fn to_bytes(self) -> [u8; 32] { + pub fn into_bytes(self) -> [u8; 32] { self.0 } } @@ -23,6 +22,6 @@ mod tests { let byte_array_32 = Secret::new([0; 32]); - assert_eq!(byte_array_32.to_bytes(), [0u8; 32]); + assert_eq!(byte_array_32.into_bytes(), [0u8; 32]); } -} \ No newline at end of file +} diff --git a/src/udp/connection/timestamp_32.rs b/src/udp/connection/timestamp_32.rs index 939330ff1..77c9a9002 100644 --- a/src/udp/connection/timestamp_32.rs +++ b/src/udp/connection/timestamp_32.rs @@ -1,52 +1,36 @@ //! A UNIX 32-bit timestamp. -use std::num::TryFromIntError; - use super::timestamp_64::Timestamp64; #[derive(PartialEq, Debug, Copy, Clone)] -pub struct Timestamp32 { - pub value: u32 -} +pub struct Timestamp32(pub u32); impl Timestamp32 { pub fn from_le_bytes(timestamp_bytes: &[u8]) -> Self { - // Little Endian let timestamp = u32::from_le_bytes([timestamp_bytes[0], timestamp_bytes[1], timestamp_bytes[2], timestamp_bytes[3]]); - Self { - value: timestamp - } - } - - pub fn to_le_bytes(self: Self) -> [u8; 4] { - // Little Endian - let mut bytes: [u8; 4] = [0u8; 4]; - bytes.copy_from_slice(&self.value.to_le_bytes()[..4]); - bytes + Self(timestamp) } } impl From for Timestamp32 { fn from(value: u32) -> Self { - Self { value } + Self(value) } } -impl TryFrom for Timestamp32 { - type Error = TryFromIntError; +impl From for Timestamp32 { + fn from(value: Timestamp64) -> Self { + let mut bytes_array = [0u8; 4]; - fn try_from(value: Timestamp64) -> Result { - let timestamp32: u32 = u32::try_from(value)?; + bytes_array.copy_from_slice(&value.to_le_bytes()[..4]); - Ok(Self { - value: timestamp32 - }) + Self(u32::from_le_bytes(bytes_array)) } } impl Into for Timestamp32 { fn into(self) -> Timestamp64 { - u64::from(self.value) + u64::from(self.0) } } @@ -59,41 +43,49 @@ mod tests { let min_timestamp = Timestamp32::from_le_bytes(&[0u8, 0u8, 0u8, 0u8]); - assert_eq!(min_timestamp, Timestamp32 { value: u32::MIN }); + assert_eq!(min_timestamp, Timestamp32(u32::MIN)); let max_timestamp = Timestamp32::from_le_bytes(&[255u8, 255u8, 255u8, 255u8]); - assert_eq!(max_timestamp, Timestamp32 { value: u32::MAX }); + assert_eq!(max_timestamp, Timestamp32(u32::MAX)); } #[test] fn it_should_be_converted_to_a_four_byte_array_in_little_indian() { - let min_timestamp = Timestamp32 { value: u32::MIN }; + let min_timestamp = Timestamp32(u32::MIN); - assert_eq!(min_timestamp.to_le_bytes(), [0u8, 0u8, 0u8, 0u8]); + assert_eq!(min_timestamp.0.to_le_bytes(), [0u8, 0u8, 0u8, 0u8]); - let max_timestamp = Timestamp32 { value: u32::MAX }; + let max_timestamp = Timestamp32(u32::MAX); - assert_eq!(max_timestamp.to_le_bytes(), [255u8, 255u8, 255u8, 255u8]); + assert_eq!(max_timestamp.0.to_le_bytes(), [255u8, 255u8, 255u8, 255u8]); } #[test] fn it_should_be_converted_from_a_64_bit_unix_timestamp() { - let timestamp32: Timestamp32 = 0u64.try_into().unwrap(); + let timestamp32: Timestamp32 = 0u64.into(); - assert_eq!(timestamp32, Timestamp32 { value: u32::MIN }); + assert_eq!(timestamp32, Timestamp32(u32::MIN)); } #[test] - fn it_should_fail_trying_to_convert_it_from_a_64_bit_unix_timestamp_which_overflows_u32_range() { + fn it_should_be_converted_from_a_64_bit_unix_timestamp_with_u32_max() { - let out_of_range_value = (u32::MAX as u64) + 1; + let timestamp64 = u32::MAX as u64; + let timestamp32: Timestamp32 = Timestamp32::from(timestamp64); - let timestamp32: Result = out_of_range_value.try_into(); + assert_eq!(timestamp32, Timestamp32(u32::MAX)); + } - assert_eq!(timestamp32.is_err(), true); + #[test] + fn it_should_be_converted_from_a_64_bit_unix_timestamp_with_u64_max() { + + let timestamp64 = u64::MAX; + let timestamp32: Timestamp32 = Timestamp32::from(timestamp64); + + assert_eq!(timestamp32, Timestamp32(u32::MAX)); } #[test] @@ -101,23 +93,23 @@ mod tests { let timestamp32: Timestamp32 = u32::MIN.into(); - assert_eq!(timestamp32, Timestamp32 { value: u32::MIN }); + assert_eq!(timestamp32, Timestamp32(u32::MIN)); } #[test] fn it_should_be_converted_to_a_timestamp_64() { - let min_timestamp_32 = Timestamp32 { value: u32::MIN }; + let min_timestamp_32 = Timestamp32(u32::MIN); let min_timestamp_64: Timestamp64 = min_timestamp_32.into(); assert_eq!(min_timestamp_64, u32::MIN as u64); - let max_timestamp_32 = Timestamp32 { value: u32::MAX }; + let max_timestamp_32 = Timestamp32(u32::MAX); let max_timestamp_64: Timestamp64 = max_timestamp_32.into(); assert_eq!(max_timestamp_64, u32::MAX as u64); } -} \ No newline at end of file +} diff --git a/src/udp/connection/timestamp_64.rs b/src/udp/connection/timestamp_64.rs index ab0092aaa..50d0ac350 100644 --- a/src/udp/connection/timestamp_64.rs +++ b/src/udp/connection/timestamp_64.rs @@ -1,9 +1,3 @@ /// Connection id contains a timestamp in 4 bytes due to its 64 bits limit. pub type Timestamp64 = u64; -pub fn timestamp_from_le_bytes(timestamp_bytes: &[u8]) -> Timestamp64 { - // Little Endian - let timestamp = u64::from_le_bytes([timestamp_bytes[0], timestamp_bytes[1], timestamp_bytes[2], timestamp_bytes[3], 0, 0, 0, 0]); - timestamp -} -