Skip to main content

mz_ore/
overflowing.rs

1// Copyright 2019 The Rust Project Contributors
2// Copyright Materialize, Inc. and contributors. All rights reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License in the LICENSE file at the
7// root of this repository, or online at
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Overflowing number types.
18
19#[cfg(feature = "proptest")]
20use proptest_derive::Arbitrary;
21use serde::{Deserialize, Serialize};
22use std::ops::{Add, AddAssign, Div, Mul, Neg, Rem, Sub, SubAssign};
23
24/// Overflowing number. Operations panic on overflow, even in release mode.
25///
26/// The `ore_overflowing_behavior` feature flag can be used to control the
27/// overflow behavior:
28/// * `panic`: panic on overflow (default when debug assertions are enabled).
29/// * `soft_panic`: log a warning on overflow, or panic, depending on whether
30///   soft assertions are enbaled.
31/// * `ignore`: ignore overflow (default when debug assertions are disabled).
32/// The default value is `panic` when `debug_assertions` are enabled, or `ignore` otherwise.
33///
34/// The non-aborting modes simply return the result of the operation, which can
35/// include overflows.
36#[derive(
37    Debug,
38    Default,
39    Ord,
40    PartialOrd,
41    Eq,
42    PartialEq,
43    Copy,
44    Clone,
45    Serialize,
46    Deserialize
47)]
48#[cfg_attr(feature = "proptest", derive(Arbitrary))]
49pub struct Overflowing<T>(T);
50
51/// The behavior of the [`Overflowing`] type when an overflow occurs.
52#[derive(Debug)]
53pub enum OverflowingBehavior {
54    /// Panic on overflow. Corresponds to the `panic` string.
55    Panic,
56    /// Soft panic on overflow. Corresponds to the `soft_panic` string.
57    SoftPanic,
58    /// Ignore overflow. Corresponds to the `ignore` string.
59    Ignore,
60}
61
62impl std::str::FromStr for OverflowingBehavior {
63    type Err = String;
64
65    fn from_str(s: &str) -> Result<Self, Self::Err> {
66        match s {
67            _ if s.eq_ignore_ascii_case("panic") => Ok(OverflowingBehavior::Panic),
68            _ if s.eq_ignore_ascii_case("soft_panic") => Ok(OverflowingBehavior::SoftPanic),
69            _ if s.eq_ignore_ascii_case("ignore") => Ok(OverflowingBehavior::Ignore),
70            _ => Err(format!("Invalid OverflowingBehavior: {s}")),
71        }
72    }
73}
74
75/// Set the overflowing behavior for the process.
76///
77/// This function is thread-safe and can be used to change the behavior at runtime.
78///
79/// The default behavior is to ignore overflows.
80pub fn set_behavior(behavior: OverflowingBehavior) {
81    overflowing_support::set_overflowing_mode(behavior);
82}
83
84impl<T> Overflowing<T> {
85    /// Returns the inner value.
86    pub fn into_inner(self) -> T {
87        self.0
88    }
89}
90
91impl<T: std::fmt::Display> std::fmt::Display for Overflowing<T> {
92    #[inline(always)]
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        self.0.fmt(f)
95    }
96}
97
98#[cfg(feature = "columnar")]
99mod columnar {
100    use crate::overflowing::Overflowing;
101    use columnar::common::PushIndexAs;
102    use columnar::{
103        AsBytes, Borrow, Clear, Columnar, Container, FromBytes, Index, IndexAs, Len, Push,
104    };
105    use serde::{Deserialize, Serialize};
106    use std::ops::Range;
107
108    impl<T: Columnar + Copy + Send> Columnar for Overflowing<T>
109    where
110        for<'a> &'a [T]: AsBytes<'a> + FromBytes<'a>,
111        Overflowing<T>: From<T>,
112    {
113        #[inline(always)]
114        fn into_owned(other: columnar::Ref<'_, Self>) -> Self {
115            other
116        }
117        type Container = Overflows<T>;
118        #[inline(always)]
119        fn reborrow<'b, 'a: 'b>(thing: columnar::Ref<'a, Self>) -> columnar::Ref<'b, Self>
120        where
121            Self: 'a,
122        {
123            thing
124        }
125    }
126
127    /// Columnar container for [`Overflowing`].
128    #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
129    pub struct Overflows<T, TC = Vec<T>>(TC, std::marker::PhantomData<T>);
130
131    impl<T, TC: Default> Default for Overflows<T, TC> {
132        #[inline(always)]
133        fn default() -> Self {
134            Self(TC::default(), std::marker::PhantomData)
135        }
136    }
137
138    impl<T: Columnar + Copy + Send, TC: PushIndexAs<T>> Borrow for Overflows<T, TC>
139    where
140        Overflowing<T>: From<T>,
141    {
142        type Ref<'a> = Overflowing<T>;
143        type Borrowed<'a>
144            = Overflows<T, TC::Borrowed<'a>>
145        where
146            Self: 'a;
147        #[inline(always)]
148        fn borrow<'a>(&'a self) -> Self::Borrowed<'a> {
149            Overflows(self.0.borrow(), std::marker::PhantomData)
150        }
151        #[inline(always)]
152        fn reborrow<'b, 'a: 'b>(item: Self::Borrowed<'a>) -> Self::Borrowed<'b>
153        where
154            Self: 'a,
155        {
156            Overflows(TC::reborrow(item.0), std::marker::PhantomData)
157        }
158
159        #[inline(always)]
160        fn reborrow_ref<'b, 'a: 'b>(item: Self::Ref<'a>) -> Self::Ref<'b>
161        where
162            Self: 'a,
163        {
164            item
165        }
166    }
167
168    impl<T: Columnar + Copy + Send, TC: PushIndexAs<T>> Container for Overflows<T, TC>
169    where
170        Overflowing<T>: From<T>,
171    {
172        #[inline(always)]
173        fn extend_from_self(&mut self, other: Self::Borrowed<'_>, range: Range<usize>) {
174            self.0.extend_from_self(other.0, range);
175        }
176        #[inline(always)]
177        fn reserve_for<'a, I>(&mut self, selves: I)
178        where
179            Self: 'a,
180            I: Iterator<Item = Self::Borrowed<'a>> + Clone,
181        {
182            self.0.reserve_for(selves.map(|s| s.0));
183        }
184    }
185
186    impl<'a, T: Copy, TC: AsBytes<'a>> AsBytes<'a> for Overflows<T, TC> {
187        const SLICE_COUNT: usize = TC::SLICE_COUNT;
188        #[inline(always)]
189        fn get_byte_slice(&self, index: usize) -> (u64, &'a [u8]) {
190            self.0.get_byte_slice(index)
191        }
192        #[inline(always)]
193        fn as_bytes(&self) -> impl Iterator<Item = (u64, &'a [u8])> {
194            self.0.as_bytes()
195        }
196    }
197
198    impl<'a, T: Copy, TC: FromBytes<'a>> FromBytes<'a> for Overflows<T, TC> {
199        const SLICE_COUNT: usize = TC::SLICE_COUNT;
200        #[inline(always)]
201        fn from_bytes(bytes: &mut impl Iterator<Item = &'a [u8]>) -> Self {
202            Self(TC::from_bytes(bytes), std::marker::PhantomData)
203        }
204    }
205
206    impl<T: Copy, TC: Len> Len for Overflows<T, TC> {
207        #[inline(always)]
208        fn len(&self) -> usize {
209            self.0.len()
210        }
211    }
212
213    impl<T: Copy, TC: Clear> Clear for Overflows<T, TC> {
214        #[inline(always)]
215        fn clear(&mut self) {
216            self.0.clear();
217        }
218    }
219
220    impl<T: Copy, TC: IndexAs<T>> Index for Overflows<T, TC>
221    where
222        Overflowing<T>: From<T>,
223    {
224        type Ref = Overflowing<T>;
225        #[inline(always)]
226        fn get(&self, index: usize) -> Self::Ref {
227            self.0.index_as(index).into()
228        }
229    }
230
231    impl<T: Copy, TC: for<'a> Push<&'a T>> Push<Overflowing<T>> for Overflows<T, TC> {
232        #[inline(always)]
233        fn push(&mut self, item: Overflowing<T>) {
234            self.0.push(&item.0);
235        }
236    }
237
238    impl<T: Copy, TC: Push<T>> Push<&Overflowing<T>> for Overflows<T, TC> {
239        #[inline(always)]
240        fn push(&mut self, item: &Overflowing<T>) {
241            self.0.push(item.0);
242        }
243    }
244}
245
246macro_rules! impl_overflowing {
247    ($t:ty) => {
248        impl Overflowing<$t> {
249            /// The value zero.
250            pub const ZERO: Self = Self(0);
251            /// The value one.
252            pub const ONE: Self = Self(1);
253            /// The minimum value.
254            pub const MIN: Self = Self(<$t>::MIN);
255            /// The maximum value.
256            pub const MAX: Self = Self(<$t>::MAX);
257
258            /// Checked addition. Returns `None` if overflow occurred.
259            #[inline(always)]
260            pub fn checked_add(self, rhs: Self) -> Option<Self> {
261                self.0.checked_add(rhs.0).map(Self)
262            }
263
264            /// Wrapping addition.
265            #[inline(always)]
266            pub fn wrapping_add(self, rhs: Self) -> Self {
267                Self(self.0.wrapping_add(rhs.0))
268            }
269
270            /// Checked multiplication. Returns `None` if overflow occurred.
271            #[inline(always)]
272            pub fn checked_mul(self, rhs: Self) -> Option<Self> {
273                self.0.checked_mul(rhs.0).map(Self)
274            }
275
276            /// Wrapping multiplication.
277            #[inline(always)]
278            pub fn wrapping_mul(self, rhs: Self) -> Self {
279                Self(self.0.wrapping_mul(rhs.0))
280            }
281
282            /// Returns `true` if the number is zero.
283            pub fn is_zero(self) -> bool {
284                self == Self::ZERO
285            }
286        }
287
288        impl Add<Self> for Overflowing<$t> {
289            type Output = Self;
290
291            #[inline(always)]
292            fn add(self, rhs: Self) -> Self::Output {
293                match self.0.overflowing_add(rhs.0) {
294                    (result, true) => {
295                        overflowing_support::handle_overflow(result, format_args!("{self} + {rhs}"))
296                    }
297                    (result, false) => Self(result),
298                }
299            }
300        }
301
302        impl<'a> Add<&'a Self> for Overflowing<$t> {
303            type Output = Self;
304
305            #[inline(always)]
306            fn add(self, rhs: &'a Self) -> Self::Output {
307                match self.0.overflowing_add(rhs.0) {
308                    (result, true) => {
309                        overflowing_support::handle_overflow(result, format_args!("{self} + {rhs}"))
310                    }
311                    (result, false) => Self(result),
312                }
313            }
314        }
315
316        impl AddAssign<Self> for Overflowing<$t> {
317            #[inline(always)]
318            fn add_assign(&mut self, rhs: Self) {
319                *self = *self + rhs;
320            }
321        }
322
323        impl AddAssign<&Self> for Overflowing<$t> {
324            #[inline(always)]
325            fn add_assign(&mut self, rhs: &Self) {
326                *self = *self + *rhs;
327            }
328        }
329
330        impl Div<Self> for Overflowing<$t> {
331            type Output = Overflowing<<$t as Div>::Output>;
332
333            #[inline(always)]
334            fn div(self, rhs: Self) -> Self::Output {
335                match self.0.overflowing_div(rhs.0) {
336                    (result, true) => {
337                        overflowing_support::handle_overflow(result, format_args!("{self} / {rhs}"))
338                    }
339                    (result, false) => Self(result),
340                }
341            }
342        }
343
344        impl Rem<Self> for Overflowing<$t> {
345            type Output = Overflowing<<$t as Rem>::Output>;
346
347            #[inline(always)]
348            fn rem(self, rhs: Self) -> Self::Output {
349                match self.0.overflowing_rem(rhs.0) {
350                    (result, true) => {
351                        overflowing_support::handle_overflow(result, format_args!("{self} % {rhs}"))
352                    }
353                    (result, false) => Self(result),
354                }
355            }
356        }
357
358        impl Sub<Self> for Overflowing<$t> {
359            type Output = Self;
360
361            #[inline(always)]
362            fn sub(self, rhs: Self) -> Self::Output {
363                match self.0.overflowing_sub(rhs.0) {
364                    (result, true) => {
365                        overflowing_support::handle_overflow(result, format_args!("{self} - {rhs}"))
366                    }
367                    (result, false) => Self(result),
368                }
369            }
370        }
371
372        impl<'a> Sub<&'a Self> for Overflowing<$t> {
373            type Output = Self;
374
375            #[inline(always)]
376            fn sub(self, rhs: &'a Self) -> Self::Output {
377                match self.0.overflowing_sub(rhs.0) {
378                    (result, true) => {
379                        overflowing_support::handle_overflow(result, format_args!("{self} - {rhs}"))
380                    }
381                    (result, false) => Self(result),
382                }
383            }
384        }
385
386        impl SubAssign<Self> for Overflowing<$t> {
387            #[inline(always)]
388            fn sub_assign(&mut self, rhs: Self) {
389                *self = *self - rhs;
390            }
391        }
392
393        impl SubAssign<&Self> for Overflowing<$t> {
394            #[inline(always)]
395            fn sub_assign(&mut self, rhs: &Self) {
396                *self = *self - *rhs;
397            }
398        }
399
400        impl std::iter::Sum<Overflowing<$t>> for Overflowing<$t> {
401            #[inline(always)]
402            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
403                iter.fold(Self::ZERO, |a, b| a + b)
404            }
405        }
406
407        impl<'a> std::iter::Sum<&'a Overflowing<$t>> for Overflowing<$t> {
408            #[inline(always)]
409            fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
410                iter.fold(Self::ZERO, |a, b| a + b)
411            }
412        }
413
414        impl Mul for Overflowing<$t> {
415            type Output = Self;
416
417            #[inline(always)]
418            fn mul(self, rhs: Self) -> Self::Output {
419                match self.0.overflowing_mul(rhs.0) {
420                    (result, true) => {
421                        overflowing_support::handle_overflow(result, format_args!("{self} * {rhs}"))
422                    }
423                    (result, false) => Self(result),
424                }
425            }
426        }
427
428        #[cfg(feature = "differential-dataflow")]
429        impl differential_dataflow::difference::IsZero for Overflowing<$t> {
430            #[inline(always)]
431            fn is_zero(&self) -> bool {
432                self.0.is_zero()
433            }
434        }
435
436        #[cfg(feature = "differential-dataflow")]
437        impl differential_dataflow::difference::Semigroup for Overflowing<$t> {
438            #[inline(always)]
439            fn plus_equals(&mut self, rhs: &Self) {
440                *self += *rhs
441            }
442        }
443
444        #[cfg(feature = "differential-dataflow")]
445        impl differential_dataflow::difference::Monoid for Overflowing<$t> {
446            #[inline(always)]
447            fn zero() -> Self {
448                Self::ZERO
449            }
450        }
451
452        #[cfg(feature = "differential-dataflow")]
453        impl differential_dataflow::difference::Multiply<Self> for Overflowing<$t> {
454            type Output = Self;
455            #[inline(always)]
456            fn multiply(self, rhs: &Self) -> Self::Output {
457                self * *rhs
458            }
459        }
460
461        #[cfg(feature = "columnation")]
462        impl columnation::Columnation for Overflowing<$t> {
463            type InnerRegion = columnation::CopyRegion<Self>;
464        }
465
466        impl std::str::FromStr for Overflowing<$t> {
467            type Err = <$t as std::str::FromStr>::Err;
468
469            #[inline(always)]
470            fn from_str(s: &str) -> Result<Self, Self::Err> {
471                <$t>::from_str(s).map(Self)
472            }
473        }
474
475        impl std::hash::Hash for Overflowing<$t> {
476            #[inline(always)]
477            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
478                self.0.hash(state);
479            }
480        }
481
482        impl<T> crate::cast::CastFrom<T> for Overflowing<$t>
483        where
484            $t: crate::cast::CastFrom<T>,
485        {
486            #[inline(always)]
487            fn cast_from(value: T) -> Self {
488                Self(<$t>::cast_from(value))
489            }
490        }
491
492        #[cfg(feature = "num-traits")]
493        impl num_traits::identities::Zero for Overflowing<$t> {
494            #[inline(always)]
495            fn zero() -> Self {
496                Self::ZERO
497            }
498            #[inline(always)]
499            fn is_zero(&self) -> bool {
500                self.0.is_zero()
501            }
502        }
503
504        #[cfg(feature = "num-traits")]
505        impl num_traits::identities::One for Overflowing<$t> {
506            #[inline(always)]
507            fn one() -> Self {
508                Self::ONE
509            }
510        }
511
512        #[cfg(feature = "num-traits")]
513        impl num_traits::Num for Overflowing<$t> {
514            type FromStrRadixErr = <$t as num_traits::Num>::FromStrRadixErr;
515
516            #[inline(always)]
517            fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
518                <$t>::from_str_radix(str, radix).map(Self)
519            }
520        }
521    };
522}
523
524macro_rules! impl_overflowing_from {
525    ($t:ty, $($f:ty)+) => {
526        $(
527            impl From<$f> for Overflowing<$t> {
528                #[inline(always)]
529                fn from(value: $f) -> Self {
530                    Self(value.into())
531                }
532            }
533        )+
534    };
535}
536
537macro_rules! impl_overflowing_from_overflowing {
538    ($t:ty, $($f:ty)+) => {
539        $(
540            impl From<Overflowing<$f>> for Overflowing<$t> {
541                #[inline(always)]
542                fn from(value: Overflowing<$f>) -> Self {
543                    Self(value.0.into())
544                }
545            }
546        )+
547    };
548}
549
550macro_rules! impl_overflowing_try_from {
551    ($t:ty, $($f:ty)+) => {
552        $(
553            impl TryFrom<$f> for Overflowing<$t> {
554                type Error = <$t as TryFrom<$f>>::Error;
555                #[inline(always)]
556                fn try_from(value: $f) -> Result<Self, Self::Error> {
557                    <$t>::try_from(value).map(Self)
558                }
559            }
560
561            impl TryFrom<Overflowing<$f>> for Overflowing<$t> {
562                type Error = <$t as TryFrom<$f>>::Error;
563                #[inline(always)]
564                fn try_from(value: Overflowing<$f>) -> Result<Self, Self::Error> {
565                    <$t>::try_from(value.0).map(Self)
566                }
567            }
568        )+
569    };
570}
571
572// Implement Overflowing for signed types.
573macro_rules! impl_overflowing_signed {
574    ($t:ty, $u:ty) => {
575        impl Overflowing<$t> {
576            /// The value minus one.
577            pub const MINUS_ONE: Self = Self(-1);
578
579            /// Returns the absolute value of the number.
580            pub fn abs(self) -> Self {
581                Self(self.0.abs())
582            }
583
584            /// Returns the absolute value of the number as an unsigned integer.
585            #[inline(always)]
586            pub fn unsigned_abs(self) -> $u {
587                self.0.unsigned_abs()
588            }
589
590            /// Returns `true` if the number is positive and `false` if the number is zero
591            /// or negative.
592            ///
593            /// # Examples
594            ///
595            /// ```
596            /// # use mz_ore::Overflowing;
597            /// assert!(!Overflowing::<i64>::from(-10i32).is_positive());
598            /// assert!(Overflowing::<i64>::from(10i32).is_positive());
599            /// ```
600            pub fn is_positive(self) -> bool {
601                self > Self::ZERO
602            }
603
604            /// Returns `true` if the number is negative and `false` if the number is zero
605            /// or positive.
606            ///
607            /// # Examples
608            ///
609            /// ```
610            /// # use mz_ore::Overflowing;
611            /// assert!(Overflowing::<i64>::from(-10i32).is_negative());
612            /// assert!(!Overflowing::<i64>::from(10i32).is_negative());
613            /// ```
614            pub fn is_negative(self) -> bool {
615                self < Self::ZERO
616            }
617        }
618
619        impl Neg for Overflowing<$t> {
620            type Output = Overflowing<<$t as Neg>::Output>;
621
622            #[inline(always)]
623            fn neg(self) -> Self::Output {
624                match self.0.overflowing_neg() {
625                    (result, true) => {
626                        overflowing_support::handle_overflow(result, format_args!("-{self}"))
627                    }
628                    (result, false) => Self(result),
629                }
630            }
631        }
632
633        impl Neg for &Overflowing<$t> {
634            type Output = Overflowing<<$t as Neg>::Output>;
635
636            #[inline(always)]
637            fn neg(self) -> Self::Output {
638                match self.0.overflowing_neg() {
639                    (result, true) => {
640                        overflowing_support::handle_overflow(result, format_args!("-{self}"))
641                    }
642                    (result, false) => Overflowing(result),
643                }
644            }
645        }
646
647        #[cfg(feature = "differential-dataflow")]
648        impl differential_dataflow::difference::Abelian for Overflowing<$t> {
649            #[inline(always)]
650            fn negate(&mut self) {
651                *self = -*self
652            }
653        }
654
655        #[cfg(feature = "num-traits")]
656        impl num_traits::sign::Signed for Overflowing<$t> {
657            #[inline(always)]
658            fn abs(&self) -> Self {
659                Self(self.0.abs())
660            }
661            #[inline(always)]
662            fn abs_sub(&self, other: &Self) -> Self {
663                Self(self.0.abs_sub(&other.0))
664            }
665            #[inline(always)]
666            fn signum(&self) -> Self {
667                Self(self.0.signum())
668            }
669            #[inline(always)]
670            fn is_positive(&self) -> bool {
671                self.0.is_positive()
672            }
673            #[inline(always)]
674            fn is_negative(&self) -> bool {
675                self.0.is_negative()
676            }
677        }
678    };
679}
680
681macro_rules! overflowing {
682    ($t:ty, $($fit:ty)+, $($may_fit:ty)+ $(, $unsigned:ty)?) => {
683        impl_overflowing!($t);
684        impl_overflowing_from!($t, $($fit)+ $t);
685        impl_overflowing_from_overflowing!($t, $($fit)+);
686        impl_overflowing_try_from!($t, $($may_fit)+);
687        $( impl_overflowing_signed!($t, $unsigned); )?
688    };
689}
690
691// type, types that certainly fit, types that may fit, optional corresponding unsigned type
692overflowing!(u8, bool, u16 u32 u64 u128 i8 i16 i32 i64 i128 isize usize);
693overflowing!(u16, bool u8, u32 u64 u128 i8 i16 i32 i64 i128 isize usize);
694overflowing!(u32, bool u8 u16, u64 u128 i8 i16 i32 i64 i128 isize usize);
695overflowing!(u64, bool u8 u16 u32, u128 i8 i16 i32 i64 i128 isize usize);
696overflowing!(u128, bool u8 u16 u32 u64, i8 i16 i32 i64 i128 isize usize);
697
698overflowing!(i8, bool, u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize, u8);
699overflowing!(i16, bool i8 u8, u16 i32 u32 i64 u64 i128 u128 isize usize, u16);
700overflowing!(i32, bool i8 u8 i16 u16, u32 i64 u64 i128 u128 isize usize, u32);
701overflowing!(i64, bool i8 u8 i16 u16 i32 u32, u64 i128 u128 isize usize, u64);
702overflowing!(i128, bool i8 u8 i16 u16 i32 u32 i64 u64, u128 isize usize, u128);
703
704mod overflowing_support {
705    use std::sync::atomic::AtomicUsize;
706
707    use crate::overflowing::OverflowingBehavior;
708
709    /// Ignore overflow.
710    const MODE_IGNORE: usize = 0;
711    /// Soft assert on overflow.
712    const MODE_SOFT_PANIC: usize = 1;
713    /// Panic on overflow.
714    const MODE_PANIC: usize = 2;
715
716    static OVERFLOWING_MODE: AtomicUsize = AtomicUsize::new(MODE_IGNORE);
717
718    /// Handles overflow for [`Overflowing`](super::Overflowing) numbers.
719    #[track_caller]
720    #[cold]
721    pub(super) fn handle_overflow<T: Into<O>, O>(result: T, description: std::fmt::Arguments) -> O {
722        let mode = OVERFLOWING_MODE.load(std::sync::atomic::Ordering::Relaxed);
723        match mode {
724            #[cfg(not(target_arch = "wasm32"))]
725            MODE_SOFT_PANIC => crate::soft_panic_or_log!("Overflow: {description}"),
726            // We cannot use the logging `soft_panic_or_log` in wasm, so we panic instead (soft
727            // assertions are always enabled in wasm).
728            #[cfg(target_arch = "wasm32")]
729            MODE_SOFT_PANIC => panic!("Overflow: {description}"),
730            MODE_PANIC => panic!("Overflow: {description}"),
731            // MODE_IGNORE and all other (impossible) values
732            _ => {}
733        }
734        result.into()
735    }
736
737    /// Set the overflowing mode.
738    pub(crate) fn set_overflowing_mode(behavior: OverflowingBehavior) {
739        let value = match behavior {
740            OverflowingBehavior::Panic => MODE_PANIC,
741            OverflowingBehavior::SoftPanic => MODE_SOFT_PANIC,
742            OverflowingBehavior::Ignore => MODE_IGNORE,
743        };
744        OVERFLOWING_MODE.store(value, std::sync::atomic::Ordering::Relaxed);
745    }
746}
747
748#[cfg(test)]
749mod test {
750    use super::*;
751
752    #[cfg(debug_assertions)]
753    #[crate::test]
754    #[should_panic]
755    fn test_panicking_add() {
756        set_behavior(OverflowingBehavior::Panic);
757        let _ = Overflowing::<i8>::MAX + Overflowing::<i8>::ONE;
758    }
759
760    #[crate::test]
761    fn test_wrapping_add() {
762        let result = Overflowing::<i8>::MAX.wrapping_add(Overflowing::<i8>::ONE);
763        assert_eq!(result, Overflowing::<i8>::MIN);
764    }
765
766    #[crate::test]
767    fn test_checked_add() {
768        let result = Overflowing::<i8>::MAX.checked_add(Overflowing::<i8>::ONE);
769        assert_eq!(result, None);
770    }
771}