Skip to main content

time/
internal_macros.rs

1//! Macros for use within the library. They are not publicly available.
2
3/// Division of integers, rounding the resulting value towards negative infinity.
4macro_rules! div_floor {
5    ($self:expr, $rhs:expr) => {
6        match ($self, $rhs) {
7            (this, rhs) => {
8                let d = this / rhs;
9                let r = this % rhs;
10
11                // If the remainder is non-zero, we need to subtract one if the
12                // signs of self and rhs differ, as this means we rounded upwards
13                // instead of downwards. We do this branchlessly by creating a mask
14                // which is all-ones iff the signs differ, and 0 otherwise. Then by
15                // adding this mask (which corresponds to the signed value -1), we
16                // get our correction.
17                let correction = (this ^ rhs) >> (size_of_val(&this) * 8 - 1);
18                if r != 0 { d + correction } else { d }
19            }
20        }
21    };
22}
23
24/// Similar to `overflowing_add`, but returning the number of times that it overflowed. Contained to
25/// a certain range and only overflows a maximum number of times.
26macro_rules! carry {
27    (@most_once $value:expr, $min:literal.. $max:expr) => {
28        match ($value, $min, $max) {
29            (value, min, max) => {
30                if crate::hint::likely(value >= min) {
31                    if crate::hint::likely(value < max) {
32                        (value, 0)
33                    } else {
34                        (value - (max - min), 1)
35                    }
36                } else {
37                    (value + (max - min), -1)
38                }
39            }
40        }
41    };
42    (@most_twice $value:expr, $min:literal.. $max:expr) => {
43        match ($value, $min, $max) {
44            (value, min, max) => {
45                if crate::hint::likely(value >= min) {
46                    if crate::hint::likely(value < max) {
47                        (value, 0)
48                    } else if value < 2 * max - min {
49                        (value - (max - min), 1)
50                    } else {
51                        (value - 2 * (max - min), 2)
52                    }
53                } else {
54                    if value >= min - max {
55                        (value + (max - min), -1)
56                    } else {
57                        (value + 2 * (max - min), -2)
58                    }
59                }
60            }
61        }
62    };
63    (@most_thrice $value:expr, $min:literal.. $max:expr) => {
64        match ($value, $min, $max) {
65            (value, min, max) => {
66                if crate::hint::likely(value >= min) {
67                    if crate::hint::likely(value < max) {
68                        (value, 0)
69                    } else if value < 2 * max - min {
70                        (value - (max - min), 1)
71                    } else if value < 3 * max - 2 * min {
72                        (value - 2 * (max - min), 2)
73                    } else {
74                        (value - 3 * (max - min), 3)
75                    }
76                } else {
77                    if value >= min - max {
78                        (value + (max - min), -1)
79                    } else if value >= 2 * (min - max) {
80                        (value + 2 * (max - min), -2)
81                    } else {
82                        (value + 3 * (max - min), -3)
83                    }
84                }
85            }
86        }
87    };
88}
89
90/// Cascade an out-of-bounds value.
91macro_rules! cascade {
92    (@ordinal ordinal) => {};
93    (@year year) => {};
94
95    // Cascade an out-of-bounds value from "from" to "to".
96    ($from:ident in $min:literal.. $max:expr => $to:tt) => {
97        #[allow(unused_comparisons, unused_assignments)]
98        let min = $min;
99        let max = $max;
100        if crate::hint::unlikely($from >= max) {
101            $from -= max - min;
102            $to += 1;
103        } else if crate::hint::unlikely($from < min) {
104            $from += max - min;
105            $to -= 1;
106        }
107    };
108
109    // Special case the ordinal-to-year cascade, as it has different behavior.
110    ($ordinal:ident => $year:ident) => {
111        // We need to actually capture the idents. Without this, macro hygiene causes errors.
112        cascade!(@ordinal $ordinal);
113        cascade!(@year $year);
114
115        let days_in_year = crate::util::range_validated::days_in_year($year).cast_signed();
116        #[allow(unused_assignments)]
117        if crate::hint::unlikely($ordinal > days_in_year) {
118            $ordinal -= days_in_year;
119            $year += 1;
120        } else if crate::hint::unlikely($ordinal < 1) {
121            $year -= 1;
122            // The year may now be out of range if it was previously `MIN_YEAR`. This macro arm is
123            // only called in situations where this is possible, so branching on where this is a
124            // possibility is not necessary.
125            $ordinal += crate::util::days_in_year($year).cast_signed();
126        }
127    };
128}
129
130/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range.
131macro_rules! ensure_ranged {
132    ($type:ty : $value:ident) => {
133        match <$type>::new($value) {
134            Some(val) => val,
135            None => {
136                $crate::hint::cold_path();
137                return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
138            }
139        }
140    };
141
142    ($type:ty : $value:ident ($name:literal)) => {
143        match <$type>::new($value) {
144            Some(val) => val,
145            None => {
146                $crate::hint::cold_path();
147                return Err(crate::error::ComponentRange::unconditional($name));
148            }
149        }
150    };
151
152    ($type:ty : $value:ident $(as $as_type:ident)? * $factor:expr) => {
153        match ($value $(as $as_type)?).checked_mul($factor) {
154            Some(val) => match <$type>::new(val) {
155                Some(val) => val,
156                None => {
157                    $crate::hint::cold_path();
158                    return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
159                }
160            },
161            None => {
162                $crate::hint::cold_path();
163                return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
164            }
165        }
166    };
167}
168
169/// Try to unwrap an expression, returning if not possible.
170///
171/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
172/// usable in `const` contexts.
173macro_rules! const_try {
174    ($e:expr) => {
175        match $e {
176            Ok(value) => value,
177            Err(error) => {
178                $crate::hint::cold_path();
179                return Err(error);
180            }
181        }
182    };
183}
184
185/// Try to unwrap an expression, returning if not possible.
186///
187/// This is identical to `?` in terms of behavior, but marks the error path as cold.
188#[cfg(any(feature = "formatting", feature = "parsing", feature = "serde"))]
189macro_rules! try_likely_ok {
190    ($e:expr) => {
191        match $e {
192            Ok(value) => value,
193            Err(error) => {
194                $crate::hint::cold_path();
195                return Err(error.into());
196            }
197        }
198    };
199}
200
201/// Try to unwrap an expression, returning if not possible.
202///
203/// This is similar to the `?` operator, but is usable in `const` contexts.
204macro_rules! const_try_opt {
205    ($e:expr) => {
206        match $e {
207            Some(value) => value,
208            None => {
209                $crate::hint::cold_path();
210                return None;
211            }
212        }
213    };
214}
215
216/// `unreachable!()`, but better.
217macro_rules! bug {
218    () => {
219        compile_error!("provide an error message to help fix a possible bug")
220    };
221    ($descr:literal) => {{
222        $crate::hint::cold_path();
223        panic!(concat!("internal error: ", $descr))
224    }};
225}
226
227pub(crate) use bug;
228pub(crate) use carry;
229pub(crate) use cascade;
230pub(crate) use const_try;
231pub(crate) use const_try_opt;
232pub(crate) use div_floor;
233pub(crate) use ensure_ranged;
234#[cfg(any(feature = "formatting", feature = "parsing", feature = "serde"))]
235pub(crate) use try_likely_ok;