format: Add Signed<T> <=> signed integer conversions

Co-authored-by: Sebastian Dröge <sebastian@centricular.com>
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1149>
This commit is contained in:
Edward Hervey 2022-11-15 17:20:18 +01:00 committed by Sebastian Dröge
parent 88791294a5
commit 77fd187986
5 changed files with 125 additions and 60 deletions

View file

@ -368,6 +368,7 @@ impl From<ClockTime> for Duration {
impl_common_ops_for_newtype_uint!(ClockTime, u64);
impl_signed_div_mul!(ClockTime, u64);
impl_signed_int_into_signed!(ClockTime, u64);
// rustdoc-stripper-ignore-next
/// Tell [`pad_clocktime`] what kind of time we're formatting
@ -1342,4 +1343,37 @@ mod tests {
fn attempt_to_build_from_u64max() {
let _ = ClockTime::from_nseconds(u64::MAX);
}
#[test]
fn try_into_signed() {
let time = crate::Signed::Positive(ClockTime::from_nseconds(0));
assert_eq!(i64::try_from(time), Ok(0));
let time = crate::Signed::Positive(ClockTime::from_nseconds(123));
assert_eq!(i64::try_from(time), Ok(123));
let time = crate::Signed::Positive(ClockTime::from_nseconds(u64::MAX - 1));
assert!(i64::try_from(time).is_err());
let time = crate::Signed::Positive(ClockTime::from_nseconds(u64::MAX >> 1));
assert_eq!(i64::MAX as i128, (u64::MAX >> 1) as i128);
assert_eq!(i64::try_from(time), Ok(i64::MAX));
let time = crate::Signed::Negative(ClockTime::from_nseconds(0));
assert_eq!(i64::try_from(time), Ok(0));
let time = crate::Signed::Negative(ClockTime::from_nseconds(123));
assert_eq!(i64::try_from(time), Ok(-123));
let time = crate::Signed::Negative(ClockTime::from_nseconds(u64::MAX - 1));
assert!(i64::try_from(time).is_err());
let time = crate::Signed::Negative(ClockTime::from_nseconds(u64::MAX >> 1));
assert_eq!(i64::MIN as i128 + 1, -((u64::MAX >> 1) as i128));
assert_eq!(i64::try_from(time), Ok(i64::MIN + 1));
let time = crate::Signed::Negative(ClockTime::from_nseconds((u64::MAX >> 1) + 1));
assert_eq!(i64::MIN as i128, -(((u64::MAX >> 1) + 1) as i128));
assert_eq!(i64::try_from(time), Ok(i64::MIN));
}
}

View file

@ -52,6 +52,7 @@ impl Other {
impl_common_ops_for_newtype_uint!(Other, u64);
impl_signed_div_mul!(Other, u64);
impl_signed_int_into_signed!(Other, u64);
option_glib_newtype_from_to!(Other, u64::MAX);
glib_newtype_display!(Other, DisplayableOptionOther);

View file

@ -517,6 +517,10 @@ macro_rules! impl_signed_ops(
impl_signed_ops!(u32, u32, 0);
};
(usize) => {
impl_signed_ops!(usize, usize, 0);
};
($typ:ty, $inner:ty, $zero:expr) => {
impl crate::Signed<$typ> {
// rustdoc-stripper-ignore-next
@ -879,6 +883,26 @@ macro_rules! impl_signed_div_mul(
($typ:ty, $inner:ty, $signed_rhs:ty, $into_inner:expr) => {
impl crate::Signed<$typ> {
#[allow(dead_code)]
fn signed_from_inner(val: $inner, sign: $signed_rhs) -> Option<crate::Signed<$typ>> {
skip_assert_initialized!();
if sign.is_positive() {
Self::positive_from_inner(val)
} else {
Self::negative_from_inner(val)
}
}
fn positive_from_inner(val: $inner) -> Option<Self> {
skip_assert_initialized!();
<$typ>::try_from(val).ok().map(crate::Signed::Positive)
}
fn negative_from_inner(val: $inner) -> Option<Self> {
skip_assert_initialized!();
<$typ>::try_from(val).ok().map(crate::Signed::Negative)
}
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn checked_div(self, rhs:$signed_rhs) -> Option<Self> {
use crate::Signed::*;
@ -1274,27 +1298,6 @@ macro_rules! impl_signed_extra_div_mul(
macro_rules! impl_signed_div_mul_trait(
($typ:ty, $inner:ty, $signed_rhs:ty, $into_inner:expr) => {
impl crate::Signed<$typ> {
fn signed_from_inner(val: $inner, sign: $signed_rhs) -> Option<crate::Signed<$typ>> {
skip_assert_initialized!();
if sign.is_positive() {
Self::positive_from_inner(val)
} else {
Self::negative_from_inner(val)
}
}
fn positive_from_inner(val: $inner) -> Option<Self> {
skip_assert_initialized!();
<$typ>::try_from(val).ok().map(crate::Signed::Positive)
}
fn negative_from_inner(val: $inner) -> Option<Self> {
skip_assert_initialized!();
<$typ>::try_from(val).ok().map(crate::Signed::Negative)
}
}
impl muldiv::MulDiv<$signed_rhs> for crate::Signed<$typ> {
type Output = Self;
@ -1628,3 +1631,59 @@ macro_rules! glib_newtype_display {
}
};
}
macro_rules! impl_signed_int_into_signed(
(u64) => {
impl_signed_int_into_signed!(u64, u64, i64, |val: u64| val);
};
(usize) => {
impl_signed_int_into_signed!(usize, usize, isize, |val: usize| val);
};
(u32) => {
impl_signed_int_into_signed!(u32, u32, i32, |val: u32| val);
};
($newtyp:ty, u64) => {
impl_signed_int_into_signed!($newtyp, u64, i64, |val: $newtyp| *val);
};
($newtyp:ty, u32) => {
impl_signed_int_into_signed!($newtyp, u32, i32, |val: $newtyp| *val);
};
($typ:ty, $inner:ty, $signed:ty, $into_inner:expr) => {
impl TryFrom<crate::Signed<$typ>> for $signed {
type Error = std::num::TryFromIntError;
fn try_from(value: crate::Signed<$typ>) -> Result<$signed, Self::Error> {
assert_eq!(::std::mem::size_of::<$inner>(), ::std::mem::size_of::<$signed>());
match value {
crate::Signed::Positive(value) => <$signed>::try_from($into_inner(value)),
crate::Signed::Negative(value) => {
let inner = $into_inner(value);
// `$signed::MIN.abs()` can't be represented as an `$signed`
if inner == (<$inner>::MAX >> 1) + 1 {
Ok(<$signed>::MIN)
} else {
Ok(-<$signed>::try_from(inner)?)
}
},
}
}
}
impl From<$signed> for crate::Signed<$typ> {
fn from(value: $signed) -> crate::Signed<$typ> {
let abs = value.unsigned_abs();
if value.signum() >= 0 {
Self::positive_from_inner(abs).unwrap()
} else {
Self::negative_from_inner(abs).unwrap()
}
}
}
};
);

View file

@ -220,50 +220,17 @@ pub trait UnsignedIntoSigned: Copy + Sized {
impl_unsigned_int_into_signed!(u64);
impl_signed_ops!(u64);
impl_signed_div_mul!(u64);
impl_signed_int_into_signed!(u64);
impl_unsigned_int_into_signed!(u32);
impl_signed_ops!(u32);
impl_signed_div_mul!(u32);
impl_signed_int_into_signed!(u32);
impl From<i64> for Signed<u64> {
fn from(val: i64) -> Signed<u64> {
skip_assert_initialized!();
match val {
positive if positive.is_positive() => Signed::Positive(positive as u64),
i64::MIN => {
// `i64::MIN.abs()` can't be represented as an `i64`
Signed::Negative((-(i64::MIN as i128)) as u64)
}
negative => Signed::Negative((-negative) as u64),
}
}
}
impl From<isize> for Signed<usize> {
fn from(val: isize) -> Signed<usize> {
skip_assert_initialized!();
match val {
positive if positive.is_positive() => Signed::Positive(positive as usize),
isize::MIN => {
// `isize::MIN.abs()` can't be represented as an `isize`
Signed::Negative((-(isize::MIN as i128)) as usize)
}
negative => Signed::Negative((-negative) as usize),
}
}
}
// `i32::MIN.abs()` can't be represented as an `i32`
impl From<i32> for Signed<u32> {
fn from(val: i32) -> Signed<u32> {
skip_assert_initialized!();
if val.is_positive() {
Signed::Positive(val as u32)
} else {
Signed::Negative((-(val as i64)) as u32)
}
}
}
impl_unsigned_int_into_signed!(usize);
impl_signed_ops!(usize);
impl_signed_div_mul!(usize);
impl_signed_int_into_signed!(usize);
pub trait NoneSignedBuilder: FormattedValueNoneBuilder {
type Signed;

View file

@ -60,6 +60,7 @@ impl Buffers {
impl_common_ops_for_newtype_uint!(Buffers, u64);
impl_signed_div_mul!(Buffers, u64);
impl_signed_int_into_signed!(Buffers, u64);
impl_format_value_traits!(Buffers, Buffers, Buffers, u64);
option_glib_newtype_from_to!(Buffers, Buffers::OFFSET_NONE);
glib_newtype_display!(Buffers, DisplayableOptionBuffers, Format::Buffers);
@ -132,6 +133,7 @@ impl Bytes {
impl_common_ops_for_newtype_uint!(Bytes, u64);
impl_signed_div_mul!(Bytes, u64);
impl_signed_int_into_signed!(Bytes, u64);
impl_format_value_traits!(Bytes, Bytes, Bytes, u64);
option_glib_newtype_from_to!(Bytes, u64::MAX);
glib_newtype_display!(Bytes, DisplayableOptionBytes, Format::Bytes);
@ -222,6 +224,7 @@ impl Default {
impl_common_ops_for_newtype_uint!(Default, u64);
impl_signed_div_mul!(Default, u64);
impl_signed_int_into_signed!(Default, u64);
impl_format_value_traits!(Default, Default, Default, u64);
option_glib_newtype_from_to!(Default, u64::MAX);
glib_newtype_display!(Default, DisplayableOptionDefault, Format::Default);
@ -297,6 +300,7 @@ impl Percent {
impl_common_ops_for_newtype_uint!(Percent, u32, one: ffi::GST_FORMAT_PERCENT_SCALE as u32);
impl_signed_div_mul!(Percent, u32);
impl_signed_int_into_signed!(Percent, u32);
impl FormattedValue for Option<Percent> {
type FullRange = Option<Percent>;