use std::{i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64};
use std::fmt::{self, Display, Formatter};
use std::mem;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ClampError {
Nan,
Min,
Max,
}
impl ClampError {
fn as_str (self) -> &'static str {
match self {
ClampError::Nan => "value is not a number",
ClampError::Min => "value is below clamped range",
ClampError::Max => "value is above clamped range",
}
}
}
impl Display for ClampError {
fn fmt (&self, f: &mut Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(feature = "std")]
impl ::std::error::Error for ClampError {
fn description (&self) -> &str { self.as_str() }
}
pub trait Clamp: Sized + PartialOrd {
fn clamp_min () -> Self;
fn clamp_max () -> Self;
fn clamp (self) -> Self {
let min = Self::clamp_min();
if self.is_clamp_nan() || self < min {
return min;
}
let max = Self::clamp_max();
if self > max {
max
} else {
self
}
}
fn is_clamp_nan (&self) -> bool;
}
macro_rules! impl_clamp_int {
($($ty:ident),*) => { $(
impl Clamp for $ty {
#[inline(always)]
fn clamp_min () -> $ty { 0 }
#[inline(always)]
fn clamp_max () -> $ty { $ty::MAX }
#[inline(always)]
fn clamp (self) -> $ty { if self < 0 { 0 } else { self }}
#[inline(always)]
fn is_clamp_nan (&self) -> bool { false }
}
)* };
}
macro_rules! impl_clamp_uint {
($($ty:ident),*) => { $(
impl Clamp for $ty {
#[inline(always)]
fn clamp_min () -> $ty { 0 }
#[inline(always)]
fn clamp_max () -> $ty { $ty::MAX }
#[inline(always)]
fn clamp (self) -> $ty { self }
#[inline(always)]
fn is_clamp_nan (&self) -> bool { false }
}
)* };
}
macro_rules! impl_clamp_float {
($($ty:ident),*) => { $(
impl Clamp for $ty {
#[inline(always)]
fn clamp_min () -> $ty { 0.0 }
#[inline(always)]
fn clamp_max () -> $ty { 1.0 }
#[inline(always)]
fn is_clamp_nan (&self) -> bool { self.is_nan() }
}
)* };
}
impl_clamp_int!(i8, i16, i32, i64, isize);
impl_clamp_uint!(u8, u16, u32, u64, usize);
impl_clamp_float!(f32, f64);
pub trait ClampFrom<F: Sized>: Sized {
fn clamp_from (other: F) -> Self;
fn saturating_clamp_from (other: F) -> Self;
fn try_clamp_from (other: F) -> Result<Self, ClampError>;
}
impl<T: Clamp> ClampFrom<T> for T {
#[inline(always)]
fn clamp_from (other: T) -> T { other }
fn saturating_clamp_from (other: T) -> T {
let min = T::clamp_min();
if other.is_clamp_nan() || other < min {
return min;
}
let max = T::clamp_max();
if other > max {
max
} else {
other
}
}
#[inline(always)]
fn try_clamp_from (other: T) -> Result<T, ClampError> {
if other.is_clamp_nan() {
Err(ClampError::Nan)
} else if other < T::clamp_min() {
Err(ClampError::Min)
} else if other > T::clamp_max() {
Err(ClampError::Max)
} else {
Ok(other)
}
}
}
macro_rules! impl_clamp_from_int_to_float {
{ $($f:ident => $t:ty,)* } => { $(
impl ClampFrom<$f> for $t {
#[inline(always)]
fn clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
fn saturating_clamp_from (other: $f) -> $t {
if other < 0 {
0.0
} else {
other as $t / $f::MAX as $t
}
}
fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
if other < 0 {
Err(ClampError::Min)
} else {
Ok(other as $t / $f::MAX as $t)
}
}
}
)* };
}
macro_rules! impl_clamp_from_uint_to_float {
{ $($f:ident => $t:ty,)* } => { $(
impl ClampFrom<$f> for $t {
#[inline(always)]
fn clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
#[inline(always)]
fn saturating_clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
#[inline(always)]
fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
Ok(other as $t / $f::MAX as $t)
}
}
)* };
}
macro_rules! impl_clamp_from_float_to_int {
{ $($f:ty => $t:ident,)* } => { $(
impl ClampFrom<$f> for $t {
#[inline(always)]
fn clamp_from (other: $f) -> $t { (other * $t::MAX as $f) as $t }
fn saturating_clamp_from (other: $f) -> $t {
if other.is_clamp_nan() || other < 0.0 {
0
} else if other > 1.0 {
$t::MAX
} else {
(other * $t::MAX as $f) as $t
}
}
fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
if other.is_clamp_nan() {
Err(ClampError::Nan)
} else if other < 0.0 {
Err(ClampError::Min)
} else if other > 1.0 {
Err(ClampError::Max)
} else {
Ok((other * $t::MAX as $f) as $t)
}
}
}
)* };
}
macro_rules! impl_clamp_from_float_to_float {
{ $($f:ty => $t:ty,)* } => { $(
impl ClampFrom<$f> for $t {
#[inline(always)]
fn clamp_from (other: $f) -> $t { other as $t }
fn saturating_clamp_from (other: $f) -> $t {
if other.is_clamp_nan() || other < 0.0 {
0.0
} else if other > 1.0 {
1.0
} else {
other as $t
}
}
fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
if other.is_clamp_nan() {
Err(ClampError::Nan)
} else if other < 0.0 {
Err(ClampError::Min)
} else if other > 1.0 {
Err(ClampError::Max)
} else {
Ok(other as $t)
}
}
}
)* };
}
macro_rules! impl_clamp_from_uint_expand {
{ $($f:ident($mul:expr) => $t:ty,)* } => { $(
impl ClampFrom<$f> for $t {
#[inline(always)]
fn clamp_from (other: $f) -> $t { other as $t * ($mul) }
#[inline(always)]
fn saturating_clamp_from (other: $f) -> $t { other as $t * ($mul) }
#[inline(always)]
fn try_clamp_from (other: $f) -> Result<$t, ClampError> { Ok(other as $t * ($mul)) }
}
)* };
}
macro_rules! impl_clamp_from_uint_shrink {
{ $($f:ident($shift:expr) => $t:ty,)* } => { $(
impl ClampFrom<$f> for $t {
#[inline(always)]
fn clamp_from (other: $f) -> $t { (other >> ($shift)) as $t }
#[inline(always)]
fn saturating_clamp_from (other: $f) -> $t { (other >> ($shift)) as $t }
#[inline(always)]
fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
Ok((other >> ($shift)) as $t)
}
}
)* };
}
impl_clamp_from_int_to_float! {
i8 => f32,
i8 => f64,
i16 => f32,
i16 => f64,
i32 => f32,
i32 => f64,
i64 => f32,
i64 => f64,
isize => f32,
isize => f64,
}
impl_clamp_from_uint_to_float! {
u8 => f32,
u8 => f64,
u16 => f32,
u16 => f64,
u32 => f32,
u32 => f64,
u64 => f32,
u64 => f64,
usize => f32,
usize => f64,
}
impl_clamp_from_float_to_int! {
f32 => i8,
f32 => i16,
f32 => i32,
f32 => i64,
f32 => isize,
f32 => u8,
f32 => u16,
f32 => u32,
f32 => u64,
f32 => usize,
f64 => i8,
f64 => i16,
f64 => i32,
f64 => i64,
f64 => isize,
f64 => u8,
f64 => u16,
f64 => u32,
f64 => u64,
f64 => usize,
}
impl_clamp_from_float_to_float! {
f32 => f64,
f64 => f32,
}
impl_clamp_from_uint_expand! {
u8(0x0101) => u16,
u8(0x0101_0101) => u32,
u8(0x0101_0101_0101_0101) => u64,
u8(0x0101_0101_0101_0101u64 as usize) => usize,
u16(0x0001_0001) => u32,
u16(0x0001_0001_0001_0001) => u64,
u16(0x0001_0001_0001_0001u64 as usize) => usize,
u32(0x0000_0001_0000_0001) => u64,
u32(0x0000_0001_0000_0001u64 as usize) => usize,
usize((usize::MAX as u64).wrapping_add(2)) => u64,
}
impl_clamp_from_uint_shrink! {
u16(8) => u8,
u32(24) => u8,
u32(16) => u16,
u64(56) => u8,
u64(48) => u16,
u64(32) => u32,
u64(64 - mem::size_of::<usize>() * 8) => usize,
usize(mem::size_of::<usize>() * 8 - 8) => u8,
usize(mem::size_of::<usize>() * 8 - 16) => u16,
usize(mem::size_of::<usize>() * 8 - 32) => u32,
}
pub trait ClampInto<T: Sized> : Sized {
fn clamp_into (self) -> T;
fn saturating_clamp_into (self) -> T;
fn try_clamp_into (self) -> Result<T, ClampError>;
}
impl<F: Sized, T: ClampFrom<F>> ClampInto<T> for F {
#[inline(always)]
fn clamp_into (self) -> T { T::clamp_from(self) }
#[inline(always)]
fn saturating_clamp_into (self) -> T { T::saturating_clamp_from(self) }
#[inline(always)]
fn try_clamp_into (self) -> Result<T, ClampError> {
T::try_clamp_from(self)
}
}