cuadra/
clamper.rs

1// cuadra::clamper
2//
3//!
4//
5
6#![allow(dead_code)]
7
8/// Defines constant saturating casting functions between the permutations
9/// of all the requested origin primitives and the destination primitives.
10///
11/// Some of the functions are redundant, and not all of them will be used.
12macro_rules! scast {
13    (all_orig: $($orig:ty),+) => {
14        $( scast![all_dest: $orig; i8, u8, i16, u16, i32, u32, i64, u64, usize, isize]; )+
15    };
16
17    (all_dest: $orig:ty; $($dest:ty),+) => {
18        $( scast![single: $orig, $dest]; )+
19    };
20
21    (single: $orig:ty, $dest:ty) => {
22        paste::paste! {
23            #[doc = "Returns a saturating casted value from " $orig " to " $dest "."]
24            #[inline]
25            const fn [<cast_$orig _$dest>](orig: $orig) -> $dest {
26                // if casted != orig it can be either 0 or -1.
27                let casted = orig as $dest;
28                let overflow = orig != casted as $orig;
29                if overflow { if casted == 0 { $dest::MIN } else { $dest::MAX } } else { casted }
30            }
31        }
32    };
33}
34scast![all_orig: i8, u8, i16, u16, i32, u32, i64, u64, usize, isize];
35
36/// Generates size-specific Clamper implementations.
37macro_rules! macro_clamper {
38    ($($ip:ty, $b:literal),+) => {
39        $( macro_clamper![single: $ip, $b]; )+
40    };
41
42    // $ip: inner primitive
43    // $b: bit size
44    (single: $ip:ty, $b:literal) => {
45        paste::paste! {
46            #[doc = "Clamps a distance to half the range of an [`" $ip "`]."]
47            ///
48            /// It makes sure to clamp to the minimum or maximum representable
49            /// value in the new bit-size, under the new range-limit conditions.
50            pub struct [<Clamper$b>];
51
52            impl [<Clamper$b>] {
53                /// Minimum value for any layout dimension.
54                pub const MIN: $ip = $ip::MIN / 2;
55
56                /// Maximum value for any layout dimension.
57                pub const MAX: $ip = $ip::MAX / 2;
58
59                /* clamping from the same primitive */
60
61                #[doc = "Clamps [`" $ip "`] distance to [`MIN`][Self::MIN]`..`[`MAX`][Self::MAX]."]
62                ///
63                /// This function clamps a value using the same primitive type.
64                ///
65                /// It is simpler and probably faster than the other clamping
66                /// functions with the bit-size in the name.
67                #[inline(always)]
68                pub const fn clamp(d: $ip) -> $ip {
69                    if d < Self::MIN {
70                        Self::MIN
71                    } else if d > Self::MAX {
72                        Self::MAX
73                    } else {
74                        d
75                    }
76                }
77
78                #[doc = "Clamps [`" $ip "`] distance to `0..`[`MAX`][Self::MAX]."]
79                ///
80                /// This function clamps a value using the same primitive type.
81                ///
82                /// It is simpler and probably faster than the other clamping
83                /// functions with the bit-size in the name.
84                #[inline]
85                pub const fn clamp_non_negative(d: $ip) -> $ip {
86                    if d < 0 {
87                        0
88                    } else if d > Self::MAX {
89                        Self::MAX
90                    } else {
91                        d
92                    }
93                }
94                #[doc = "Clamps [`" $ip "`] distance to `1..`[`MAX`][Self::MAX]."]
95                ///
96                /// This function clamps a value using the same primitive type.
97                ///
98                /// It is simpler and probably faster than the other clamping
99                /// functions with the bit-size in the name.
100                #[inline]
101                pub const fn clamp_positive(d: $ip) -> $ip {
102                    if d < 1 {
103                        1
104                    } else if d > Self::MAX {
105                        Self::MAX
106                    } else {
107                        d
108                    }
109                }
110
111                /* from/to i32*/
112
113                #[doc = "Clamps [`i32`] distance to [`" $ip "`] [`MIN`][Self::MIN]`..`[`MAX`][Self::MAX]."]
114                #[inline(always)]
115                pub const fn clamp_from_i32(d: i32) -> $ip {
116                    [<cast_i32_$ip>](d)
117                }
118                #[doc = "Clamps [`i32`] distance to [`" $ip "`] `0..`[`MAX`][Self::MAX]."]
119                #[inline(always)]
120                pub const fn clamp_non_negative_from_i32(d: i32) -> $ip {
121                    if d < 0 {
122                        0
123                    } else {
124                        [<cast_i32_$ip>](d)
125                    }
126                }
127                #[doc = "Clamps [`i32`] distance to [`" $ip "`] `1..`[`MAX`][Self::MAX]."]
128                #[inline(always)]
129                pub const fn clamp_positive_from_i32(d: i32) -> $ip {
130                    if d < 1 {
131                        1
132                    } else {
133                        [<cast_i32_$ip>](d)
134                    }
135                }
136
137                //
138
139                #[doc = "Clamps [`" $ip "`] distance to [`i32`] [`MIN`][Self::MIN]`..`[`MAX`][Self::MAX]."]
140                #[inline]
141                pub const fn clamp_to_i32(d: $ip) -> i32 {
142                    if d < [<cast_i32_ $ip>](i32::MIN) {
143                        i32::MIN
144                    } else if d > [<cast_i32_ $ip>](i32::MAX) {
145                        i32::MAX
146                    } else {
147                        [<cast_$ip _i32>](d)
148                    }
149                }
150                #[doc = "Clamps [`" $ip "`] distance to [`i32`] `0..`[`MAX`][Self::MAX]."]
151                #[inline]
152                pub const fn clamp_non_negative_to_i32(d: $ip) -> i32 {
153                    if d < 0 {
154                        0
155                    } else if d > [<cast_i32_ $ip>](i32::MAX) {
156                        i32::MAX
157                    } else {
158                        [<cast_$ip _i32>](d)
159                    }
160                }
161                #[doc = "Clamps [`" $ip "`] distance to [`i32`] `1..`[`MAX`][Self::MAX]."]
162                #[inline]
163                pub const fn clamp_positive_to_i32(d: $ip) -> i32 {
164                    if d < 1 {
165                        1
166                    } else if d > [<cast_i32_ $ip>](i32::MAX) {
167                        i32::MAX
168                    } else {
169                        [<cast_$ip _i32>](d)
170                    }
171                }
172
173                /* from/to u32 (e.g. notcurses, tiny-skia)*/
174
175                #[doc = "Clamps an [`u32`] distance to [`" $ip "`] `0..`[`MAX`][Self::MAX]."]
176                #[inline]
177                pub const fn clamp_from_u32(d: u32) -> $ip {
178                    if d > [<cast_$ip _u32>](Self::MAX) {
179                        Self::MAX
180                    } else {
181                        [<cast_u32_$ip>](d)
182                    }
183                }
184                #[doc = "Clamps [`u32`] distance to [`" $ip "`] `1..`[`MAX`][Self::MAX]."]
185                #[inline]
186                pub const fn clamp_positive_from_u32(d: u32) -> $ip {
187                    if d == 0 {
188                        1
189                    } else if d > [<cast_$ip _u32>](Self::MAX) {
190                        Self::MAX
191                    } else {
192                        [<cast_u32_$ip>](d)
193                    }
194                }
195
196                //
197
198                #[doc = "Clamps [`" $ip "`] distance to [`u32`] `0..`[`MAX`][Self::MAX]."]
199                #[inline]
200                pub const fn clamp_to_u32(d: $ip) -> u32 {
201                    [<cast _$ip _u32>](Self::clamp_non_negative(d))
202                }
203                #[doc = "Clamps [`" $ip "`] distance to [`u32`] `1..`[`MAX`][Self::MAX]."]
204                #[inline]
205                pub const fn clamp_positive_to_u32(d: $ip) -> u32 {
206                    [<cast _$ip _u32>](Self::clamp_positive(d))
207                }
208
209                /* from/to u16 (e.g. crossterm) */
210
211                #[doc = "Clamps [`u16`] distance to [`" $ip "`] `0..`[`MAX`][Self::MAX]."]
212                #[inline]
213                pub const fn clamp_from_u16(d: u16) -> $ip {
214                    if d > [<cast_$ip _u16>](Self::MAX) {
215                        Self::MAX
216                    } else {
217                        [<cast_u16_$ip>](d)
218                    }
219                }
220                #[doc = "Clamps [`u16`] distance to [`" $ip "`] `1..`[`MAX`][Self::MAX]."]
221                #[inline]
222                pub const fn clamp_positive_from_u16(d: u16) -> $ip {
223                    if d == 0 {
224                        1
225                    } else if d > [<cast_$ip _u16>](Self::MAX) {
226                        Self::MAX
227                    } else {
228                        [<cast_u16_$ip>](d)
229                    }
230                }
231
232                //
233
234                #[doc = "Clamps [`" $ip "`] distance to [`u16`] `0..`[`MAX`][Self::MAX]."]
235                #[inline]
236                pub const fn clamp_to_u16(d: $ip) -> u16 {
237                    if d < 0 {
238                        0
239                    } else if d > [<cast_u16_ $ip>](u16::MAX) {
240                        u16::MAX
241                    } else {
242                        [<cast_$ip _u16>](d)
243                    }
244                }
245                #[doc = "Clamps [`" $ip "`] distance to [`u16`] `1..`[`MAX`][Self::MAX]."]
246                #[inline]
247                pub const fn clamp_positive_to_u16(d: $ip) -> u16 {
248                    if d < 1 {
249                        1
250                    } else if d > [<cast_u16_ $ip>](u16::MAX) {
251                        u16::MAX
252                    } else {
253                        [<cast_$ip _u16>](d)
254                    }
255                }
256
257                /* from/to i16 (e.g. sdl) */
258
259                #[doc = "Clamps [`i16`] distance to [`" $ip "`] [`MIN`][Self::MIN]`..`[`MAX`][Self::MAX]."]
260                #[inline(always)]
261                pub const fn clamp_from_i16(d: i16) -> $ip {
262                    [<cast_i16_$ip>](d)
263                }
264                #[doc = "Clamps [`i16`] distance to [`" $ip "`] `0..`[`MAX`][Self::MAX]."]
265                #[inline(always)]
266                pub const fn clamp_non_negative_from_i16(d: i16) -> $ip {
267                    if d < 0 {
268                        0
269                    } else {
270                        [<cast_i16_$ip>](d)
271                    }
272                }
273                #[doc = "Clamps [`i16`] distance to [`" $ip "`] `1..`[`MAX`][Self::MAX]."]
274                #[inline(always)]
275                pub const fn clamp_positive_from_i16(d: i16) -> $ip {
276                    if d < 1 {
277                        1
278                    } else {
279                        [<cast_i16_$ip>](d)
280                    }
281                }
282
283                //
284
285                #[doc = "Clamps [`" $ip "`] distance to [`i16`] [`MIN`][Self::MIN]`..`[`MAX`][Self::MAX]."]
286                #[inline]
287                pub const fn clamp_to_i16(d: $ip) -> i16 {
288                    if d < [<cast_i16_ $ip>](i16::MIN) {
289                        i16::MIN
290                    } else if d > [<cast_i16_ $ip>](i16::MAX) {
291                        i16::MAX
292                    } else {
293                        [<cast_$ip _i16>](d)
294                    }
295                }
296                #[doc = "Clamps [`" $ip "`] distance to [`i16`] `0..`[`MAX`][Self::MAX]."]
297                #[inline]
298                pub const fn clamp_non_negative_to_i16(d: $ip) -> i16 {
299                    if d < 0 {
300                        0
301                    } else if d > [<cast_i16_ $ip>](i16::MAX) {
302                        i16::MAX
303                    } else {
304                        [<cast_$ip _i16>](d)
305                    }
306                }
307                #[doc = "Clamps [`" $ip "`] distance to [`i16`] `1..`[`MAX`][Self::MAX]."]
308                #[inline]
309                pub const fn clamp_positive_to_i16(d: $ip) -> i16 {
310                    if d < 1 {
311                        1
312                    } else if d > [<cast_i16_ $ip>](i16::MAX) {
313                        i16::MAX
314                    } else {
315                        [<cast_$ip _i16>](d)
316                    }
317                }
318
319                /* from/to usize (e.g. machine word size & slice len)*/
320
321                #[doc = "Clamps [`usize`] distance to [`" $ip "`] `0..`[`MAX`][Self::MAX]."]
322                #[inline]
323                pub const fn clamp_from_usize(d: usize) -> $ip {
324                    if d > [<cast_$ip _usize>](Self::MAX) {
325                        Self::MAX
326                    } else {
327                        [<cast_usize_$ip>](d)
328                    }
329                }
330                #[doc = "Clamps [`usize`] distance to [`" $ip "`] `1..`[`MAX`][Self::MAX]."]
331                #[inline]
332                pub const fn clamp_positive_from_usize(d: usize) -> $ip {
333                    if d == 0 {
334                        1
335                    } else if d > [<cast_$ip _usize>](Self::MAX) {
336                        Self::MAX
337                    } else {
338                        [<cast_usize_$ip>](d)
339                    }
340                }
341
342                //
343
344                #[doc = "Clamps [`" $ip "`] distance to [`usize`] `0..`[`MAX`][Self::MAX]."]
345                #[inline]
346                pub const fn clamp_to_usize(d: $ip) -> usize {
347                    [<cast_$ip _usize>](Self::clamp_non_negative(d))
348                }
349                #[doc = "Clamps [`" $ip "`] distance to [`usize`] `1..`[`MAX`][Self::MAX]."]
350                #[inline]
351                pub const fn clamp_positive_to_usize(d: $ip) -> usize {
352                    [<cast_$ip _usize>](Self::clamp_positive(d))
353                }
354            }
355        }
356    };
357}
358macro_clamper![i8, 8, i16, 16, i32, 32, i64, 64];