1use std::{i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64};
6use std::fmt::{self, Display, Formatter};
7use std::mem;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum ClampError {
12 Nan,
13 Min,
14 Max,
15}
16
17impl ClampError {
18 fn as_str (self) -> &'static str {
19 match self {
20 ClampError::Nan => "value is not a number",
21 ClampError::Min => "value is below clamped range",
22 ClampError::Max => "value is above clamped range",
23 }
24 }
25}
26
27impl Display for ClampError {
28 fn fmt (&self, f: &mut Formatter) -> fmt::Result {
29 f.write_str(self.as_str())
30 }
31}
32
33#[cfg(feature = "std")]
34impl ::std::error::Error for ClampError {
35 fn description (&self) -> &str { self.as_str() }
36}
37
38pub trait Clamp: Sized + PartialOrd {
43 fn clamp_min () -> Self;
44 fn clamp_max () -> Self;
45
46 fn clamp (self) -> Self {
47 let min = Self::clamp_min();
48
49 if self.is_clamp_nan() || self < min {
50 return min;
51 }
52
53 let max = Self::clamp_max();
54
55 if self > max {
56 max
57 } else {
58 self
59 }
60 }
61
62 fn is_clamp_nan (&self) -> bool;
63}
64
65macro_rules! impl_clamp_int {
66 ($($ty:ident),*) => { $(
67 impl Clamp for $ty {
68 #[inline(always)]
69 fn clamp_min () -> $ty { 0 }
70
71 #[inline(always)]
72 fn clamp_max () -> $ty { $ty::MAX }
73
74 #[inline(always)]
75 fn clamp (self) -> $ty { if self < 0 { 0 } else { self }}
76
77 #[inline(always)]
78 fn is_clamp_nan (&self) -> bool { false }
79 }
80 )* };
81}
82
83macro_rules! impl_clamp_uint {
84 ($($ty:ident),*) => { $(
85 impl Clamp for $ty {
86 #[inline(always)]
87 fn clamp_min () -> $ty { 0 }
88
89 #[inline(always)]
90 fn clamp_max () -> $ty { $ty::MAX }
91
92 #[inline(always)]
93 fn clamp (self) -> $ty { self }
94
95 #[inline(always)]
96 fn is_clamp_nan (&self) -> bool { false }
97 }
98 )* };
99}
100
101macro_rules! impl_clamp_float {
102 ($($ty:ident),*) => { $(
103 impl Clamp for $ty {
104 #[inline(always)]
105 fn clamp_min () -> $ty { 0.0 }
106
107 #[inline(always)]
108 fn clamp_max () -> $ty { 1.0 }
109
110 #[inline(always)]
111 fn is_clamp_nan (&self) -> bool { self.is_nan() }
112 }
113 )* };
114}
115
116impl_clamp_int!(i8, i16, i32, i64, isize);
117impl_clamp_uint!(u8, u16, u32, u64, usize);
118impl_clamp_float!(f32, f64);
119
120pub trait ClampFrom<F: Sized>: Sized {
122 fn clamp_from (other: F) -> Self;
123 fn saturating_clamp_from (other: F) -> Self;
124 fn try_clamp_from (other: F) -> Result<Self, ClampError>;
125}
126
127impl<T: Clamp> ClampFrom<T> for T {
128 #[inline(always)]
129 fn clamp_from (other: T) -> T { other }
130
131 fn saturating_clamp_from (other: T) -> T {
132 let min = T::clamp_min();
133
134 if other.is_clamp_nan() || other < min {
135 return min;
136 }
137
138 let max = T::clamp_max();
139
140 if other > max {
141 max
142 } else {
143 other
144 }
145 }
146
147 #[inline(always)]
148 fn try_clamp_from (other: T) -> Result<T, ClampError> {
149 if other.is_clamp_nan() {
150 Err(ClampError::Nan)
151 } else if other < T::clamp_min() {
152 Err(ClampError::Min)
153 } else if other > T::clamp_max() {
154 Err(ClampError::Max)
155 } else {
156 Ok(other)
157 }
158 }
159}
160
161macro_rules! impl_clamp_from_int_to_float {
162 { $($f:ident => $t:ty,)* } => { $(
163 impl ClampFrom<$f> for $t {
164 #[inline(always)]
165 fn clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
166
167 fn saturating_clamp_from (other: $f) -> $t {
168 if other < 0 {
169 0.0
170 } else {
171 other as $t / $f::MAX as $t
172 }
173 }
174
175 fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
176 if other < 0 {
177 Err(ClampError::Min)
178 } else {
179 Ok(other as $t / $f::MAX as $t)
180 }
181 }
182 }
183 )* };
184}
185
186macro_rules! impl_clamp_from_uint_to_float {
187 { $($f:ident => $t:ty,)* } => { $(
188 impl ClampFrom<$f> for $t {
189 #[inline(always)]
190 fn clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
191
192 #[inline(always)]
193 fn saturating_clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
194
195 #[inline(always)]
196 fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
197 Ok(other as $t / $f::MAX as $t)
198 }
199 }
200 )* };
201}
202
203macro_rules! impl_clamp_from_float_to_int {
204 { $($f:ty => $t:ident,)* } => { $(
205 impl ClampFrom<$f> for $t {
206 #[inline(always)]
207 fn clamp_from (other: $f) -> $t { (other * $t::MAX as $f) as $t }
208
209 fn saturating_clamp_from (other: $f) -> $t {
210 if other.is_clamp_nan() || other < 0.0 {
211 0
212 } else if other > 1.0 {
213 $t::MAX
214 } else {
215 (other * $t::MAX as $f) as $t
216 }
217 }
218
219 fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
220 if other.is_clamp_nan() {
221 Err(ClampError::Nan)
222 } else if other < 0.0 {
223 Err(ClampError::Min)
224 } else if other > 1.0 {
225 Err(ClampError::Max)
226 } else {
227 Ok((other * $t::MAX as $f) as $t)
228 }
229 }
230 }
231 )* };
232}
233
234macro_rules! impl_clamp_from_float_to_float {
235 { $($f:ty => $t:ty,)* } => { $(
236 impl ClampFrom<$f> for $t {
237 #[inline(always)]
238 fn clamp_from (other: $f) -> $t { other as $t }
239
240 fn saturating_clamp_from (other: $f) -> $t {
241 if other.is_clamp_nan() || other < 0.0 {
242 0.0
243 } else if other > 1.0 {
244 1.0
245 } else {
246 other as $t
247 }
248 }
249
250 fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
251 if other.is_clamp_nan() {
252 Err(ClampError::Nan)
253 } else if other < 0.0 {
254 Err(ClampError::Min)
255 } else if other > 1.0 {
256 Err(ClampError::Max)
257 } else {
258 Ok(other as $t)
259 }
260 }
261 }
262 )* };
263}
264
265macro_rules! impl_clamp_from_uint_expand {
266 { $($f:ident($mul:expr) => $t:ty,)* } => { $(
267 impl ClampFrom<$f> for $t {
268 #[inline(always)]
269 fn clamp_from (other: $f) -> $t { other as $t * ($mul) }
270
271 #[inline(always)]
272 fn saturating_clamp_from (other: $f) -> $t { other as $t * ($mul) }
273
274 #[inline(always)]
275 fn try_clamp_from (other: $f) -> Result<$t, ClampError> { Ok(other as $t * ($mul)) }
276 }
277 )* };
278}
279
280macro_rules! impl_clamp_from_uint_shrink {
281 { $($f:ident($shift:expr) => $t:ty,)* } => { $(
282 impl ClampFrom<$f> for $t {
283 #[inline(always)]
284 fn clamp_from (other: $f) -> $t { (other >> ($shift)) as $t }
285
286 #[inline(always)]
287 fn saturating_clamp_from (other: $f) -> $t { (other >> ($shift)) as $t }
288
289 #[inline(always)]
290 fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
291 Ok((other >> ($shift)) as $t)
292 }
293 }
294 )* };
295}
296
297impl_clamp_from_int_to_float! {
298 i8 => f32,
299 i8 => f64,
300 i16 => f32,
301 i16 => f64,
302 i32 => f32,
303 i32 => f64,
304 i64 => f32,
305 i64 => f64,
306 isize => f32,
307 isize => f64,
308}
309
310impl_clamp_from_uint_to_float! {
311 u8 => f32,
312 u8 => f64,
313 u16 => f32,
314 u16 => f64,
315 u32 => f32,
316 u32 => f64,
317 u64 => f32,
318 u64 => f64,
319 usize => f32,
320 usize => f64,
321}
322
323impl_clamp_from_float_to_int! {
324 f32 => i8,
325 f32 => i16,
326 f32 => i32,
327 f32 => i64,
328 f32 => isize,
329 f32 => u8,
330 f32 => u16,
331 f32 => u32,
332 f32 => u64,
333 f32 => usize,
334 f64 => i8,
335 f64 => i16,
336 f64 => i32,
337 f64 => i64,
338 f64 => isize,
339 f64 => u8,
340 f64 => u16,
341 f64 => u32,
342 f64 => u64,
343 f64 => usize,
344}
345
346impl_clamp_from_float_to_float! {
347 f32 => f64,
348 f64 => f32,
349}
350
351impl_clamp_from_uint_expand! {
355 u8(0x0101) => u16,
356 u8(0x0101_0101) => u32,
357 u8(0x0101_0101_0101_0101) => u64,
358 u8(0x0101_0101_0101_0101u64 as usize) => usize,
359 u16(0x0001_0001) => u32,
360 u16(0x0001_0001_0001_0001) => u64,
361 u16(0x0001_0001_0001_0001u64 as usize) => usize,
362 u32(0x0000_0001_0000_0001) => u64,
363 u32(0x0000_0001_0000_0001u64 as usize) => usize,
364 usize((usize::MAX as u64).wrapping_add(2)) => u64,
365}
366
367impl_clamp_from_uint_shrink! {
368 u16(8) => u8,
369 u32(24) => u8,
370 u32(16) => u16,
371 u64(56) => u8,
372 u64(48) => u16,
373 u64(32) => u32,
374 u64(64 - mem::size_of::<usize>() * 8) => usize,
375 usize(mem::size_of::<usize>() * 8 - 8) => u8,
376 usize(mem::size_of::<usize>() * 8 - 16) => u16,
377 usize(mem::size_of::<usize>() * 8 - 32) => u32,
378}
379
380pub trait ClampInto<T: Sized> : Sized {
382 fn clamp_into (self) -> T;
383 fn saturating_clamp_into (self) -> T;
384 fn try_clamp_into (self) -> Result<T, ClampError>;
385}
386
387impl<F: Sized, T: ClampFrom<F>> ClampInto<T> for F {
388 #[inline(always)]
389 fn clamp_into (self) -> T { T::clamp_from(self) }
390
391 #[inline(always)]
392 fn saturating_clamp_into (self) -> T { T::saturating_clamp_from(self) }
393
394 #[inline(always)]
395 fn try_clamp_into (self) -> Result<T, ClampError> {
396 T::try_clamp_from(self)
397 }
398}