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            $ordinal += crate::util::range_validated::days_in_year($year).cast_signed();
123        }
124    };
125}
126
127/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range.
128macro_rules! ensure_ranged {
129    ($type:ty : $value:ident) => {
130        match <$type>::new($value) {
131            Some(val) => val,
132            None => {
133                $crate::hint::cold_path();
134                return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
135            }
136        }
137    };
138
139    ($type:ty : $value:ident ($name:literal)) => {
140        match <$type>::new($value) {
141            Some(val) => val,
142            None => {
143                $crate::hint::cold_path();
144                return Err(crate::error::ComponentRange::unconditional($name));
145            }
146        }
147    };
148
149    ($type:ty : $value:ident $(as $as_type:ident)? * $factor:expr) => {
150        match ($value $(as $as_type)?).checked_mul($factor) {
151            Some(val) => match <$type>::new(val) {
152                Some(val) => val,
153                None => {
154                    $crate::hint::cold_path();
155                    return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
156                }
157            },
158            None => {
159                $crate::hint::cold_path();
160                return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
161            }
162        }
163    };
164}
165
166/// Try to unwrap an expression, returning if not possible.
167///
168/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
169/// usable in `const` contexts.
170macro_rules! const_try {
171    ($e:expr) => {
172        match $e {
173            Ok(value) => value,
174            Err(error) => {
175                $crate::hint::cold_path();
176                return Err(error);
177            }
178        }
179    };
180}
181
182/// Try to unwrap an expression, returning if not possible.
183///
184/// This is similar to the `?` operator, but is usable in `const` contexts.
185macro_rules! const_try_opt {
186    ($e:expr) => {
187        match $e {
188            Some(value) => value,
189            None => {
190                $crate::hint::cold_path();
191                return None;
192            }
193        }
194    };
195}
196
197/// `unreachable!()`, but better.
198#[cfg(any(feature = "formatting", feature = "parsing"))]
199macro_rules! bug {
200    () => {
201        compile_error!("provide an error message to help fix a possible bug")
202    };
203    ($descr:literal) => {
204        panic!(concat!("internal error: ", $descr))
205    };
206}
207
208#[cfg(any(feature = "formatting", feature = "parsing"))]
209pub(crate) use bug;
210pub(crate) use {carry, cascade, const_try, const_try_opt, div_floor, ensure_ranged};