time/
internal_macros.rs

1//! Macros for use within the library. They are not publicly available.
2
3/// Helper macro for easily implementing `OpAssign`.
4macro_rules! __impl_assign {
5    ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$(
6        #[allow(unused_qualifications)]
7        $(#[$attr])*
8        impl core::ops::$op<$t> for $target {
9            #[inline]
10            fn $fn(&mut self, rhs: $t) {
11                *self = *self $sym rhs;
12            }
13        }
14    )+};
15}
16
17/// Implement `AddAssign` for the provided types.
18macro_rules! impl_add_assign {
19    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
20        $crate::internal_macros::__impl_assign!(
21            + AddAssign add_assign $target : $($(#[$attr])* $t),+
22        );
23    };
24}
25
26/// Implement `SubAssign` for the provided types.
27macro_rules! impl_sub_assign {
28    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
29        $crate::internal_macros::__impl_assign!(
30            - SubAssign sub_assign $target : $($(#[$attr])* $t),+
31        );
32    };
33}
34
35/// Implement `MulAssign` for the provided types.
36macro_rules! impl_mul_assign {
37    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
38        $crate::internal_macros::__impl_assign!(
39            * MulAssign mul_assign $target : $($(#[$attr])* $t),+
40        );
41    };
42}
43
44/// Implement `DivAssign` for the provided types.
45macro_rules! impl_div_assign {
46    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
47        $crate::internal_macros::__impl_assign!(
48            / DivAssign div_assign $target : $($(#[$attr])* $t),+
49        );
50    };
51}
52
53/// Division of integers, rounding the resulting value towards negative infinity.
54macro_rules! div_floor {
55    ($self:expr, $rhs:expr) => {
56        match ($self, $rhs) {
57            (this, rhs) => {
58                let d = this / rhs;
59                let r = this % rhs;
60
61                // If the remainder is non-zero, we need to subtract one if the
62                // signs of self and rhs differ, as this means we rounded upwards
63                // instead of downwards. We do this branchlessly by creating a mask
64                // which is all-ones iff the signs differ, and 0 otherwise. Then by
65                // adding this mask (which corresponds to the signed value -1), we
66                // get our correction.
67                let correction = (this ^ rhs) >> ($crate::size_of_val(&this) * 8 - 1);
68                if r != 0 {
69                    d + correction
70                } else {
71                    d
72                }
73            }
74        }
75    };
76}
77
78/// Similar to `overflowing_add`, but returning the number of times that it overflowed. Contained to
79/// a certain range and only overflows a maximum number of times.
80macro_rules! carry {
81    (@most_once $value:expr, $min:literal.. $max:expr) => {
82        match ($value, $min, $max) {
83            (value, min, max) => {
84                if crate::hint::likely(value >= min) {
85                    if crate::hint::likely(value < max) {
86                        (value, 0)
87                    } else {
88                        (value - (max - min), 1)
89                    }
90                } else {
91                    (value + (max - min), -1)
92                }
93            }
94        }
95    };
96    (@most_twice $value:expr, $min:literal.. $max:expr) => {
97        match ($value, $min, $max) {
98            (value, min, max) => {
99                if crate::hint::likely(value >= min) {
100                    if crate::hint::likely(value < max) {
101                        (value, 0)
102                    } else if value < 2 * max - min {
103                        (value - (max - min), 1)
104                    } else {
105                        (value - 2 * (max - min), 2)
106                    }
107                } else {
108                    if value >= min - max {
109                        (value + (max - min), -1)
110                    } else {
111                        (value + 2 * (max - min), -2)
112                    }
113                }
114            }
115        }
116    };
117    (@most_thrice $value:expr, $min:literal.. $max:expr) => {
118        match ($value, $min, $max) {
119            (value, min, max) => {
120                if crate::hint::likely(value >= min) {
121                    if crate::hint::likely(value < max) {
122                        (value, 0)
123                    } else if value < 2 * max - min {
124                        (value - (max - min), 1)
125                    } else if value < 3 * max - 2 * min {
126                        (value - 2 * (max - min), 2)
127                    } else {
128                        (value - 3 * (max - min), 3)
129                    }
130                } else {
131                    if value >= min - max {
132                        (value + (max - min), -1)
133                    } else if value >= 2 * (min - max) {
134                        (value + 2 * (max - min), -2)
135                    } else {
136                        (value + 3 * (max - min), -3)
137                    }
138                }
139            }
140        }
141    };
142}
143
144/// Cascade an out-of-bounds value.
145macro_rules! cascade {
146    (@ordinal ordinal) => {};
147    (@year year) => {};
148
149    // Cascade an out-of-bounds value from "from" to "to".
150    ($from:ident in $min:literal.. $max:expr => $to:tt) => {
151        #[allow(unused_comparisons, unused_assignments)]
152        let min = $min;
153        let max = $max;
154        if crate::hint::unlikely($from >= max) {
155            $from -= max - min;
156            $to += 1;
157        } else if crate::hint::unlikely($from < min) {
158            $from += max - min;
159            $to -= 1;
160        }
161    };
162
163    // Special case the ordinal-to-year cascade, as it has different behavior.
164    ($ordinal:ident => $year:ident) => {
165        // We need to actually capture the idents. Without this, macro hygiene causes errors.
166        cascade!(@ordinal $ordinal);
167        cascade!(@year $year);
168
169        let days_in_year = crate::util::days_in_year($year) as i16;
170        #[allow(unused_assignments)]
171        if crate::hint::unlikely($ordinal > days_in_year) {
172            $ordinal -= days_in_year;
173            $year += 1;
174        } else if crate::hint::unlikely($ordinal < 1) {
175            $year -= 1;
176            $ordinal += crate::util::days_in_year($year) as i16;
177        }
178    };
179}
180
181/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range.
182macro_rules! ensure_ranged {
183    ($type:ty : $value:ident) => {
184        match <$type>::new($value) {
185            Some(val) => val,
186            None => {
187                $crate::hint::cold_path();
188                #[allow(trivial_numeric_casts)]
189                return Err(crate::error::ComponentRange {
190                    name: stringify!($value),
191                    minimum: <$type>::MIN.get() as i64,
192                    maximum: <$type>::MAX.get() as i64,
193                    value: $value as i64,
194                    conditional_message: None,
195                });
196            }
197        }
198    };
199
200    ($type:ty : $value:ident $(as $as_type:ident)? * $factor:expr) => {
201        match ($value $(as $as_type)?).checked_mul($factor) {
202            Some(val) => match <$type>::new(val) {
203                Some(val) => val,
204                None => {
205                    $crate::hint::cold_path();
206                    #[allow(trivial_numeric_casts)]
207                    return Err(crate::error::ComponentRange {
208                        name: stringify!($value),
209                        minimum: <$type>::MIN.get() as i64 / $factor as i64,
210                        maximum: <$type>::MAX.get() as i64 / $factor as i64,
211                        value: $value as i64,
212                        conditional_message: None,
213                    });
214                }
215            },
216            None => {
217                $crate::hint::cold_path();
218                return Err(crate::error::ComponentRange {
219                    name: stringify!($value),
220                    minimum: <$type>::MIN.get() as i64 / $factor as i64,
221                    maximum: <$type>::MAX.get() as i64 / $factor as i64,
222                    value: $value as i64,
223                    conditional_message: None,
224                });
225            }
226        }
227    };
228}
229
230/// Try to unwrap an expression, returning if not possible.
231///
232/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
233/// usable in `const` contexts.
234macro_rules! const_try {
235    ($e:expr) => {
236        match $e {
237            Ok(value) => value,
238            Err(error) => return Err(error),
239        }
240    };
241}
242
243/// Try to unwrap an expression, returning if not possible.
244///
245/// This is similar to the `?` operator, but is usable in `const` contexts.
246macro_rules! const_try_opt {
247    ($e:expr) => {
248        match $e {
249            Some(value) => value,
250            None => return None,
251        }
252    };
253}
254
255/// Try to unwrap an expression, panicking if not possible.
256///
257/// This is similar to `$e.expect($message)`, but is usable in `const` contexts.
258macro_rules! expect_opt {
259    ($e:expr, $message:literal) => {
260        match $e {
261            Some(value) => value,
262            None => crate::expect_failed($message),
263        }
264    };
265}
266
267/// `unreachable!()`, but better.
268#[cfg(any(feature = "formatting", feature = "parsing"))]
269macro_rules! bug {
270    () => { compile_error!("provide an error message to help fix a possible bug") };
271    ($descr:literal $($rest:tt)?) => {
272        panic!(concat!("internal error: ", $descr) $($rest)?)
273    }
274}
275
276#[cfg(any(feature = "formatting", feature = "parsing"))]
277pub(crate) use bug;
278pub(crate) use {
279    __impl_assign, carry, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt,
280    impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign,
281};