mirror of
https://github.com/Zenithsiz/int-conv.git
synced 2026-02-03 14:10:08 +00:00
Merge branch '0.1.4'
This commit is contained in:
parent
e2898bb653
commit
627ec21edf
@ -1,4 +1,10 @@
|
||||
|
||||
# 0.1.4
|
||||
|
||||
Split items in `extend` module into their own modules inside `extend`.
|
||||
Added `Signed::abs_unsigned` method.
|
||||
Improved `sign` module's tests.
|
||||
|
||||
# 0.1.3
|
||||
|
||||
Added insight on implementations on references for most traits.
|
||||
@ -4,7 +4,7 @@ description = "Explicit integer conversions"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
license_file = "LICENSE"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
authors = ["Filipe Rodrigues <filipejacintorodrigues1@gmail.com>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/Zenithsiz/int-conv"
|
||||
|
||||
450
src/extend.rs
450
src/extend.rs
@ -3,446 +3,12 @@
|
||||
//! This module contains several traits that dictate behavior for extending
|
||||
//! an integer past it's range.
|
||||
|
||||
// Imports
|
||||
use crate::Signed;
|
||||
use core::mem;
|
||||
// Modules
|
||||
pub mod default;
|
||||
pub mod sign;
|
||||
pub mod zero;
|
||||
|
||||
/// Zero extend
|
||||
///
|
||||
/// This trait serves to extend integers with `0`s,
|
||||
/// including signed ones.
|
||||
pub trait ZeroExtend<T>: Sized {
|
||||
/// Zero extends this type
|
||||
fn zero_extend(self) -> T;
|
||||
}
|
||||
|
||||
/// Sign extends
|
||||
///
|
||||
/// This trait serves to extend integers with their
|
||||
/// sign signal.
|
||||
pub trait SignExtend<T>: Sized {
|
||||
/// Sign extends this type
|
||||
fn sign_extend(self) -> T;
|
||||
}
|
||||
|
||||
/// Generic extension
|
||||
///
|
||||
/// This type performs either a zero extend or
|
||||
/// a sign extend, depending if the type is signed.
|
||||
pub trait Extend<T>: Sized {
|
||||
/// Extends this type
|
||||
fn extend(self) -> T;
|
||||
}
|
||||
|
||||
/// Zero extending to the same type simply returns it
|
||||
impl<T> ZeroExtend<T> for T {
|
||||
#[inline]
|
||||
fn zero_extend(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign extending to the same type simply returns it
|
||||
impl<T> SignExtend<T> for T {
|
||||
#[inline]
|
||||
fn sign_extend(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Extending to the same type simply returns it
|
||||
impl<T> Extend<T> for T {
|
||||
#[inline]
|
||||
fn extend(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to help implement [`ZeroExtend`]
|
||||
///
|
||||
/// Note: Regardless if `GAT`s are available, a `impl ZeroExtend<&'b U> for &'a T` isn't
|
||||
/// possible, as it would require memory available for `U` at `T`, which we don't
|
||||
/// know from just receiving a reference to `T`.
|
||||
macro_rules! impl_zero_extend {
|
||||
($T:ty => $( $U:ty ),+ $(,)?) => {
|
||||
$(
|
||||
// Make sure `U` is bigger or equal to `T` so we don't truncate the integer
|
||||
// Note: It is guaranteed that signed and unsigned variants have the same size
|
||||
::static_assertions::const_assert!(mem::size_of::<$U>() >= mem::size_of::<$T>());
|
||||
|
||||
impl ZeroExtend<$U> for $T
|
||||
{
|
||||
#[inline]
|
||||
#[allow(clippy::as_conversions)]
|
||||
fn zero_extend(self) -> $U {
|
||||
// Casting between signedness is a no-op.
|
||||
// Casting from a smaller to larger unsigned integer will zero-extend.
|
||||
self
|
||||
as <$T as Signed>::Unsigned
|
||||
as <$U as Signed>::Unsigned
|
||||
as $U
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with generic version once specialization is stable
|
||||
impl<'a> ZeroExtend<$U> for &'a $T
|
||||
where
|
||||
$T: ZeroExtend<$U>
|
||||
{
|
||||
#[inline]
|
||||
fn zero_extend(self) -> $U {
|
||||
<$T as ZeroExtend<$U>>::zero_extend(*self)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
// Unsigned
|
||||
impl_zero_extend! { u8 => u16, u32, u64, u128 }
|
||||
impl_zero_extend! { u16 => u32, u64, u128 }
|
||||
impl_zero_extend! { u32 => u64, u128 }
|
||||
impl_zero_extend! { u64 => u128 }
|
||||
|
||||
// Signed
|
||||
impl_zero_extend! { i8 => i16, i32, i64, i128 }
|
||||
impl_zero_extend! { i16 => i32, i64, i128 }
|
||||
impl_zero_extend! { i32 => i64, i128 }
|
||||
impl_zero_extend! { i64 => i128 }
|
||||
|
||||
/// Macro to help implement [`SignExtend`]
|
||||
///
|
||||
/// Note: Regardless if `GAT`s are available, a `impl SignExtend<&'b U> for &'a T` isn't
|
||||
/// possible, as it would require memory available for `U` at `T`, which we don't
|
||||
/// know from just receiving a reference to `T`.
|
||||
macro_rules! impl_sign_extend {
|
||||
($T:ty => $( $U:ty ),+ $(,)?) => {
|
||||
$(
|
||||
// Make sure `U` is bigger or equal to `T` so we don't truncate the integer
|
||||
// Note: It is guaranteed that signed and unsigned variants have the same size
|
||||
::static_assertions::const_assert!(mem::size_of::<$U>() >= mem::size_of::<$T>());
|
||||
|
||||
impl SignExtend<$U> for $T
|
||||
{
|
||||
#[inline]
|
||||
#[allow(clippy::as_conversions)]
|
||||
fn sign_extend(self) -> $U {
|
||||
// Casting between signedness is a no-op.
|
||||
// Casting from a smaller to larger signed integer will sign-extend.
|
||||
self
|
||||
as <$T as Signed>::Signed
|
||||
as <$U as Signed>::Signed
|
||||
as $U
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with generic version once specialization is stable
|
||||
impl<'a> SignExtend<$U> for &'a $T
|
||||
where
|
||||
$T: SignExtend<$U>
|
||||
{
|
||||
#[inline]
|
||||
fn sign_extend(self) -> $U {
|
||||
<$T as SignExtend<$U>>::sign_extend(*self)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
// Signed
|
||||
impl_sign_extend! { i8 => i16, i32, i64, i128 }
|
||||
impl_sign_extend! { i16 => i32, i64, i128 }
|
||||
impl_sign_extend! { i32 => i64, i128 }
|
||||
impl_sign_extend! { i64 => i128 }
|
||||
|
||||
/// Macro to help implement [`Extend`]
|
||||
///
|
||||
/// Note: Regardless if `GAT`s are available, a `impl Extend<&'b U> for &'a T` isn't
|
||||
/// possible, as it would require memory available for `U` at `T`, which we don't
|
||||
/// know from just receiving a reference to `T`.
|
||||
macro_rules! impl_extend {
|
||||
($T:ty => $( $U:ty ),+ $(,)? => $method:ident) => {
|
||||
$(
|
||||
impl Extend<$U> for $T
|
||||
{
|
||||
#[inline]
|
||||
fn extend(self) -> $U {
|
||||
self.$method()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with generic version once specialization is stable
|
||||
impl<'a> Extend<$U> for &'a $T
|
||||
where
|
||||
$T: Extend<$U>
|
||||
{
|
||||
#[inline]
|
||||
fn extend(self) -> $U {
|
||||
<$T as Extend<$U>>::extend(*self)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
// Unsigned
|
||||
impl_extend! { u8 => u16, u32, u64, u128 => zero_extend }
|
||||
impl_extend! { u16 => u32, u64, u128 => zero_extend }
|
||||
impl_extend! { u32 => u64, u128 => zero_extend }
|
||||
impl_extend! { u64 => u128 => zero_extend }
|
||||
|
||||
// Signed
|
||||
impl_extend! { i8 => i16, i32, i64, i128 => sign_extend }
|
||||
impl_extend! { i16 => i32, i64, i128 => sign_extend }
|
||||
impl_extend! { i32 => i64, i128 => sign_extend }
|
||||
impl_extend! { i64 => i128 => sign_extend }
|
||||
|
||||
/// Helper trait for [`ZeroExtend`] to be used with turbofish syntax
|
||||
pub trait ZeroExtended: Sized {
|
||||
/// Zero extends this type
|
||||
#[inline]
|
||||
fn zero_extended<T>(self) -> T
|
||||
where
|
||||
Self: ZeroExtend<T>,
|
||||
{
|
||||
self.zero_extend()
|
||||
}
|
||||
}
|
||||
impl<T> ZeroExtended for T {}
|
||||
|
||||
/// Helper trait for [`SignExtend`] to be used with turbofish syntax
|
||||
pub trait SignExtended {
|
||||
/// Sign extends this type
|
||||
#[inline]
|
||||
fn sign_extended<T>(self) -> T
|
||||
where
|
||||
Self: SignExtend<T>,
|
||||
{
|
||||
self.sign_extend()
|
||||
}
|
||||
}
|
||||
impl<T> SignExtended for T {}
|
||||
|
||||
/// Helper trait for [`Extend`] to be used with turbofish syntax
|
||||
pub trait Extended {
|
||||
/// Extends this type
|
||||
#[inline]
|
||||
fn extended<T>(self) -> T
|
||||
where
|
||||
Self: Extend<T>,
|
||||
{
|
||||
self.extend()
|
||||
}
|
||||
}
|
||||
impl<T> Extended for T {}
|
||||
|
||||
// Check that all `ZeroExtend` / `SignExtend` / `Extend` impls exist
|
||||
static_assertions::assert_impl_all! { i8 : ZeroExtend<i8>, ZeroExtend<i16>, ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i16 : ZeroExtend<i16>, ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i32 : ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i64 : ZeroExtend<i64>, ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i128 : ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { u8 : ZeroExtend<u8>, ZeroExtend<u16>, ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { u16 : ZeroExtend<u16>, ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { u32 : ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { u64 : ZeroExtend<u64>, ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { u128 : ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { i8 : SignExtend<i8>, SignExtend<i16>, SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i16 : SignExtend<i16>, SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i32 : SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i64 : SignExtend<i64>, SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i128 : SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i8 : Extend<i8>, Extend<i16>, Extend<i32>, Extend<i64>, Extend<i128> }
|
||||
static_assertions::assert_impl_all! { i16 : Extend<i16>, Extend<i32>, Extend<i64>, Extend<i128> }
|
||||
static_assertions::assert_impl_all! { i32 : Extend<i32>, Extend<i64>, Extend<i128> }
|
||||
static_assertions::assert_impl_all! { i64 : Extend<i64>, Extend<i128> }
|
||||
static_assertions::assert_impl_all! { i128 : Extend<i128> }
|
||||
static_assertions::assert_impl_all! { u8 : Extend<u8>, Extend<u16>, Extend<u32>, Extend<u64>, Extend<u128> }
|
||||
static_assertions::assert_impl_all! { u16 : Extend<u16>, Extend<u32>, Extend<u64>, Extend<u128> }
|
||||
static_assertions::assert_impl_all! { u32 : Extend<u32>, Extend<u64>, Extend<u128> }
|
||||
static_assertions::assert_impl_all! { u64 : Extend<u64>, Extend<u128> }
|
||||
static_assertions::assert_impl_all! { u128 : Extend<u128> }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn zero_extend_unsigned() {
|
||||
assert_eq!( u8::zero_extended::< u8>(1), 1);
|
||||
assert_eq!( u8::zero_extended::< u16>(1), 1);
|
||||
assert_eq!( u8::zero_extended::< u32>(1), 1);
|
||||
assert_eq!( u8::zero_extended::< u64>(1), 1);
|
||||
assert_eq!( u8::zero_extended::<u128>(1), 1);
|
||||
assert_eq!( u16::zero_extended::< u16>(1), 1);
|
||||
assert_eq!( u16::zero_extended::< u32>(1), 1);
|
||||
assert_eq!( u16::zero_extended::< u64>(1), 1);
|
||||
assert_eq!( u16::zero_extended::<u128>(1), 1);
|
||||
assert_eq!( u32::zero_extended::< u32>(1), 1);
|
||||
assert_eq!( u32::zero_extended::< u64>(1), 1);
|
||||
assert_eq!( u32::zero_extended::<u128>(1), 1);
|
||||
assert_eq!( u64::zero_extended::< u64>(1), 1);
|
||||
assert_eq!( u64::zero_extended::<u128>(1), 1);
|
||||
assert_eq!(u128::zero_extended::<u128>(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn zero_extend_unsigned_big() {
|
||||
|
||||
assert_eq!( u8::zero_extended::< u16>( u8::MAX), u16::from( u8::MAX));
|
||||
assert_eq!( u8::zero_extended::< u32>( u8::MAX), u32::from( u8::MAX));
|
||||
assert_eq!( u8::zero_extended::< u64>( u8::MAX), u64::from( u8::MAX));
|
||||
assert_eq!( u8::zero_extended::<u128>( u8::MAX), u128::from( u8::MAX));
|
||||
assert_eq!(u16::zero_extended::< u32>(u16::MAX), u32::from(u16::MAX));
|
||||
assert_eq!(u16::zero_extended::< u64>(u16::MAX), u64::from(u16::MAX));
|
||||
assert_eq!(u16::zero_extended::<u128>(u16::MAX), u128::from(u16::MAX));
|
||||
assert_eq!(u32::zero_extended::< u64>(u32::MAX), u64::from(u32::MAX));
|
||||
assert_eq!(u32::zero_extended::<u128>(u32::MAX), u128::from(u32::MAX));
|
||||
assert_eq!(u64::zero_extended::<u128>(u64::MAX), u128::from(u64::MAX));
|
||||
|
||||
assert_eq!( u8::zero_extended::< u8>( u8::MAX), u8::MAX);
|
||||
assert_eq!( u16::zero_extended::< u16>( u16::MAX), u16::MAX);
|
||||
assert_eq!( u32::zero_extended::< u32>( u32::MAX), u32::MAX);
|
||||
assert_eq!( u64::zero_extended::< u64>( u64::MAX), u64::MAX);
|
||||
assert_eq!(u128::zero_extended::<u128>(u128::MAX), u128::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn zero_extend_signed_positive() {
|
||||
assert_eq!( i8::zero_extended::< i8>(1), 1);
|
||||
assert_eq!( i8::zero_extended::< i16>(1), 1);
|
||||
assert_eq!( i8::zero_extended::< i32>(1), 1);
|
||||
assert_eq!( i8::zero_extended::< i64>(1), 1);
|
||||
assert_eq!( i8::zero_extended::<i128>(1), 1);
|
||||
assert_eq!( i16::zero_extended::< i16>(1), 1);
|
||||
assert_eq!( i16::zero_extended::< i32>(1), 1);
|
||||
assert_eq!( i16::zero_extended::< i64>(1), 1);
|
||||
assert_eq!( i16::zero_extended::<i128>(1), 1);
|
||||
assert_eq!( i32::zero_extended::< i32>(1), 1);
|
||||
assert_eq!( i32::zero_extended::< i64>(1), 1);
|
||||
assert_eq!( i32::zero_extended::<i128>(1), 1);
|
||||
assert_eq!( i64::zero_extended::< i64>(1), 1);
|
||||
assert_eq!( i64::zero_extended::<i128>(1), 1);
|
||||
assert_eq!(i128::zero_extended::<i128>(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn zero_extend_signed_negative() {
|
||||
assert_eq!( i8::zero_extended::< i16>(-1), i16::from(u8 ::MAX));
|
||||
assert_eq!( i8::zero_extended::< i32>(-1), i32::from(u8 ::MAX));
|
||||
assert_eq!( i8::zero_extended::< i64>(-1), i64::from(u8 ::MAX));
|
||||
assert_eq!( i8::zero_extended::<i128>(-1), i128::from(u8 ::MAX));
|
||||
assert_eq!(i16::zero_extended::< i32>(-1), i32::from(u16 ::MAX));
|
||||
assert_eq!(i16::zero_extended::< i64>(-1), i64::from(u16 ::MAX));
|
||||
assert_eq!(i16::zero_extended::<i128>(-1), i128::from(u16 ::MAX));
|
||||
assert_eq!(i32::zero_extended::< i64>(-1), i64::from(u32 ::MAX));
|
||||
assert_eq!(i32::zero_extended::<i128>(-1), i128::from(u32 ::MAX));
|
||||
assert_eq!(i64::zero_extended::<i128>(-1), i128::from(u64 ::MAX));
|
||||
|
||||
assert_eq!( i8::zero_extended::< i8>(-1), -1);
|
||||
assert_eq!( i16::zero_extended::< i16>(-1), -1);
|
||||
assert_eq!( i32::zero_extended::< i32>(-1), -1);
|
||||
assert_eq!( i64::zero_extended::< i64>(-1), -1);
|
||||
assert_eq!(i128::zero_extended::<i128>(-1), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn sign_extend_positive() {
|
||||
assert_eq!(i8 ::sign_extended::< i16>(1), 1);
|
||||
assert_eq!(i8 ::sign_extended::< i32>(1), 1);
|
||||
assert_eq!(i8 ::sign_extended::< i64>(1), 1);
|
||||
assert_eq!(i8 ::sign_extended::<i128>(1), 1);
|
||||
assert_eq!(i16::sign_extended::< i32>(1), 1);
|
||||
assert_eq!(i16::sign_extended::< i64>(1), 1);
|
||||
assert_eq!(i16::sign_extended::<i128>(1), 1);
|
||||
assert_eq!(i32::sign_extended::< i64>(1), 1);
|
||||
assert_eq!(i32::sign_extended::<i128>(1), 1);
|
||||
assert_eq!(i64::sign_extended::<i128>(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn sign_extend_negative() {
|
||||
assert_eq!( i8::sign_extended::< i16>(-1), -1);
|
||||
assert_eq!( i8::sign_extended::< i32>(-1), -1);
|
||||
assert_eq!( i8::sign_extended::< i64>(-1), -1);
|
||||
assert_eq!( i8::sign_extended::<i128>(-1), -1);
|
||||
assert_eq!(i16::sign_extended::< i32>(-1), -1);
|
||||
assert_eq!(i16::sign_extended::< i64>(-1), -1);
|
||||
assert_eq!(i16::sign_extended::<i128>(-1), -1);
|
||||
assert_eq!(i32::sign_extended::< i64>(-1), -1);
|
||||
assert_eq!(i32::sign_extended::<i128>(-1), -1);
|
||||
assert_eq!(i64::sign_extended::<i128>(-1), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn extend_unsigned() {
|
||||
assert_eq!( u8::extended::< u16>(1), u8::zero_extended::< u16>(1));
|
||||
assert_eq!( u8::extended::< u32>(1), u8::zero_extended::< u32>(1));
|
||||
assert_eq!( u8::extended::< u64>(1), u8::zero_extended::< u64>(1));
|
||||
assert_eq!( u8::extended::<u128>(1), u8::zero_extended::<u128>(1));
|
||||
assert_eq!(u16::extended::< u32>(1), u16::zero_extended::< u32>(1));
|
||||
assert_eq!(u16::extended::< u64>(1), u16::zero_extended::< u64>(1));
|
||||
assert_eq!(u16::extended::<u128>(1), u16::zero_extended::<u128>(1));
|
||||
assert_eq!(u32::extended::< u64>(1), u32::zero_extended::< u64>(1));
|
||||
assert_eq!(u32::extended::<u128>(1), u32::zero_extended::<u128>(1));
|
||||
assert_eq!(u64::extended::<u128>(1), u64::zero_extended::<u128>(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn extend_unsigned_big() {
|
||||
assert_eq!( u8::extended::< u8>( u8::MAX), u8::zero_extended::< u8>( u8::MAX));
|
||||
assert_eq!( u8::extended::< u16>( u8::MAX), u8::zero_extended::< u16>( u8::MAX));
|
||||
assert_eq!( u8::extended::< u32>( u8::MAX), u8::zero_extended::< u32>( u8::MAX));
|
||||
assert_eq!( u8::extended::< u64>( u8::MAX), u8::zero_extended::< u64>( u8::MAX));
|
||||
assert_eq!( u8::extended::<u128>( u8::MAX), u8::zero_extended::<u128>( u8::MAX));
|
||||
assert_eq!( u16::extended::< u16>( u16::MAX), u16::zero_extended::< u16>( u16::MAX));
|
||||
assert_eq!( u16::extended::< u32>( u16::MAX), u16::zero_extended::< u32>( u16::MAX));
|
||||
assert_eq!( u16::extended::< u64>( u16::MAX), u16::zero_extended::< u64>( u16::MAX));
|
||||
assert_eq!( u16::extended::<u128>( u16::MAX), u16::zero_extended::<u128>( u16::MAX));
|
||||
assert_eq!( u32::extended::< u32>( u32::MAX), u32::zero_extended::< u32>( u32::MAX));
|
||||
assert_eq!( u32::extended::< u64>( u32::MAX), u32::zero_extended::< u64>( u32::MAX));
|
||||
assert_eq!( u32::extended::<u128>( u32::MAX), u32::zero_extended::<u128>( u32::MAX));
|
||||
assert_eq!( u64::extended::< u64>( u64::MAX), u64::zero_extended::< u64>( u64::MAX));
|
||||
assert_eq!( u64::extended::<u128>( u64::MAX), u64::zero_extended::<u128>( u64::MAX));
|
||||
assert_eq!(u128::extended::<u128>(u128::MAX), u128::zero_extended::<u128>(u128::MAX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn extend_signed_positive() {
|
||||
assert_eq!( i8::extended::< i16>(1), i8::sign_extended::< i16>(1));
|
||||
assert_eq!( i8::extended::< i32>(1), i8::sign_extended::< i32>(1));
|
||||
assert_eq!( i8::extended::< i64>(1), i8::sign_extended::< i64>(1));
|
||||
assert_eq!( i8::extended::<i128>(1), i8::sign_extended::<i128>(1));
|
||||
assert_eq!(i16::extended::< i32>(1), i16::sign_extended::< i32>(1));
|
||||
assert_eq!(i16::extended::< i64>(1), i16::sign_extended::< i64>(1));
|
||||
assert_eq!(i16::extended::<i128>(1), i16::sign_extended::<i128>(1));
|
||||
assert_eq!(i32::extended::< i64>(1), i32::sign_extended::< i64>(1));
|
||||
assert_eq!(i32::extended::<i128>(1), i32::sign_extended::<i128>(1));
|
||||
assert_eq!(i64::extended::<i128>(1), i64::sign_extended::<i128>(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn extend_signed_negative() {
|
||||
assert_eq!( i8::extended::< i16>(-1), i8::sign_extended::< i16>(-1));
|
||||
assert_eq!( i8::extended::< i32>(-1), i8::sign_extended::< i32>(-1));
|
||||
assert_eq!( i8::extended::< i64>(-1), i8::sign_extended::< i64>(-1));
|
||||
assert_eq!( i8::extended::<i128>(-1), i8::sign_extended::<i128>(-1));
|
||||
assert_eq!(i16::extended::< i32>(-1), i16::sign_extended::< i32>(-1));
|
||||
assert_eq!(i16::extended::< i64>(-1), i16::sign_extended::< i64>(-1));
|
||||
assert_eq!(i16::extended::<i128>(-1), i16::sign_extended::<i128>(-1));
|
||||
assert_eq!(i32::extended::< i64>(-1), i32::sign_extended::< i64>(-1));
|
||||
assert_eq!(i32::extended::<i128>(-1), i32::sign_extended::<i128>(-1));
|
||||
assert_eq!(i64::extended::<i128>(-1), i64::sign_extended::<i128>(-1));
|
||||
}
|
||||
}
|
||||
// Exports
|
||||
pub use default::{Extend, Extended};
|
||||
pub use sign::{SignExtend, SignExtended};
|
||||
pub use zero::{ZeroExtend, ZeroExtended};
|
||||
|
||||
160
src/extend/default.rs
Normal file
160
src/extend/default.rs
Normal file
@ -0,0 +1,160 @@
|
||||
//! Default extension
|
||||
|
||||
// Imports
|
||||
use crate::{SignExtend, ZeroExtend};
|
||||
|
||||
/// Generic extension
|
||||
///
|
||||
/// This type performs either a zero extend or
|
||||
/// a sign extend, depending if the type is signed.
|
||||
pub trait Extend<T>: Sized {
|
||||
/// Extends this type
|
||||
fn extend(self) -> T;
|
||||
}
|
||||
|
||||
/// Extending to the same type simply returns it
|
||||
impl<T> Extend<T> for T {
|
||||
#[inline]
|
||||
fn extend(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to help implement [`Extend`]
|
||||
///
|
||||
/// Note: Regardless if `GAT`s are available, a `impl Extend<&'b U> for &'a T` isn't
|
||||
/// possible, as it would require memory available for `U` at `T`, which we don't
|
||||
/// know from just receiving a reference to `T`.
|
||||
macro_rules! impl_extend {
|
||||
($T:ty => $( $U:ty ),+ $(,)? => $method:ident) => {
|
||||
$(
|
||||
impl Extend<$U> for $T
|
||||
{
|
||||
#[inline]
|
||||
fn extend(self) -> $U {
|
||||
self.$method()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with generic version once specialization is stable
|
||||
impl<'a> Extend<$U> for &'a $T
|
||||
where
|
||||
$T: Extend<$U>
|
||||
{
|
||||
#[inline]
|
||||
fn extend(self) -> $U {
|
||||
<$T as Extend<$U>>::extend(*self)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
// Unsigned
|
||||
impl_extend! { u8 => u16, u32, u64, u128 => zero_extend }
|
||||
impl_extend! { u16 => u32, u64, u128 => zero_extend }
|
||||
impl_extend! { u32 => u64, u128 => zero_extend }
|
||||
impl_extend! { u64 => u128 => zero_extend }
|
||||
|
||||
// Signed
|
||||
impl_extend! { i8 => i16, i32, i64, i128 => sign_extend }
|
||||
impl_extend! { i16 => i32, i64, i128 => sign_extend }
|
||||
impl_extend! { i32 => i64, i128 => sign_extend }
|
||||
impl_extend! { i64 => i128 => sign_extend }
|
||||
|
||||
/// Helper trait for [`Extend`] to be used with turbofish syntax
|
||||
pub trait Extended {
|
||||
/// Extends this type
|
||||
#[inline]
|
||||
fn extended<T>(self) -> T
|
||||
where
|
||||
Self: Extend<T>,
|
||||
{
|
||||
self.extend()
|
||||
}
|
||||
}
|
||||
impl<T> Extended for T {}
|
||||
|
||||
// Check that all `Extend` impls exist
|
||||
static_assertions::assert_impl_all! { i8 : Extend<i8>, Extend<i16>, Extend<i32>, Extend<i64>, Extend<i128> }
|
||||
static_assertions::assert_impl_all! { i16 : Extend<i16>, Extend<i32>, Extend<i64>, Extend<i128> }
|
||||
static_assertions::assert_impl_all! { i32 : Extend<i32>, Extend<i64>, Extend<i128> }
|
||||
static_assertions::assert_impl_all! { i64 : Extend<i64>, Extend<i128> }
|
||||
static_assertions::assert_impl_all! { i128 : Extend<i128> }
|
||||
static_assertions::assert_impl_all! { u8 : Extend<u8>, Extend<u16>, Extend<u32>, Extend<u64>, Extend<u128> }
|
||||
static_assertions::assert_impl_all! { u16 : Extend<u16>, Extend<u32>, Extend<u64>, Extend<u128> }
|
||||
static_assertions::assert_impl_all! { u32 : Extend<u32>, Extend<u64>, Extend<u128> }
|
||||
static_assertions::assert_impl_all! { u64 : Extend<u64>, Extend<u128> }
|
||||
static_assertions::assert_impl_all! { u128 : Extend<u128> }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// Imports
|
||||
use super::*;
|
||||
use crate::{SignExtended, ZeroExtended};
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn extend_unsigned() {
|
||||
assert_eq!( u8::extended::< u16>(1), u8::zero_extended::< u16>(1));
|
||||
assert_eq!( u8::extended::< u32>(1), u8::zero_extended::< u32>(1));
|
||||
assert_eq!( u8::extended::< u64>(1), u8::zero_extended::< u64>(1));
|
||||
assert_eq!( u8::extended::<u128>(1), u8::zero_extended::<u128>(1));
|
||||
assert_eq!(u16::extended::< u32>(1), u16::zero_extended::< u32>(1));
|
||||
assert_eq!(u16::extended::< u64>(1), u16::zero_extended::< u64>(1));
|
||||
assert_eq!(u16::extended::<u128>(1), u16::zero_extended::<u128>(1));
|
||||
assert_eq!(u32::extended::< u64>(1), u32::zero_extended::< u64>(1));
|
||||
assert_eq!(u32::extended::<u128>(1), u32::zero_extended::<u128>(1));
|
||||
assert_eq!(u64::extended::<u128>(1), u64::zero_extended::<u128>(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn extend_unsigned_big() {
|
||||
assert_eq!( u8::extended::< u8>( u8::MAX), u8::zero_extended::< u8>( u8::MAX));
|
||||
assert_eq!( u8::extended::< u16>( u8::MAX), u8::zero_extended::< u16>( u8::MAX));
|
||||
assert_eq!( u8::extended::< u32>( u8::MAX), u8::zero_extended::< u32>( u8::MAX));
|
||||
assert_eq!( u8::extended::< u64>( u8::MAX), u8::zero_extended::< u64>( u8::MAX));
|
||||
assert_eq!( u8::extended::<u128>( u8::MAX), u8::zero_extended::<u128>( u8::MAX));
|
||||
assert_eq!( u16::extended::< u16>( u16::MAX), u16::zero_extended::< u16>( u16::MAX));
|
||||
assert_eq!( u16::extended::< u32>( u16::MAX), u16::zero_extended::< u32>( u16::MAX));
|
||||
assert_eq!( u16::extended::< u64>( u16::MAX), u16::zero_extended::< u64>( u16::MAX));
|
||||
assert_eq!( u16::extended::<u128>( u16::MAX), u16::zero_extended::<u128>( u16::MAX));
|
||||
assert_eq!( u32::extended::< u32>( u32::MAX), u32::zero_extended::< u32>( u32::MAX));
|
||||
assert_eq!( u32::extended::< u64>( u32::MAX), u32::zero_extended::< u64>( u32::MAX));
|
||||
assert_eq!( u32::extended::<u128>( u32::MAX), u32::zero_extended::<u128>( u32::MAX));
|
||||
assert_eq!( u64::extended::< u64>( u64::MAX), u64::zero_extended::< u64>( u64::MAX));
|
||||
assert_eq!( u64::extended::<u128>( u64::MAX), u64::zero_extended::<u128>( u64::MAX));
|
||||
assert_eq!(u128::extended::<u128>(u128::MAX), u128::zero_extended::<u128>(u128::MAX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn extend_signed_positive() {
|
||||
assert_eq!( i8::extended::< i16>(1), i8::sign_extended::< i16>(1));
|
||||
assert_eq!( i8::extended::< i32>(1), i8::sign_extended::< i32>(1));
|
||||
assert_eq!( i8::extended::< i64>(1), i8::sign_extended::< i64>(1));
|
||||
assert_eq!( i8::extended::<i128>(1), i8::sign_extended::<i128>(1));
|
||||
assert_eq!(i16::extended::< i32>(1), i16::sign_extended::< i32>(1));
|
||||
assert_eq!(i16::extended::< i64>(1), i16::sign_extended::< i64>(1));
|
||||
assert_eq!(i16::extended::<i128>(1), i16::sign_extended::<i128>(1));
|
||||
assert_eq!(i32::extended::< i64>(1), i32::sign_extended::< i64>(1));
|
||||
assert_eq!(i32::extended::<i128>(1), i32::sign_extended::<i128>(1));
|
||||
assert_eq!(i64::extended::<i128>(1), i64::sign_extended::<i128>(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn extend_signed_negative() {
|
||||
assert_eq!( i8::extended::< i16>(-1), i8::sign_extended::< i16>(-1));
|
||||
assert_eq!( i8::extended::< i32>(-1), i8::sign_extended::< i32>(-1));
|
||||
assert_eq!( i8::extended::< i64>(-1), i8::sign_extended::< i64>(-1));
|
||||
assert_eq!( i8::extended::<i128>(-1), i8::sign_extended::<i128>(-1));
|
||||
assert_eq!(i16::extended::< i32>(-1), i16::sign_extended::< i32>(-1));
|
||||
assert_eq!(i16::extended::< i64>(-1), i16::sign_extended::< i64>(-1));
|
||||
assert_eq!(i16::extended::<i128>(-1), i16::sign_extended::<i128>(-1));
|
||||
assert_eq!(i32::extended::< i64>(-1), i32::sign_extended::< i64>(-1));
|
||||
assert_eq!(i32::extended::<i128>(-1), i32::sign_extended::<i128>(-1));
|
||||
assert_eq!(i64::extended::<i128>(-1), i64::sign_extended::<i128>(-1));
|
||||
}
|
||||
}
|
||||
124
src/extend/sign.rs
Normal file
124
src/extend/sign.rs
Normal file
@ -0,0 +1,124 @@
|
||||
//! Sign extension
|
||||
|
||||
// Imports
|
||||
use crate::Signed;
|
||||
use core::mem;
|
||||
|
||||
/// Sign extends
|
||||
///
|
||||
/// This trait serves to extend integers with their
|
||||
/// sign signal.
|
||||
pub trait SignExtend<T>: Sized {
|
||||
/// Sign extends this type
|
||||
fn sign_extend(self) -> T;
|
||||
}
|
||||
|
||||
/// Sign extending to the same type simply returns it
|
||||
impl<T> SignExtend<T> for T {
|
||||
#[inline]
|
||||
fn sign_extend(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to help implement [`SignExtend`]
|
||||
///
|
||||
/// Note: Regardless if `GAT`s are available, a `impl SignExtend<&'b U> for &'a T` isn't
|
||||
/// possible, as it would require memory available for `U` at `T`, which we don't
|
||||
/// know from just receiving a reference to `T`.
|
||||
macro_rules! impl_sign_extend {
|
||||
($T:ty => $( $U:ty ),+ $(,)?) => {
|
||||
$(
|
||||
// Make sure `U` is bigger or equal to `T` so we don't truncate the integer
|
||||
// Note: It is guaranteed that signed and unsigned variants have the same size
|
||||
::static_assertions::const_assert!(mem::size_of::<$U>() >= mem::size_of::<$T>());
|
||||
|
||||
impl SignExtend<$U> for $T
|
||||
{
|
||||
#[inline]
|
||||
#[allow(clippy::as_conversions)]
|
||||
fn sign_extend(self) -> $U {
|
||||
// Casting between signedness is a no-op.
|
||||
// Casting from a smaller to larger signed integer will sign-extend.
|
||||
self
|
||||
as <$T as Signed>::Signed
|
||||
as <$U as Signed>::Signed
|
||||
as $U
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with generic version once specialization is stable
|
||||
impl<'a> SignExtend<$U> for &'a $T
|
||||
where
|
||||
$T: SignExtend<$U>
|
||||
{
|
||||
#[inline]
|
||||
fn sign_extend(self) -> $U {
|
||||
<$T as SignExtend<$U>>::sign_extend(*self)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
// Signed
|
||||
impl_sign_extend! { i8 => i16, i32, i64, i128 }
|
||||
impl_sign_extend! { i16 => i32, i64, i128 }
|
||||
impl_sign_extend! { i32 => i64, i128 }
|
||||
impl_sign_extend! { i64 => i128 }
|
||||
|
||||
/// Helper trait for [`SignExtend`] to be used with turbofish syntax
|
||||
pub trait SignExtended {
|
||||
/// Sign extends this type
|
||||
#[inline]
|
||||
fn sign_extended<T>(self) -> T
|
||||
where
|
||||
Self: SignExtend<T>,
|
||||
{
|
||||
self.sign_extend()
|
||||
}
|
||||
}
|
||||
impl<T> SignExtended for T {}
|
||||
|
||||
// Check that all `SignExtend` / `Extend` impls exist
|
||||
static_assertions::assert_impl_all! { i8 : SignExtend<i8>, SignExtend<i16>, SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i16 : SignExtend<i16>, SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i32 : SignExtend<i32>, SignExtend<i64>, SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i64 : SignExtend<i64>, SignExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i128 : SignExtend<i128> }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// Imports
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn sign_extend_positive() {
|
||||
assert_eq!(i8 ::sign_extended::< i16>(1), 1);
|
||||
assert_eq!(i8 ::sign_extended::< i32>(1), 1);
|
||||
assert_eq!(i8 ::sign_extended::< i64>(1), 1);
|
||||
assert_eq!(i8 ::sign_extended::<i128>(1), 1);
|
||||
assert_eq!(i16::sign_extended::< i32>(1), 1);
|
||||
assert_eq!(i16::sign_extended::< i64>(1), 1);
|
||||
assert_eq!(i16::sign_extended::<i128>(1), 1);
|
||||
assert_eq!(i32::sign_extended::< i64>(1), 1);
|
||||
assert_eq!(i32::sign_extended::<i128>(1), 1);
|
||||
assert_eq!(i64::sign_extended::<i128>(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn sign_extend_negative() {
|
||||
assert_eq!( i8::sign_extended::< i16>(-1), -1);
|
||||
assert_eq!( i8::sign_extended::< i32>(-1), -1);
|
||||
assert_eq!( i8::sign_extended::< i64>(-1), -1);
|
||||
assert_eq!( i8::sign_extended::<i128>(-1), -1);
|
||||
assert_eq!(i16::sign_extended::< i32>(-1), -1);
|
||||
assert_eq!(i16::sign_extended::< i64>(-1), -1);
|
||||
assert_eq!(i16::sign_extended::<i128>(-1), -1);
|
||||
assert_eq!(i32::sign_extended::< i64>(-1), -1);
|
||||
assert_eq!(i32::sign_extended::<i128>(-1), -1);
|
||||
assert_eq!(i64::sign_extended::<i128>(-1), -1);
|
||||
}
|
||||
}
|
||||
188
src/extend/zero.rs
Normal file
188
src/extend/zero.rs
Normal file
@ -0,0 +1,188 @@
|
||||
//! Zero extension
|
||||
|
||||
// Imports
|
||||
use crate::Signed;
|
||||
use core::mem;
|
||||
|
||||
/// Zero extend
|
||||
///
|
||||
/// This trait serves to extend integers with `0`s,
|
||||
/// including signed ones.
|
||||
pub trait ZeroExtend<T>: Sized {
|
||||
/// Zero extends this type
|
||||
fn zero_extend(self) -> T;
|
||||
}
|
||||
|
||||
/// Zero extending to the same type simply returns it
|
||||
impl<T> ZeroExtend<T> for T {
|
||||
#[inline]
|
||||
fn zero_extend(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to help implement [`ZeroExtend`]
|
||||
///
|
||||
/// Note: Regardless if `GAT`s are available, a `impl ZeroExtend<&'b U> for &'a T` isn't
|
||||
/// possible, as it would require memory available for `U` at `T`, which we don't
|
||||
/// know from just receiving a reference to `T`.
|
||||
macro_rules! impl_zero_extend {
|
||||
($T:ty => $( $U:ty ),+ $(,)?) => {
|
||||
$(
|
||||
// Make sure `U` is bigger or equal to `T` so we don't truncate the integer
|
||||
// Note: It is guaranteed that signed and unsigned variants have the same size
|
||||
::static_assertions::const_assert!(mem::size_of::<$U>() >= mem::size_of::<$T>());
|
||||
|
||||
impl ZeroExtend<$U> for $T
|
||||
{
|
||||
#[inline]
|
||||
#[allow(clippy::as_conversions)]
|
||||
fn zero_extend(self) -> $U {
|
||||
// Casting between signedness is a no-op.
|
||||
// Casting from a smaller to larger unsigned integer will zero-extend.
|
||||
self
|
||||
as <$T as Signed>::Unsigned
|
||||
as <$U as Signed>::Unsigned
|
||||
as $U
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with generic version once specialization is stable
|
||||
impl<'a> ZeroExtend<$U> for &'a $T
|
||||
where
|
||||
$T: ZeroExtend<$U>
|
||||
{
|
||||
#[inline]
|
||||
fn zero_extend(self) -> $U {
|
||||
<$T as ZeroExtend<$U>>::zero_extend(*self)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
// Unsigned
|
||||
impl_zero_extend! { u8 => u16, u32, u64, u128 }
|
||||
impl_zero_extend! { u16 => u32, u64, u128 }
|
||||
impl_zero_extend! { u32 => u64, u128 }
|
||||
impl_zero_extend! { u64 => u128 }
|
||||
|
||||
// Signed
|
||||
impl_zero_extend! { i8 => i16, i32, i64, i128 }
|
||||
impl_zero_extend! { i16 => i32, i64, i128 }
|
||||
impl_zero_extend! { i32 => i64, i128 }
|
||||
impl_zero_extend! { i64 => i128 }
|
||||
|
||||
/// Helper trait for [`ZeroExtend`] to be used with turbofish syntax
|
||||
pub trait ZeroExtended: Sized {
|
||||
/// Zero extends this type
|
||||
#[inline]
|
||||
fn zero_extended<T>(self) -> T
|
||||
where
|
||||
Self: ZeroExtend<T>,
|
||||
{
|
||||
self.zero_extend()
|
||||
}
|
||||
}
|
||||
impl<T> ZeroExtended for T {}
|
||||
|
||||
// Check that all `ZeroExtend` impls exist
|
||||
static_assertions::assert_impl_all! { i8 : ZeroExtend<i8>, ZeroExtend<i16>, ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i16 : ZeroExtend<i16>, ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i32 : ZeroExtend<i32>, ZeroExtend<i64>, ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i64 : ZeroExtend<i64>, ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { i128 : ZeroExtend<i128> }
|
||||
static_assertions::assert_impl_all! { u8 : ZeroExtend<u8>, ZeroExtend<u16>, ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { u16 : ZeroExtend<u16>, ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { u32 : ZeroExtend<u32>, ZeroExtend<u64>, ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { u64 : ZeroExtend<u64>, ZeroExtend<u128> }
|
||||
static_assertions::assert_impl_all! { u128 : ZeroExtend<u128> }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// Imports
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn zero_extend_unsigned() {
|
||||
assert_eq!( u8::zero_extended::< u8>(1), 1);
|
||||
assert_eq!( u8::zero_extended::< u16>(1), 1);
|
||||
assert_eq!( u8::zero_extended::< u32>(1), 1);
|
||||
assert_eq!( u8::zero_extended::< u64>(1), 1);
|
||||
assert_eq!( u8::zero_extended::<u128>(1), 1);
|
||||
assert_eq!( u16::zero_extended::< u16>(1), 1);
|
||||
assert_eq!( u16::zero_extended::< u32>(1), 1);
|
||||
assert_eq!( u16::zero_extended::< u64>(1), 1);
|
||||
assert_eq!( u16::zero_extended::<u128>(1), 1);
|
||||
assert_eq!( u32::zero_extended::< u32>(1), 1);
|
||||
assert_eq!( u32::zero_extended::< u64>(1), 1);
|
||||
assert_eq!( u32::zero_extended::<u128>(1), 1);
|
||||
assert_eq!( u64::zero_extended::< u64>(1), 1);
|
||||
assert_eq!( u64::zero_extended::<u128>(1), 1);
|
||||
assert_eq!(u128::zero_extended::<u128>(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn zero_extend_unsigned_big() {
|
||||
|
||||
assert_eq!( u8::zero_extended::< u16>( u8::MAX), u16::from( u8::MAX));
|
||||
assert_eq!( u8::zero_extended::< u32>( u8::MAX), u32::from( u8::MAX));
|
||||
assert_eq!( u8::zero_extended::< u64>( u8::MAX), u64::from( u8::MAX));
|
||||
assert_eq!( u8::zero_extended::<u128>( u8::MAX), u128::from( u8::MAX));
|
||||
assert_eq!(u16::zero_extended::< u32>(u16::MAX), u32::from(u16::MAX));
|
||||
assert_eq!(u16::zero_extended::< u64>(u16::MAX), u64::from(u16::MAX));
|
||||
assert_eq!(u16::zero_extended::<u128>(u16::MAX), u128::from(u16::MAX));
|
||||
assert_eq!(u32::zero_extended::< u64>(u32::MAX), u64::from(u32::MAX));
|
||||
assert_eq!(u32::zero_extended::<u128>(u32::MAX), u128::from(u32::MAX));
|
||||
assert_eq!(u64::zero_extended::<u128>(u64::MAX), u128::from(u64::MAX));
|
||||
|
||||
assert_eq!( u8::zero_extended::< u8>( u8::MAX), u8::MAX);
|
||||
assert_eq!( u16::zero_extended::< u16>( u16::MAX), u16::MAX);
|
||||
assert_eq!( u32::zero_extended::< u32>( u32::MAX), u32::MAX);
|
||||
assert_eq!( u64::zero_extended::< u64>( u64::MAX), u64::MAX);
|
||||
assert_eq!(u128::zero_extended::<u128>(u128::MAX), u128::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn zero_extend_signed_positive() {
|
||||
assert_eq!( i8::zero_extended::< i8>(1), 1);
|
||||
assert_eq!( i8::zero_extended::< i16>(1), 1);
|
||||
assert_eq!( i8::zero_extended::< i32>(1), 1);
|
||||
assert_eq!( i8::zero_extended::< i64>(1), 1);
|
||||
assert_eq!( i8::zero_extended::<i128>(1), 1);
|
||||
assert_eq!( i16::zero_extended::< i16>(1), 1);
|
||||
assert_eq!( i16::zero_extended::< i32>(1), 1);
|
||||
assert_eq!( i16::zero_extended::< i64>(1), 1);
|
||||
assert_eq!( i16::zero_extended::<i128>(1), 1);
|
||||
assert_eq!( i32::zero_extended::< i32>(1), 1);
|
||||
assert_eq!( i32::zero_extended::< i64>(1), 1);
|
||||
assert_eq!( i32::zero_extended::<i128>(1), 1);
|
||||
assert_eq!( i64::zero_extended::< i64>(1), 1);
|
||||
assert_eq!( i64::zero_extended::<i128>(1), 1);
|
||||
assert_eq!(i128::zero_extended::<i128>(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn zero_extend_signed_negative() {
|
||||
assert_eq!( i8::zero_extended::< i16>(-1), i16::from(u8 ::MAX));
|
||||
assert_eq!( i8::zero_extended::< i32>(-1), i32::from(u8 ::MAX));
|
||||
assert_eq!( i8::zero_extended::< i64>(-1), i64::from(u8 ::MAX));
|
||||
assert_eq!( i8::zero_extended::<i128>(-1), i128::from(u8 ::MAX));
|
||||
assert_eq!(i16::zero_extended::< i32>(-1), i32::from(u16 ::MAX));
|
||||
assert_eq!(i16::zero_extended::< i64>(-1), i64::from(u16 ::MAX));
|
||||
assert_eq!(i16::zero_extended::<i128>(-1), i128::from(u16 ::MAX));
|
||||
assert_eq!(i32::zero_extended::< i64>(-1), i64::from(u32 ::MAX));
|
||||
assert_eq!(i32::zero_extended::<i128>(-1), i128::from(u32 ::MAX));
|
||||
assert_eq!(i64::zero_extended::<i128>(-1), i128::from(u64 ::MAX));
|
||||
|
||||
assert_eq!( i8::zero_extended::< i8>(-1), -1);
|
||||
assert_eq!( i16::zero_extended::< i16>(-1), -1);
|
||||
assert_eq!( i32::zero_extended::< i32>(-1), -1);
|
||||
assert_eq!( i64::zero_extended::< i64>(-1), -1);
|
||||
assert_eq!(i128::zero_extended::<i128>(-1), -1);
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,8 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
// We want to explicitly use the type we're converting to in implementations
|
||||
#![allow(clippy::use_self)]
|
||||
// We use integer division when we want to discard any decimal parts
|
||||
#![allow(clippy::integer_division)]
|
||||
// In tests, we make sure `as` conversions are correct.
|
||||
#![cfg_attr(test, allow(clippy::as_conversions))]
|
||||
// Tests sometimes contain a lot of cases, but they're all simple
|
||||
|
||||
160
src/sign.rs
160
src/sign.rs
@ -15,12 +15,15 @@ pub trait Signed {
|
||||
/// Unsigned variant of this type
|
||||
type Unsigned;
|
||||
|
||||
/// Reinterprets this type as unsigned
|
||||
/// Reinterprets this value as unsigned
|
||||
fn as_unsigned(self) -> Self::Unsigned;
|
||||
|
||||
/// Reinterprets this type as signed
|
||||
/// Reinterprets this value as signed
|
||||
fn as_signed(self) -> Self::Signed;
|
||||
|
||||
/// Returns the absolute value of `self` as unsigned.
|
||||
fn abs_unsigned(self) -> Self::Unsigned;
|
||||
|
||||
// TODO: Maybe add a `fn signal() -> Signal` method? Or maybe two `is_positive` / `is_negative` methods.
|
||||
}
|
||||
|
||||
@ -53,6 +56,17 @@ macro_rules! impl_signed {
|
||||
fn as_signed(self) -> Self::Signed {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn abs_unsigned(self) -> Self::Unsigned {
|
||||
// Note: Branch is optimized by compiler in release mode.
|
||||
if self < 0 {
|
||||
// Note: We don't use `-self.as_unsigned()` because it can panic
|
||||
(!self.as_unsigned()).wrapping_add(1)
|
||||
} else {
|
||||
self.as_unsigned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Signed for $TUnsigned {
|
||||
@ -70,6 +84,12 @@ macro_rules! impl_signed {
|
||||
// Casting between integers of the same size is a no-op
|
||||
self as $TSigned
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn abs_unsigned(self) -> Self::Unsigned {
|
||||
// Note: We're already unsigned
|
||||
self
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -115,38 +135,122 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn as_unsigned() {
|
||||
assert_eq!( u8::as_unsigned(1), 1);
|
||||
assert_eq!( u16::as_unsigned(1), 1);
|
||||
assert_eq!( u32::as_unsigned(1), 1);
|
||||
assert_eq!( u64::as_unsigned(1), 1);
|
||||
assert_eq!( u128::as_unsigned(1), 1);
|
||||
fn as_unsigned_positive() {
|
||||
assert_eq!(u8 ::as_unsigned(1), 1);
|
||||
assert_eq!(u16 ::as_unsigned(1), 1);
|
||||
assert_eq!(u32 ::as_unsigned(1), 1);
|
||||
assert_eq!(u64 ::as_unsigned(1), 1);
|
||||
assert_eq!(u128 ::as_unsigned(1), 1);
|
||||
assert_eq!(usize::as_unsigned(1), 1);
|
||||
|
||||
assert_eq!( i8::as_unsigned(-1), u8 ::MAX);
|
||||
assert_eq!( i16::as_unsigned(-1), u16 ::MAX);
|
||||
assert_eq!( i32::as_unsigned(-1), u32 ::MAX);
|
||||
assert_eq!( i64::as_unsigned(-1), u64 ::MAX);
|
||||
assert_eq!( i128::as_unsigned(-1), u128 ::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn as_unsigned_negative() {
|
||||
assert_eq!(i8 ::as_unsigned(-1), u8 ::MAX);
|
||||
assert_eq!(i16 ::as_unsigned(-1), u16 ::MAX);
|
||||
assert_eq!(i32 ::as_unsigned(-1), u32 ::MAX);
|
||||
assert_eq!(i64 ::as_unsigned(-1), u64 ::MAX);
|
||||
assert_eq!(i128 ::as_unsigned(-1), u128 ::MAX);
|
||||
assert_eq!(isize::as_unsigned(-1), usize::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::cast_possible_wrap)] // We want to wrap around in this test
|
||||
#[rustfmt::skip]
|
||||
fn as_signed() {
|
||||
assert_eq!( u8::as_signed(1), 1);
|
||||
assert_eq!( u16::as_signed(1), 1);
|
||||
assert_eq!( u32::as_signed(1), 1);
|
||||
assert_eq!( u64::as_signed(1), 1);
|
||||
assert_eq!( u128::as_signed(1), 1);
|
||||
fn as_unsigned_negative_big() {
|
||||
assert_eq!(i8 ::as_unsigned(i8 ::MIN), u8 ::MAX / 2 + 1);
|
||||
assert_eq!(i16 ::as_unsigned(i16 ::MIN), u16 ::MAX / 2 + 1);
|
||||
assert_eq!(i32 ::as_unsigned(i32 ::MIN), u32 ::MAX / 2 + 1);
|
||||
assert_eq!(i64 ::as_unsigned(i64 ::MIN), u64 ::MAX / 2 + 1);
|
||||
assert_eq!(i128 ::as_unsigned(i128 ::MIN), u128 ::MAX / 2 + 1);
|
||||
assert_eq!(isize::as_unsigned(isize::MIN), usize::MAX / 2 + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn as_signed_positive() {
|
||||
assert_eq!(u8 ::as_signed(1), 1);
|
||||
assert_eq!(u16 ::as_signed(1), 1);
|
||||
assert_eq!(u32 ::as_signed(1), 1);
|
||||
assert_eq!(u64 ::as_signed(1), 1);
|
||||
assert_eq!(u128 ::as_signed(1), 1);
|
||||
assert_eq!(usize::as_signed(1), 1);
|
||||
|
||||
assert_eq!( i8::as_signed(u8 ::MAX as i8), -1);
|
||||
assert_eq!( i16::as_signed(u16 ::MAX as i16), -1);
|
||||
assert_eq!( i32::as_signed(u32 ::MAX as i32), -1);
|
||||
assert_eq!( i64::as_signed(u64 ::MAX as i64), -1);
|
||||
assert_eq!( i128::as_signed(u128 ::MAX as i128), -1);
|
||||
assert_eq!(isize::as_signed(usize::MAX as isize), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn as_signed_negative() {
|
||||
assert_eq!(i8 ::as_signed(u8 ::MAX.as_signed()), -1);
|
||||
assert_eq!(i16 ::as_signed(u16 ::MAX.as_signed()), -1);
|
||||
assert_eq!(i32 ::as_signed(u32 ::MAX.as_signed()), -1);
|
||||
assert_eq!(i64 ::as_signed(u64 ::MAX.as_signed()), -1);
|
||||
assert_eq!(i128 ::as_signed(u128 ::MAX.as_signed()), -1);
|
||||
assert_eq!(isize::as_signed(usize::MAX.as_signed()), -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn abs_unsigned_unsigned() {
|
||||
assert_eq!(u8 ::abs_unsigned(1), 1);
|
||||
assert_eq!(u16 ::abs_unsigned(1), 1);
|
||||
assert_eq!(u32 ::abs_unsigned(1), 1);
|
||||
assert_eq!(u64 ::abs_unsigned(1), 1);
|
||||
assert_eq!(u128 ::abs_unsigned(1), 1);
|
||||
assert_eq!(usize::abs_unsigned(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn abs_unsigned_unsigned_big() {
|
||||
assert_eq!(u8 ::abs_unsigned(u8 ::MAX), u8 ::MAX);
|
||||
assert_eq!(u16 ::abs_unsigned(u16 ::MAX), u16 ::MAX);
|
||||
assert_eq!(u32 ::abs_unsigned(u32 ::MAX), u32 ::MAX);
|
||||
assert_eq!(u64 ::abs_unsigned(u64 ::MAX), u64 ::MAX);
|
||||
assert_eq!(u128 ::abs_unsigned(u128 ::MAX), u128 ::MAX);
|
||||
assert_eq!(usize::abs_unsigned(usize::MAX), usize::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn abs_unsigned_signed_positive() {
|
||||
assert_eq!(i8 ::abs_unsigned(1), 1);
|
||||
assert_eq!(i16 ::abs_unsigned(1), 1);
|
||||
assert_eq!(i32 ::abs_unsigned(1), 1);
|
||||
assert_eq!(i64 ::abs_unsigned(1), 1);
|
||||
assert_eq!(i128 ::abs_unsigned(1), 1);
|
||||
assert_eq!(isize::abs_unsigned(1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn abs_unsigned_signed_positive_big() {
|
||||
assert_eq!(i8 ::abs_unsigned(i8 ::MAX), u8 ::MAX / 2);
|
||||
assert_eq!(i16 ::abs_unsigned(i16 ::MAX), u16 ::MAX / 2);
|
||||
assert_eq!(i32 ::abs_unsigned(i32 ::MAX), u32 ::MAX / 2);
|
||||
assert_eq!(i64 ::abs_unsigned(i64 ::MAX), u64 ::MAX / 2);
|
||||
assert_eq!(i128 ::abs_unsigned(i128 ::MAX), u128 ::MAX / 2);
|
||||
assert_eq!(isize::abs_unsigned(isize::MAX), usize::MAX / 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn abs_unsigned_signed_negative() {
|
||||
assert_eq!(i8 ::abs_unsigned(-1), 1);
|
||||
assert_eq!(i16 ::abs_unsigned(-1), 1);
|
||||
assert_eq!(i32 ::abs_unsigned(-1), 1);
|
||||
assert_eq!(i64 ::abs_unsigned(-1), 1);
|
||||
assert_eq!(i128 ::abs_unsigned(-1), 1);
|
||||
assert_eq!(isize::abs_unsigned(-1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[rustfmt::skip]
|
||||
fn abs_unsigned_signed_negative_big() {
|
||||
assert_eq!(i8 ::abs_unsigned(i8 ::MIN), u8 ::MAX / 2 + 1);
|
||||
assert_eq!(i16 ::abs_unsigned(i16 ::MIN), u16 ::MAX / 2 + 1);
|
||||
assert_eq!(i32 ::abs_unsigned(i32 ::MIN), u32 ::MAX / 2 + 1);
|
||||
assert_eq!(i64 ::abs_unsigned(i64 ::MIN), u64 ::MAX / 2 + 1);
|
||||
assert_eq!(i128 ::abs_unsigned(i128 ::MIN), u128 ::MAX / 2 + 1);
|
||||
assert_eq!(isize::abs_unsigned(isize::MIN), usize::MAX / 2 + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user