Skip to content

Commit

Permalink
Fix try_from_*_bytes on the empty interval (#49)
Browse files Browse the repository at this point in the history
* Fix `try_from_*_bytes` on the empty interval

* Clippy
  • Loading branch information
unageek committed May 9, 2021
1 parent 6d47f12 commit 185de67
Showing 1 changed file with 104 additions and 80 deletions.
184 changes: 104 additions & 80 deletions src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,33 @@ use crate::interval::*;
use std::convert::TryFrom;

macro_rules! impl_to_bytes {
($(#[$meta:meta])* $to_xe_bytes:ident) => {
($(#[$meta:meta])* $to_bytes:ident) => {
$(#[$meta])*
pub fn $to_xe_bytes(self) -> [u8; 16] {
pub fn $to_bytes(self) -> [u8; 16] {
let mut bytes = [0u8; 16];
bytes[..8].copy_from_slice(&f64::$to_xe_bytes(self.inf()));
bytes[8..16].copy_from_slice(&f64::$to_xe_bytes(self.sup()));
bytes[..8].copy_from_slice(&f64::$to_bytes(self.inf()));
bytes[8..16].copy_from_slice(&f64::$to_bytes(self.sup()));
bytes
}
};
}

macro_rules! impl_try_from_bytes {
($(#[$meta:meta])* $try_from_xe_bytes:ident, $from_xe_bytes:ident) => {
($(#[$meta:meta])* $try_from_bytes:ident, $from_bytes:ident) => {
$(#[$meta])*
pub fn $try_from_xe_bytes(bytes: [u8; 16]) -> Result<Self> {
let a = f64::$from_xe_bytes(<[u8; 8]>::try_from(&bytes[..8]).unwrap());
let b = f64::$from_xe_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());
pub fn $try_from_bytes(bytes: [u8; 16]) -> Result<Self> {
let a = f64::$from_bytes(<[u8; 8]>::try_from(&bytes[..8]).unwrap());
let b = f64::$from_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());

if a <= b
&& a != f64::INFINITY
&& b != f64::NEG_INFINITY
&& (a != 0.0 || a.is_sign_negative())
&& (b != 0.0 || b.is_sign_positive())
{
Ok(Self::with_infsup_raw(a, b))
} else if a == f64::INFINITY && b == f64::NEG_INFINITY {
Ok(Self::EMPTY)
} else {
Err(IntervalError {
kind: IntervalErrorKind::UndefinedOperation,
Expand All @@ -38,72 +41,79 @@ macro_rules! impl_try_from_bytes {

impl Interval {
impl_to_bytes!(
/// Returns the big-endian interchange representation of `self`.
/// Returns the interchange representation of the interval in the big-endian byte order.
to_be_bytes
);
impl_to_bytes!(
/// Returns the little-endian interchange representation of `self`.
/// Returns the interchange representation of the interval in the little-endian byte order.
to_le_bytes
);
impl_to_bytes!(
/// Returns the native-byte-order interchange representation of `self`.
/// Returns the interchange representation of the interval in the target platform's native byte order.
to_ne_bytes
);

impl_try_from_bytes!(
/// Creates an interval from its big-endian interchange representation.
/// Creates an interval from its interchange representation in the big-endian byte order.
try_from_be_bytes,
from_be_bytes
);
impl_try_from_bytes!(
/// Creates an interval from its little-endian interchange representation.
/// Creates an interval from its interchange representation in the little-endian byte order.
try_from_le_bytes,
from_le_bytes
);
impl_try_from_bytes!(
/// Creates an interval from its native-byte-order interchange representation.
/// Creates an interval from its interchange representation in the target platform's native byte order.
try_from_ne_bytes,
from_ne_bytes
);
}

macro_rules! impl_to_bytes {
($to_xe_bytes:ident) => {
pub fn $to_xe_bytes(self) -> [u8; 17] {
($to_bytes:ident) => {
pub fn $to_bytes(self) -> [u8; 17] {
let mut bytes = [0u8; 17];
bytes[..8].copy_from_slice(&f64::$to_xe_bytes(self.inf()));
bytes[8..16].copy_from_slice(&f64::$to_xe_bytes(self.sup()));
bytes[..8].copy_from_slice(&f64::$to_bytes(self.inf()));
bytes[8..16].copy_from_slice(&f64::$to_bytes(self.sup()));
bytes[16] = self.d as u8;
bytes
}
};
}

macro_rules! impl_try_from_bytes {
($try_from_xe_bytes:ident, $from_xe_bytes:ident) => {
pub fn $try_from_xe_bytes(bytes: [u8; 17]) -> Result<Self> {
let a = f64::$from_xe_bytes(<[u8; 8]>::try_from(&bytes[..8]).unwrap());
let b = f64::$from_xe_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());

if a.is_nan() && b.is_nan() && bytes[16] == 0 {
return Ok(Self::NAI);
}
($try_from_bytes:ident, $from_bytes:ident) => {
pub fn $try_from_bytes(bytes: [u8; 17]) -> Result<Self> {
use Decoration::*;
let a = f64::$from_bytes(<[u8; 8]>::try_from(&bytes[..8]).unwrap());
let b = f64::$from_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());
let dec = match bytes[16] {
0 => Ill,
4 => Trv,
8 => Def,
12 => Dac,
16 => Com,
_ => {
return Err(IntervalError {
kind: IntervalErrorKind::UndefinedOperation,
value: Self::NAI,
})
}
};

if let (true, Some(d)) = (
a <= b
&& a != f64::INFINITY
&& b != f64::NEG_INFINITY
&& (a != 0.0 || a.is_sign_negative())
&& (b != 0.0 || b.is_sign_positive()),
match bytes[16] {
4 => Some(Decoration::Trv),
8 => Some(Decoration::Def),
12 => Some(Decoration::Dac),
16 if a != f64::NEG_INFINITY && b != f64::INFINITY => Some(Decoration::Com),
_ => None,
},
) {
Ok(Self::new_unchecked(Interval::with_infsup_raw(a, b), d))
if a <= b
&& a != f64::INFINITY
&& b != f64::NEG_INFINITY
&& (a != 0.0 || a.is_sign_negative())
&& (b != 0.0 || b.is_sign_positive())
&& (dec != Com || a != f64::NEG_INFINITY && b != f64::INFINITY)
{
Ok(Self::new_unchecked(Interval::with_infsup_raw(a, b), dec))
} else if a == f64::INFINITY && b == f64::NEG_INFINITY && dec == Trv {
Ok(Self::EMPTY)
} else if a.is_nan() && b.is_nan() && dec == Ill {
Ok(Self::NAI)
} else {
Err(IntervalError {
kind: IntervalErrorKind::UndefinedOperation,
Expand All @@ -128,20 +138,30 @@ impl DecInterval {
mod tests {
use crate::*;
use DecInterval as DI;
use Decoration as D;
use Decoration::*;
use Interval as I;

#[test]
fn interval() {
fn test_roundtrip(x: I) {
assert_eq!(I::try_from_be_bytes(x.to_be_bytes()).unwrap(), x);
assert_eq!(I::try_from_le_bytes(x.to_le_bytes()).unwrap(), x);
assert_eq!(I::try_from_ne_bytes(x.to_ne_bytes()).unwrap(), x);
#[allow(clippy::type_complexity)]
let fs: [(fn(_) -> _, fn(_) -> _); 3] = [
(I::try_from_be_bytes, I::to_be_bytes),
(I::try_from_le_bytes, I::to_le_bytes),
(I::try_from_ne_bytes, I::to_ne_bytes),
];
for (try_from_bytes, to_bytes) in fs {
let y = try_from_bytes(to_bytes(x)).unwrap();
assert_eq!(x, y);
}
}

test_roundtrip(const_interval!(-0.0, 0.0));
test_roundtrip(const_interval!(-1.0, 3.0));
test_roundtrip(const_interval!(f64::NEG_INFINITY, f64::INFINITY));
test_roundtrip(const_interval!(0.0, 0.0));
test_roundtrip(const_interval!(-2.0, 3.0));
test_roundtrip(const_interval!(f64::NEG_INFINITY, 3.0));
test_roundtrip(const_interval!(-2.0, f64::INFINITY));
test_roundtrip(Interval::EMPTY);
test_roundtrip(Interval::ENTIRE);

fn make_bytes(a: f64, b: f64) -> [u8; 16] {
let mut bytes = [0u8; 16];
Expand All @@ -150,7 +170,7 @@ mod tests {
bytes
}

assert!(I::try_from_ne_bytes(make_bytes(3.0, -1.0)).is_err());
assert!(I::try_from_ne_bytes(make_bytes(3.0, -2.0)).is_err());
assert!(I::try_from_ne_bytes(make_bytes(f64::INFINITY, f64::INFINITY)).is_err());
assert!(I::try_from_ne_bytes(make_bytes(f64::NEG_INFINITY, f64::NEG_INFINITY)).is_err());
assert!(I::try_from_ne_bytes(make_bytes(0.0, 0.0)).is_err());
Expand All @@ -160,51 +180,55 @@ mod tests {
#[test]
fn dec_interval() {
fn test_roundtrip(x: DI) {
let y = DI::try_from_be_bytes(x.to_be_bytes()).unwrap();
assert!(x.is_nai() && y.is_nai() || x == y);
let y = DI::try_from_le_bytes(x.to_le_bytes()).unwrap();
assert!(x.is_nai() && y.is_nai() || x == y);
let y = DI::try_from_ne_bytes(x.to_ne_bytes()).unwrap();
assert!(x.is_nai() && y.is_nai() || x == y);
#[allow(clippy::type_complexity)]
let fs: [(fn(_) -> _, fn(_) -> _); 3] = [
(DI::try_from_be_bytes, DI::to_be_bytes),
(DI::try_from_le_bytes, DI::to_le_bytes),
(DI::try_from_ne_bytes, DI::to_ne_bytes),
];
for (try_from_bytes, to_bytes) in fs {
if x.is_nai() {
let y = try_from_bytes(to_bytes(x)).unwrap();
assert!(y.is_nai());
} else {
for dec in [Com, Dac, Def, Trv] {
let x = DecInterval::set_dec(x.interval().unwrap(), dec);
let y = try_from_bytes(to_bytes(x)).unwrap();
assert_eq!(x, y);
assert_eq!(x.decoration(), y.decoration());
}
}
}
}

test_roundtrip(const_dec_interval!(-0.0, 0.0));
test_roundtrip(const_dec_interval!(-1.0, 3.0));
test_roundtrip(DI::set_dec(
const_interval!(f64::NEG_INFINITY, f64::INFINITY),
D::Trv,
));
test_roundtrip(DI::set_dec(
const_interval!(f64::NEG_INFINITY, f64::INFINITY),
D::Def,
));
test_roundtrip(DI::set_dec(
const_interval!(f64::NEG_INFINITY, f64::INFINITY),
D::Dac,
));
test_roundtrip(const_dec_interval!(-2.0, 3.0));
test_roundtrip(const_dec_interval!(f64::NEG_INFINITY, 3.0));
test_roundtrip(const_dec_interval!(-2.0, f64::INFINITY));
test_roundtrip(DI::EMPTY);
test_roundtrip(DI::ENTIRE);
test_roundtrip(DI::NAI);

fn make_bytes(a: f64, b: f64, d: D) -> [u8; 17] {
fn make_bytes(a: f64, b: f64, dec: Decoration) -> [u8; 17] {
let mut bytes = [0u8; 17];
bytes[..8].copy_from_slice(&f64::to_ne_bytes(a));
bytes[8..16].copy_from_slice(&f64::to_ne_bytes(b));
bytes[16] = d as u8;
bytes[16] = dec as u8;
bytes
}

assert!(DI::try_from_ne_bytes(make_bytes(3.0, -1.0, D::Trv)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(f64::INFINITY, f64::INFINITY, D::Trv)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(3.0, -2.0, Trv)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(f64::INFINITY, f64::INFINITY, Trv)).is_err());
assert!(
DI::try_from_ne_bytes(make_bytes(f64::NEG_INFINITY, f64::NEG_INFINITY, D::Trv))
.is_err()
DI::try_from_ne_bytes(make_bytes(f64::NEG_INFINITY, f64::NEG_INFINITY, Trv)).is_err()
);
assert!(DI::try_from_ne_bytes(make_bytes(0.0, 0.0, D::Trv)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(-0.0, -0.0, D::Trv)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(0.0, 0.0, Trv)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(-0.0, -0.0, Trv)).is_err());

assert!(DI::try_from_ne_bytes(make_bytes(-1.0, f64::NAN, D::Ill)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(f64::NAN, 3.0, D::Ill)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(-2.0, f64::NAN, Ill)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(f64::NAN, 3.0, Ill)).is_err());

assert!(DI::try_from_ne_bytes(make_bytes(f64::NEG_INFINITY, 3.0, D::Com)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(-1.0, f64::INFINITY, D::Com)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(f64::NEG_INFINITY, 3.0, Com)).is_err());
assert!(DI::try_from_ne_bytes(make_bytes(-2.0, f64::INFINITY, Com)).is_err());
}
}

0 comments on commit 185de67

Please sign in to comment.