mirror of
https://github.com/sile/hls_m3u8.git
synced 2025-01-25 10:48:18 +00:00
fix Float and UFloat
This commit is contained in:
parent
86bb573c97
commit
30e8009af1
2 changed files with 120 additions and 26 deletions
|
@ -12,7 +12,7 @@ use crate::Error;
|
||||||
/// [`NaN`]: core::f32::NAN
|
/// [`NaN`]: core::f32::NAN
|
||||||
/// [`INFINITY`]: core::f32::INFINITY
|
/// [`INFINITY`]: core::f32::INFINITY
|
||||||
/// [`NEG_INFINITY`]: core::f32::NEG_INFINITY
|
/// [`NEG_INFINITY`]: core::f32::NEG_INFINITY
|
||||||
#[derive(Deref, Default, Debug, Copy, Clone, PartialEq, PartialOrd, Display)]
|
#[derive(Deref, Default, Debug, Copy, Clone, Display, PartialOrd)]
|
||||||
pub struct Float(f32);
|
pub struct Float(f32);
|
||||||
|
|
||||||
impl Float {
|
impl Float {
|
||||||
|
@ -95,8 +95,14 @@ macro_rules! implement_from {
|
||||||
|
|
||||||
implement_from!(i16, u16, i8, u8);
|
implement_from!(i16, u16, i8, u8);
|
||||||
|
|
||||||
|
impl PartialEq for Float {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
|
||||||
|
}
|
||||||
|
|
||||||
// convenience implementation to compare f32 with a Float.
|
// convenience implementation to compare f32 with a Float.
|
||||||
impl PartialEq<f32> for Float {
|
impl PartialEq<f32> for Float {
|
||||||
|
#[inline]
|
||||||
fn eq(&self, other: &f32) -> bool { &self.0 == other }
|
fn eq(&self, other: &f32) -> bool { &self.0 == other }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,11 +123,10 @@ impl Eq for Float {}
|
||||||
|
|
||||||
impl Ord for Float {
|
impl Ord for Float {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
if *self < *other {
|
if self.0 < other.0 {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
} else if *self == *other {
|
} else if self == other {
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
} else {
|
} else {
|
||||||
Ordering::Greater
|
Ordering::Greater
|
||||||
|
@ -129,15 +134,43 @@ impl Ord for Float {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The output of Hash cannot be relied upon to be stable. The same version of
|
||||||
|
/// rust can return different values in different architectures. This is not a
|
||||||
|
/// property of the Hasher that you’re using but instead of the way Hash happens
|
||||||
|
/// to be implemented for the type you’re using (e.g., the current
|
||||||
|
/// implementation of Hash for slices of integers returns different values in
|
||||||
|
/// big and little-endian architectures).
|
||||||
|
///
|
||||||
|
/// See <https://internals.rust-lang.org/t/f32-f64-should-implement-hash/5436/33>
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl ::core::hash::Hash for Float {
|
impl ::core::hash::Hash for Float {
|
||||||
fn hash<H>(&self, state: &mut H)
|
fn hash<H>(&self, state: &mut H)
|
||||||
where
|
where
|
||||||
H: ::core::hash::Hasher,
|
H: ::core::hash::Hasher,
|
||||||
{
|
{
|
||||||
// this should be totally fine (definitely not the most
|
// this implementation assumes, that the internal float is:
|
||||||
// efficient implementation as this requires an allocation)
|
// - not NaN
|
||||||
state.write(self.to_string().as_bytes())
|
// - neither negative nor positive infinity
|
||||||
|
|
||||||
|
// to validate those assumptions debug_assertions are here
|
||||||
|
// (those will be removed in a release build)
|
||||||
|
debug_assert!(self.0.is_finite());
|
||||||
|
debug_assert!(!self.0.is_nan());
|
||||||
|
|
||||||
|
// this implementation is based on
|
||||||
|
// https://internals.rust-lang.org/t/f32-f64-should-implement-hash/5436/33
|
||||||
|
//
|
||||||
|
// The important points are:
|
||||||
|
// - NaN == NaN (Float does not allow NaN, so this should be satisfied)
|
||||||
|
// - +0 == -0
|
||||||
|
|
||||||
|
if self.0 == 0.0 || self.0 == -0.0 {
|
||||||
|
state.write(&0.0_f32.to_be_bytes());
|
||||||
|
} else {
|
||||||
|
// I do not think it matters to differentiate between architectures, that use
|
||||||
|
// big endian by default and those, that use little endian.
|
||||||
|
state.write(&self.to_be_bytes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,6 +179,42 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ord() {
|
||||||
|
assert_eq!(Float::new(1.1).cmp(&Float::new(1.1)), Ordering::Equal);
|
||||||
|
assert_eq!(Float::new(1.1).cmp(&Float::new(2.1)), Ordering::Less);
|
||||||
|
assert_eq!(Float::new(1.1).cmp(&Float::new(0.1)), Ordering::Greater);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_partial_ord() {
|
||||||
|
assert_eq!(
|
||||||
|
Float::new(1.1).partial_cmp(&Float::new(1.1)),
|
||||||
|
Some(Ordering::Equal)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Float::new(1.1).partial_cmp(&Float::new(2.1)),
|
||||||
|
Some(Ordering::Less)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Float::new(1.1).partial_cmp(&Float::new(0.1)),
|
||||||
|
Some(Ordering::Greater)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_eq() {
|
||||||
|
struct _AssertEq
|
||||||
|
where
|
||||||
|
Float: Eq;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_partial_eq() {
|
||||||
|
assert_eq!(Float::new(1.0).eq(&Float::new(1.0)), true);
|
||||||
|
assert_eq!(Float::new(1.0).eq(&Float::new(33.3)), false);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_display() {
|
fn test_display() {
|
||||||
assert_eq!(Float::new(22.0).to_string(), "22".to_string());
|
assert_eq!(Float::new(22.0).to_string(), "22".to_string());
|
||||||
|
@ -185,11 +254,6 @@ mod tests {
|
||||||
#[should_panic = "float must not be `NaN`"]
|
#[should_panic = "float must not be `NaN`"]
|
||||||
fn test_new_nan() { Float::new(::core::f32::NAN); }
|
fn test_new_nan() { Float::new(::core::f32::NAN); }
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_partial_eq() {
|
|
||||||
assert_eq!(Float::new(1.1), 1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_as_f32() {
|
fn test_as_f32() {
|
||||||
assert_eq!(Float::new(1.1).as_f32(), 1.1_f32);
|
assert_eq!(Float::new(1.1).as_f32(), 1.1_f32);
|
||||||
|
@ -212,11 +276,4 @@ mod tests {
|
||||||
assert!(Float::try_from(::core::f32::NAN).is_err());
|
assert!(Float::try_from(::core::f32::NAN).is_err());
|
||||||
assert!(Float::try_from(::core::f32::NEG_INFINITY).is_err());
|
assert!(Float::try_from(::core::f32::NEG_INFINITY).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_eq() {
|
|
||||||
struct _AssertEq
|
|
||||||
where
|
|
||||||
Float: Eq;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::Error;
|
||||||
/// [`NaN`]: core::f32::NAN
|
/// [`NaN`]: core::f32::NAN
|
||||||
/// [`INFINITY`]: core::f32::INFINITY
|
/// [`INFINITY`]: core::f32::INFINITY
|
||||||
/// [`NEG_INFINITY`]: core::f32::NEG_INFINITY
|
/// [`NEG_INFINITY`]: core::f32::NEG_INFINITY
|
||||||
#[derive(Deref, Default, Debug, Copy, Clone, PartialEq, PartialOrd, Display)]
|
#[derive(Deref, Default, Debug, Copy, Clone, PartialOrd, Display)]
|
||||||
pub struct UFloat(f32);
|
pub struct UFloat(f32);
|
||||||
|
|
||||||
impl UFloat {
|
impl UFloat {
|
||||||
|
@ -106,8 +106,16 @@ macro_rules! implement_from {
|
||||||
|
|
||||||
implement_from!(u16, u8);
|
implement_from!(u16, u8);
|
||||||
|
|
||||||
|
// This has to be implemented explicitly, because `Hash` is also implemented
|
||||||
|
// manually and both implementations have to agree according to clippy.
|
||||||
|
impl PartialEq for UFloat {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
|
||||||
|
}
|
||||||
|
|
||||||
// convenience implementation to compare f32 with a Float.
|
// convenience implementation to compare f32 with a Float.
|
||||||
impl PartialEq<f32> for UFloat {
|
impl PartialEq<f32> for UFloat {
|
||||||
|
#[inline]
|
||||||
fn eq(&self, other: &f32) -> bool { &self.0 == other }
|
fn eq(&self, other: &f32) -> bool { &self.0 == other }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,11 +136,10 @@ impl Eq for UFloat {}
|
||||||
|
|
||||||
impl Ord for UFloat {
|
impl Ord for UFloat {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
if *self < *other {
|
if self.0 < other.0 {
|
||||||
Ordering::Less
|
Ordering::Less
|
||||||
} else if *self == *other {
|
} else if self == other {
|
||||||
Ordering::Equal
|
Ordering::Equal
|
||||||
} else {
|
} else {
|
||||||
Ordering::Greater
|
Ordering::Greater
|
||||||
|
@ -140,15 +147,41 @@ impl Ord for UFloat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The output of Hash cannot be relied upon to be stable. The same version of
|
||||||
|
/// rust can return different values in different architectures. This is not a
|
||||||
|
/// property of the Hasher that you’re using but instead of the way Hash happens
|
||||||
|
/// to be implemented for the type you’re using (e.g., the current
|
||||||
|
/// implementation of Hash for slices of integers returns different values in
|
||||||
|
/// big and little-endian architectures).
|
||||||
|
///
|
||||||
|
/// See <https://internals.rust-lang.org/t/f32-f64-should-implement-hash/5436/33>
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
impl ::core::hash::Hash for UFloat {
|
impl ::core::hash::Hash for UFloat {
|
||||||
fn hash<H>(&self, state: &mut H)
|
fn hash<H>(&self, state: &mut H)
|
||||||
where
|
where
|
||||||
H: ::core::hash::Hasher,
|
H: ::core::hash::Hasher,
|
||||||
{
|
{
|
||||||
// this should be totally fine (definitely not the most
|
// this implementation assumes, that the internal float is:
|
||||||
// efficient implementation as this requires an allocation)
|
// - positive
|
||||||
state.write(self.to_string().as_bytes())
|
// - not NaN
|
||||||
|
// - neither negative nor positive infinity
|
||||||
|
|
||||||
|
// to validate those assumptions debug_assertions are here
|
||||||
|
// (those will be removed in a release build)
|
||||||
|
debug_assert!(self.0.is_sign_positive());
|
||||||
|
debug_assert!(self.0.is_finite());
|
||||||
|
debug_assert!(!self.0.is_nan());
|
||||||
|
|
||||||
|
// this implementation is based on
|
||||||
|
// https://internals.rust-lang.org/t/f32-f64-should-implement-hash/5436/33
|
||||||
|
//
|
||||||
|
// The important points are:
|
||||||
|
// - NaN == NaN (UFloat does not allow NaN, so this should be satisfied)
|
||||||
|
// - +0 != -0 (UFloat does not allow negative numbers, so this is fine too)
|
||||||
|
|
||||||
|
// I do not think it matters to differentiate between architectures, that use
|
||||||
|
// big endian by default and those, that use little endian.
|
||||||
|
state.write(&self.to_be_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +217,10 @@ mod tests {
|
||||||
#[should_panic = "float must be positive: `-1.1`"]
|
#[should_panic = "float must be positive: `-1.1`"]
|
||||||
fn test_new_negative() { UFloat::new(-1.1); }
|
fn test_new_negative() { UFloat::new(-1.1); }
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic = "float must be positive: `0`"]
|
||||||
|
fn test_new_negative_zero() { UFloat::new(-0.0); }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic = "float must be finite: `inf`"]
|
#[should_panic = "float must be finite: `inf`"]
|
||||||
fn test_new_infinite() { UFloat::new(::core::f32::INFINITY); }
|
fn test_new_infinite() { UFloat::new(::core::f32::INFINITY); }
|
||||||
|
|
Loading…
Reference in a new issue