use std::marker::PhantomData;
pub(crate) struct NumWrapper<T>(PhantomData<T>);
macro_rules! impl_num_wrapper_for_smaller_than_usize {
($int:path) => {
impl NumWrapper<$int> {
pub(crate) fn wrap(count: usize) -> $int {
(count % (<$int>::MAX as usize + 1)) as $int
}
}
};
}
macro_rules! impl_num_wrapper_for_equal_or_larger_than_usize {
($int:path) => {
impl NumWrapper<$int> {
pub(crate) fn wrap(count: usize) -> $int {
count as $int
}
}
};
}
cfg_if::cfg_if! {
if #[cfg(target_pointer_width = "8")] {
impl_num_wrapper_for_equal_or_larger_than_usize!(u8);
impl_num_wrapper_for_equal_or_larger_than_usize!(u16);
impl_num_wrapper_for_equal_or_larger_than_usize!(u32);
impl_num_wrapper_for_equal_or_larger_than_usize!(u64);
impl_num_wrapper_for_equal_or_larger_than_usize!(u128);
} else if #[cfg(target_pointer_width = "16")] {
impl_num_wrapper_for_smaller_than_usize!(u8);
impl_num_wrapper_for_equal_or_larger_than_usize!(u16);
impl_num_wrapper_for_equal_or_larger_than_usize!(u32);
impl_num_wrapper_for_equal_or_larger_than_usize!(u64);
impl_num_wrapper_for_equal_or_larger_than_usize!(u128);
} else if #[cfg(target_pointer_width = "32")] {
impl_num_wrapper_for_smaller_than_usize!(u8);
impl_num_wrapper_for_smaller_than_usize!(u16);
impl_num_wrapper_for_equal_or_larger_than_usize!(u32);
impl_num_wrapper_for_equal_or_larger_than_usize!(u64);
impl_num_wrapper_for_equal_or_larger_than_usize!(u128);
} else if #[cfg(target_pointer_width = "64")] {
impl_num_wrapper_for_smaller_than_usize!(u8);
impl_num_wrapper_for_smaller_than_usize!(u16);
impl_num_wrapper_for_smaller_than_usize!(u32);
impl_num_wrapper_for_equal_or_larger_than_usize!(u64);
impl_num_wrapper_for_equal_or_larger_than_usize!(u128);
} else if #[cfg(target_pointer_width = "128")] {
impl_num_wrapper_for_smaller_than_usize!(u8);
impl_num_wrapper_for_smaller_than_usize!(u16);
impl_num_wrapper_for_smaller_than_usize!(u32);
impl_num_wrapper_for_smaller_than_usize!(u64);
impl_num_wrapper_for_equal_or_larger_than_usize!(u128);
} else {
compile_error!("Unsupported architecture - unhandled pointer size.");
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
fn incr_by(cc: u8, count: usize) -> u8 {
let v = NumWrapper::<u8>::wrap(count);
cc.wrapping_add(v)
}
fn incr_by_naive(mut cc: u8, count: usize) -> u8 {
for _ in 0..count {
cc = cc.wrapping_add(1);
}
cc
}
fn decr_by(cc: u8, count: usize) -> u8 {
let v = NumWrapper::<u8>::wrap(count);
cc.wrapping_sub(v)
}
fn decr_by_naive(mut cc: u8, count: usize) -> u8 {
for _ in 0..count {
cc = cc.wrapping_sub(1);
}
cc
}
proptest! {
#[test]
fn test_wrapping_incr(x: u8, y in 0..4096usize) {
assert_eq!(incr_by_naive(x, y), incr_by(x, y));
}
#[test]
fn test_wrapping_decr(x: u8, y in 0..4096usize) {
assert_eq!(decr_by_naive(x, y), decr_by(x, y));
}
#[test]
fn test_wrapping_incr_decr_symmetric(x: u8, y: usize) {
assert_eq!(x, decr_by(incr_by(x, y), y));
}
}
}