/// Helper for calculating the sum of all 16 bit words checksums used in /// in checksum fields in TCP and UDP headers. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Sum16BitWords { /// Partial sum #[cfg(target_pointer_width = "64")] sum: u64, /// Partial sum #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] sum: u32, } impl Sum16BitWords { pub fn new() -> Sum16BitWords { Sum16BitWords { sum: 0 } } /// Add the given slice to the checksum. In case the slice /// has a length that is not multiple of 2 the last byte /// will be padded with 0. #[inline] #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] pub fn add_slice(self, slice: &[u8]) -> Sum16BitWords { Sum16BitWords { sum: u32_16bit_word::add_slice(self.sum, slice), } } /// Add the given slice to the checksum. In case the slice /// has a length that is not multiple of 2 the last byte /// will be padded with 0. #[inline] #[cfg(target_pointer_width = "64")] pub fn add_slice(self, slice: &[u8]) -> Sum16BitWords { Sum16BitWords { sum: u64_16bit_word::add_slice(self.sum, slice), } } /// Add a 2 byte word. #[inline] #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] pub fn add_2bytes(self, value: [u8; 2]) -> Sum16BitWords { Sum16BitWords { sum: u32_16bit_word::add_2bytes(self.sum, value), } } /// Add a 2 byte word. #[inline] #[cfg(target_pointer_width = "64")] pub fn add_2bytes(self, value: [u8; 2]) -> Sum16BitWords { Sum16BitWords { sum: u64_16bit_word::add_2bytes(self.sum, value), } } /// Add a 4 byte word. #[inline] #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] pub fn add_4bytes(&mut self, value: [u8; 4]) -> Sum16BitWords { Sum16BitWords { sum: u32_16bit_word::add_4bytes(self.sum, value), } } /// Add a 4 byte word. #[inline] #[cfg(target_pointer_width = "64")] pub fn add_4bytes(&mut self, value: [u8; 4]) -> Sum16BitWords { Sum16BitWords { sum: u64_16bit_word::add_4bytes(self.sum, value), } } /// Add a 8 byte word. #[inline] #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] pub fn add_8bytes(&mut self, value: [u8; 8]) -> Sum16BitWords { self.add_4bytes([value[0], value[1], value[2], value[3]]) .add_4bytes([value[4], value[5], value[6], value[7]]) } /// Add a 8 byte word. #[inline] #[cfg(target_pointer_width = "64")] pub fn add_8bytes(&mut self, value: [u8; 8]) -> Sum16BitWords { Sum16BitWords { sum: u64_16bit_word::add_8bytes(self.sum, value), } } /// Add a 16 bytes. #[inline] pub fn add_16bytes(&mut self, value: [u8; 16]) -> Sum16BitWords { self.add_8bytes([ value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7], ]) .add_8bytes([ value[8], value[9], value[10], value[11], value[12], value[13], value[14], value[15], ]) } /// Converts summed up words from an u32 to an u16 ones complement /// which can be used in a ipv4 checksum. #[inline] #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] pub fn ones_complement(&self) -> u16 { u32_16bit_word::ones_complement(self.sum) } /// Converts summed up words from an u32 to an u16 ones complement /// which can be used in a ipv4 checksum. #[inline] #[cfg(target_pointer_width = "64")] pub fn ones_complement(&self) -> u16 { u64_16bit_word::ones_complement(self.sum) } /// Converts summed up words from an u32 to an u16 ones complement /// with 0 being replaced by 0xffff (useful for TCP and UDP). /// /// This kind of checksum is used in TCP and UDP headers. #[inline] #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] pub fn to_ones_complement_with_no_zero(&self) -> u16 { u32_16bit_word::ones_complement_with_no_zero(self.sum) } /// Converts summed up words from an u32 to an u16 ones complement /// with 0 being replaced by 0xffff (useful for TCP and UDP). /// /// This kind of checksum is used in TCP and UDP headers. #[inline] #[cfg(target_pointer_width = "64")] pub fn to_ones_complement_with_no_zero(&self) -> u16 { u64_16bit_word::ones_complement_with_no_zero(self.sum) } } #[cfg(test)] mod sum16_bit_words_tests { use super::*; use alloc::format; #[test] fn new() { assert_eq!(0xffff, Sum16BitWords::new().ones_complement()); } #[test] fn add_slice() { assert_eq!( !u16::from_ne_bytes([0x12, 0x34]), Sum16BitWords::new() .add_slice(&[0x12, 0x34]) .ones_complement() ); } #[test] fn add_2bytes() { assert_eq!( !u16::from_ne_bytes([0xf0, 0x0f]), Sum16BitWords::new() .add_2bytes([0xf0, 0x0f]) .ones_complement() ); } #[test] fn add_4bytes() { assert_eq!( !(u16::from_ne_bytes([0x12, 0x34]) + u16::from_ne_bytes([0x56, 0x78])), Sum16BitWords::new() .add_4bytes([0x12, 0x34, 0x56, 0x78]) .ones_complement() ); } #[test] fn add_8bytes() { assert_eq!( !(u16::from_ne_bytes([0x12, 0x34]) + u16::from_ne_bytes([0x56, 0x78]) + u16::from_ne_bytes([0x23, 0x22]) + u16::from_ne_bytes([0x34, 0x11])), Sum16BitWords::new() .add_8bytes([0x12, 0x34, 0x56, 0x78, 0x23, 0x22, 0x34, 0x11]) .ones_complement() ); } #[test] fn add_16bytes() { assert_eq!( u32_16bit_word::ones_complement(u32_16bit_word::add_4bytes( u32_16bit_word::add_4bytes( u32_16bit_word::add_4bytes( u32_16bit_word::add_4bytes(0, [0x12, 0x34, 0x56, 0x78]), [0x9a, 0xbc, 0xde, 0xf0] ), [0x0f, 0xed, 0xcb, 0xa9] ), [0x87, 0x65, 0x43, 0x21] )), Sum16BitWords::new() .add_16bytes([ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21, ]) .ones_complement() ); } #[test] fn ones_complement() { assert_eq!( !u16::from_ne_bytes([0xf0, 0x0f]), Sum16BitWords::new() .add_2bytes([0xf0, 0x0f]) .ones_complement() ); } #[test] fn to_ones_complement_with_no_zero() { // normal case assert_eq!( !u16::from_ne_bytes([0xf0, 0x0f]), Sum16BitWords::new() .add_2bytes([0xf0, 0x0f]) .to_ones_complement_with_no_zero() ); // zero case assert_eq!( 0xffffu16, Sum16BitWords::new() // ones complement would result in 0 // will be converted to 0xffff as 0 // is a reserved value .add_2bytes([0xff, 0xff]) .to_ones_complement_with_no_zero() ); } #[test] fn debug() { let input = Sum16BitWords::new(); assert_eq!( &format!("Sum16BitWords {{ sum: {} }}", input.sum), &format!("{:?}", input) ); } #[test] fn default() { let d: Sum16BitWords = Default::default(); assert_eq!(d.sum, 0); } #[test] fn clone_eq() { let value = Sum16BitWords::new(); assert_eq!(value.clone(), value) } } /// Helper functions for calculating a 16 bit checksum using /// a u32 to sum up all values. pub mod u32_16bit_word { /// Add a 4 byte word. #[inline] pub fn add_4bytes(start: u32, value: [u8; 4]) -> u32 { let (sum, carry) = start.overflowing_add(u32::from_ne_bytes(value)); sum + (carry as u32) } /// Add a 2 byte word. #[inline] pub fn add_2bytes(start: u32, value: [u8; 2]) -> u32 { let (sum, carry) = start.overflowing_add(u32::from(u16::from_ne_bytes(value))); sum + (carry as u32) } /// Add the given slice to the checksum. In case the slice /// has a length that is not multiple of 2 the last byte /// will be padded with 0. #[inline] pub fn add_slice(start_sum: u32, slice: &[u8]) -> u32 { let mut sum: u32 = start_sum; // sum up all 4 byte values let end_32 = slice.len() - (slice.len() % 4); for i in (0..end_32).step_by(4) { sum = add_4bytes( sum, // SAFETY: // Guranteed to always have at least 4 bytes to read // from i. As end_32 is gurenateed to be a multiple of // 4 bytes with a size equal or less then slice.len(). unsafe { [ *slice.get_unchecked(i), *slice.get_unchecked(i + 1), *slice.get_unchecked(i + 2), *slice.get_unchecked(i + 3), ] }, ); } // in case 2 bytes are left add them as an word if slice.len() - end_32 >= 2 { sum = add_2bytes( sum, // SAFETY: // If check guarantees there to be at least // 2 bytes. unsafe { [ *slice.get_unchecked(end_32), *slice.get_unchecked(end_32 + 1), ] }, ); } // unaligned end pad the last byte with if 0 != slice.len() % 2 { sum = add_2bytes( sum, // SAFETY: // If check guarantees there to be at least // 2 bytes. unsafe { [*slice.get_unchecked(slice.len() - 1), 0] }, ); } // done sum } /// Converts summed up words from an u32 to an u16 with 0 being replaced by 0xffff (useful /// for TCP and UDP headers). /// /// This kind of checksum is used in TCP and udp headers. #[inline] pub fn ones_complement_with_no_zero(sum: u32) -> u16 { // In case of 0 use the ones complement (zero is reserved // value for no checksum). let u16value = ones_complement(sum); if u16value == 0 { 0xffff } else { u16value } } /// Converts summed up words from an u32 to an u16 which can be used in a ipv4. #[inline] pub fn ones_complement(sum: u32) -> u16 { // Add the upper 16 bits to the lower 16 bits twice. // // Notes: Two carry adds are needed as the first one could // result in an additional carry add. let first = ((sum >> 16) & 0xffff) + (sum & 0xffff); let u16value = (((first >> 16) & 0xffff) + (first & 0xffff)) as u16; // switch back to big endian (allows to use // native endinaess during calculations). !u16value } #[cfg(test)] mod tests { use super::*; #[test] fn add_4bytes_test() { // trivial case assert_eq!(0, add_4bytes(0, [0, 0, 0, 0])); // check that the carry gets added assert_eq!( 0xffff_ffff, // normal overflow would result in 0xffff_fffe add_4bytes(0xffff_ffff, [0xff, 0xff, 0xff, 0xff]) ); // non max & min values assert_eq!( 0x1234_5678 + u32::from_ne_bytes([0x23, 0x45, 0x67, 0x89]), add_4bytes(0x1234_5678, [0x23, 0x45, 0x67, 0x89]) ); } #[test] fn add_2bytes_test() { // trivial case assert_eq!(0, add_2bytes(0, [0, 0])); // check that the carry gets added assert_eq!( 0x0000_ffff, // normal overflow would result in 0x10000fffe add_2bytes(0xffff_ffff, [0xff, 0xff]) ); // non max & min values assert_eq!( 0x1234_5678 + u32::from(u16::from_ne_bytes([0x23, 0x45])), add_2bytes(0x1234_5678, [0x23, 0x45]) ); } #[test] fn add_slice_test() { // empty assert_eq!(0x1234, add_slice(0x1234, &[])); // aligned assert_eq!( 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) + u32::from_ne_bytes([0x1d, 0x1e, 0x1f, 0x10]), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, ] ) ); // aligned with carry assert_eq!( 0x1 + 0x3 + // expected carry u32::from_ne_bytes([0xf1, 0x11, 0x10, 0xf0]).wrapping_add( u32::from_ne_bytes([0xf2, 0x12, 0x11, 0xf1]).wrapping_add( u32::from_ne_bytes([0xf3, 0x13, 0x12, 0xf2]).wrapping_add( u32::from_ne_bytes([0xf4, 0x14, 0x13, 0xf3]) ) ) ), add_slice( 0x1, &[ 0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1, 0xf3, 0x13, 0x12, 0xf2, 0xf4, 0x14, 0x13, 0xf3, ] ) ); // 1 byte unalgined assert_eq!( 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) + u32::from(u16::from_ne_bytes([0x1d, 0x1e])) + u32::from(u16::from_ne_bytes([0x1f, 0x00])), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ] ) ); // 2 byte unaligned assert_eq!( 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) + u32::from(u16::from_ne_bytes([0x1d, 0x1e])), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, ] ) ); // 4 byte unaligned assert_eq!( 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) + u32::from(u16::from_ne_bytes([0x1d, 0x00])), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, ] ) ); } #[test] fn ones_complement_with_no_zero_test() { // zero case assert_eq!(0xffff, ones_complement_with_no_zero(0)); // 0xffff should stay 0xffff (0 is reserved for no checksum) assert_eq!(0xffff, ones_complement_with_no_zero(0xffff)); // big endian conversion check assert_eq!(!0x1234u16, ones_complement_with_no_zero(0x1234),); // add of the upper and lower 16 bits without a carry assert_eq!( !(0x2345u16 + 0x1234), ones_complement_with_no_zero(0x2345_1234), ); // add which in itself will again produce a carry assert_eq!( !(((0x1456u32 + 0xf123u32 + 1u32) & 0xffff) as u16), ones_complement_with_no_zero(0x1456_f123), ); } #[test] fn ones_complement_test() { // zero case assert_eq!(0xffff, ones_complement(0)); // check that zero is not reserved assert_eq!(0, ones_complement(0xffff)); // big endian conversion check assert_eq!(!0x1234u16, ones_complement(0x1234),); // add of the upper and lower 16 bits without a carry assert_eq!(!(0x2345u16 + 0x1234u16), ones_complement(0x2345_1234),); // add which in itself will again produce a carry assert_eq!( !(((0x1456u32 + 0xf123u32 + 1u32) & 0xffff) as u16), ones_complement(0x1456_f123), ); } } } /// Helper functions for calculating a 16 bit checksum using /// a u64 to sum up all values. pub mod u64_16bit_word { /// Add a 8 byte word. #[inline] pub fn add_8bytes(start: u64, value: [u8; 8]) -> u64 { let (sum, carry) = start.overflowing_add(u64::from_ne_bytes(value)); sum + (carry as u64) } /// Add a 4 byte word. #[inline] pub fn add_4bytes(start: u64, value: [u8; 4]) -> u64 { let (sum, carry) = start.overflowing_add(u64::from(u32::from_ne_bytes(value))); sum + (carry as u64) } /// Add a 2 byte word. #[inline] pub fn add_2bytes(start: u64, value: [u8; 2]) -> u64 { let (sum, carry) = start.overflowing_add(u64::from(u16::from_ne_bytes(value))); sum + (carry as u64) } /// Add the given slice to the checksum. In case the slice /// has a length that is not multiple of 2 the last byte /// will be padded with 0. #[inline] pub fn add_slice(start_sum: u64, slice: &[u8]) -> u64 { let mut sum: u64 = start_sum; // sum up all 4 byte values let end_64 = slice.len() - (slice.len() % 8); for i in (0..end_64).step_by(8) { sum = add_8bytes( sum, // SAFETY: // Guranteed to always have at least 8 bytes to read // from i. As end_64 is gurenateed to be a multiple of // 8 bytes with a size equal or less then slice.len(). unsafe { [ *slice.get_unchecked(i), *slice.get_unchecked(i + 1), *slice.get_unchecked(i + 2), *slice.get_unchecked(i + 3), *slice.get_unchecked(i + 4), *slice.get_unchecked(i + 5), *slice.get_unchecked(i + 6), *slice.get_unchecked(i + 7), ] }, ); } // in case 4 or more bytes are left add the first 4 bytes let end_32 = if slice.len() - end_64 >= 4 { sum = add_4bytes( sum, // SAFETY: // If check guarantees there to be at least // 2 bytes. unsafe { [ *slice.get_unchecked(end_64), *slice.get_unchecked(end_64 + 1), *slice.get_unchecked(end_64 + 2), *slice.get_unchecked(end_64 + 3), ] }, ); // shift by 4 end_64 + 4 } else { end_64 }; // in case 2 bytes are left add them as an word if slice.len() - end_32 >= 2 { sum = add_2bytes( sum, // SAFETY: // If check guarantees there to be at least // 2 bytes. unsafe { [ *slice.get_unchecked(end_32), *slice.get_unchecked(end_32 + 1), ] }, ); } // unaligned end pad the last byte with if 0 != slice.len() % 2 { sum = add_2bytes( sum, // SAFETY: // If check guarantees there to be at least // 2 bytes. unsafe { [*slice.get_unchecked(slice.len() - 1), 0] }, ); } // done sum } /// Converts summed up words from an u64 to an u16 with 0 being replaced by 0xffff (useful /// for TCP and UDP headers). /// /// This kind of checksum is used in TCP and udp headers. #[inline] pub fn ones_complement_with_no_zero(sum: u64) -> u16 { // In case of 0 use the ones complement (zero is reserved // value for no checksum). let u16value = ones_complement(sum); if u16value == 0 { 0xffff } else { u16value } } /// Converts summed up words from an u64 to an u16 which can be used in a ipv4. #[inline] pub fn ones_complement(sum: u64) -> u16 { let first = ((sum >> 48) & 0xffff) + ((sum >> 32) & 0xffff) + ((sum >> 16) & 0xffff) + (sum & 0xffff); // Add the upper 16 bits to the lower 16 bits twice. // // Notes: Two carry adds are needed as the first one could // result in an additional carry add. let second = ((first >> 16) & 0xffff) + (first & 0xffff); let u16value = (((second >> 16) & 0xffff) + (second & 0xffff)) as u16; // switch back to big endian (allows to use // native endinaess during calculations). !u16value } #[cfg(test)] mod tests { use super::*; use proptest::prelude::*; #[test] fn add_8bytes_test() { // trivial case assert_eq!(0, add_8bytes(0, [0, 0, 0, 0, 0, 0, 0, 0])); // check that the carry gets added assert_eq!( 0xffff_ffff_ffff_ffff, // normal overflow would result in 0xffff_ffff_ffff_fffe add_8bytes( 0xffff_ffff_ffff_ffff, [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] ) ); // non max & min values assert_eq!( 0x1234_5678_1234_5678 + u64::from_ne_bytes([0x23, 0x45, 0x67, 0x89, 0x11, 0x22, 0x33, 0x44]), add_8bytes( 0x1234_5678_1234_5678, [0x23, 0x45, 0x67, 0x89, 0x11, 0x22, 0x33, 0x44] ) ); } #[test] fn add_4bytes_test() { // trivial case assert_eq!(0, add_4bytes(0, [0, 0, 0, 0])); // check that the carry gets added assert_eq!( 0xffff_ffff, // normal overflow would result in 0xffff_fffe add_4bytes(0xffff_ffff_ffff_ffff, [0xff, 0xff, 0xff, 0xff]) ); // non max & min values assert_eq!( 0x1234_5678_1234_5678 + u64::from(u32::from_ne_bytes([0x23, 0x45, 0x67, 0x89])), add_4bytes(0x1234_5678_1234_5678, [0x23, 0x45, 0x67, 0x89]) ); } #[test] fn add_2bytes_test() { // trivial case assert_eq!(0, add_2bytes(0, [0, 0])); // check that the carry gets added assert_eq!( 0xffff, // normal overflow would result in 0xfffe add_2bytes(0xffff_ffff_ffff_ffff, [0xff, 0xff]) ); // non max & min values assert_eq!( 0x9876_0123_1234_5678 + u64::from(u16::from_ne_bytes([0x23, 0x45])), add_2bytes(0x9876_0123_1234_5678, [0x23, 0x45]) ); } #[test] fn add_slice_test() { // empty assert_eq!(0x1234, add_slice(0x1234, &[])); // aligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10]), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, ] ) ); // aligned with carry assert_eq!( 0x1 + 0x1 + // expected carry u64::from_ne_bytes([0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1]).wrapping_add( u64::from_ne_bytes([0xf3, 0x13, 0x12, 0xf2, 0xf4, 0x14, 0x13, 0xf3]) ), add_slice( 0x1, &[ 0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1, 0xf3, 0x13, 0x12, 0xf2, 0xf4, 0x14, 0x13, 0xf3, ] ) ); // unaligned access { let base_data = [ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, ]; // 1 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) + u64::from(u16::from_ne_bytes([0x1d, 0x1e])) + u64::from(u16::from_ne_bytes([0x1f, 0x00])), add_slice(0x1, &base_data[..base_data.len() - 1]) ); // 2 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) + u64::from(u16::from_ne_bytes([0x1d, 0x1e])), add_slice(0x1, &base_data[..base_data.len() - 2]) ); // 3 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) + u64::from(u16::from_ne_bytes([0x1d, 0x00])), add_slice(0x1, &base_data[..base_data.len() - 3]) ); // 4 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])), add_slice(0x1, &base_data[..base_data.len() - 4]) ); // 5 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u16::from_ne_bytes([0x19, 0x1a])) + u64::from(u16::from_ne_bytes([0x1b, 0x00])), add_slice(0x1, &base_data[..base_data.len() - 5]) ); // 6 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u16::from_ne_bytes([0x19, 0x1a])), add_slice(0x1, &base_data[..base_data.len() - 6]) ); // 6 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u16::from_ne_bytes([0x19, 0x00])), add_slice(0x1, &base_data[..base_data.len() - 7]) ); } } #[test] fn ones_complement_with_no_zero_test() { // zero case assert_eq!(0xffff, ones_complement_with_no_zero(0)); // 0xffff should stay 0xffff (0 is reserved for no checksum) assert_eq!(0xffff, ones_complement_with_no_zero(0xffff)); // big endian conversion check assert_eq!(!0x1234u16, ones_complement_with_no_zero(0x1234),); // add of the upper and lower 16 bits without a carry assert_eq!( !(0x2456u16 + 0x1345 + 0x2345u16 + 0x1234u16), ones_complement_with_no_zero(0x2456_1345_2345_1234), ); // add which in itself will again produce two as carry assert_eq!( !(((0x1234 + 0xf234u32 + 0x1456u32 + 0xf123u32 + 2u32) & 0xffff) as u16), ones_complement_with_no_zero(0x1234_f234_1456_f123), ); } #[test] fn ones_complement_test() { // zero case assert_eq!(0xffff, ones_complement(0)); // check that zero is not reserved assert_eq!(0, ones_complement(0xffff)); // big endian conversion check assert_eq!(!0x1234u16, ones_complement(0x1234),); // add of the upper and lower 16 bits without a carry assert_eq!( !(0x2456u16 + 0x1345 + 0x2345u16 + 0x1234u16), ones_complement(0x2456_1345_2345_1234), ); // add which in itself will again produce two as carry assert_eq!( !(((0x1234 + 0xf234u32 + 0x1456u32 + 0xf123u32 + 2u32) & 0xffff) as u16), ones_complement(0x1234_f234_1456_f123), ); // will result in a first 16bit sum that will have to be // carry added twice assert_eq!(!1, ones_complement(0x02f6_e312_7fd7_9a20),); } proptest! { #[test] #[cfg_attr(miri, ignore)] // vec allocation reduces miri runspeed too much fn u32_u16_comparison( data in proptest::collection::vec(any::(), 0..0xfffusize) ) { use crate::checksum::*; let u32_oc = u32_16bit_word::ones_complement( u32_16bit_word::add_slice(0, &data) ); let u64_oc = u64_16bit_word::ones_complement( u64_16bit_word::add_slice(0, &data) ); assert_eq!(u32_oc, u64_oc); let struct_oc = Sum16BitWords::new() .add_slice(&data) .ones_complement(); assert_eq!(u32_oc, struct_oc); } } } }