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        #[inline(always)]
188        fn as_bytes(&self) -> impl Iterator<Item = (u64, &'a [u8])> {
189            self.0.as_bytes()
190        }
191    }
192
193    impl<'a, T: Copy, TC: FromBytes<'a>> FromBytes<'a> for Overflows<T, TC> {
194        #[inline(always)]
195        fn from_bytes(bytes: &mut impl Iterator<Item = &'a [u8]>) -> Self {
196            Self(TC::from_bytes(bytes), std::marker::PhantomData)
197        }
198    }
199
200    impl<T: Copy, TC: Len> Len for Overflows<T, TC> {
201        #[inline(always)]
202        fn len(&self) -> usize {
203            self.0.len()
204        }
205    }
206
207    impl<T: Copy, TC: Clear> Clear for Overflows<T, TC> {
208        #[inline(always)]
209        fn clear(&mut self) {
210            self.0.clear();
211        }
212    }
213
214    impl<T: Copy, TC: IndexAs<T>> Index for Overflows<T, TC>
215    where
216        Overflowing<T>: From<T>,
217    {
218        type Ref = Overflowing<T>;
219        #[inline(always)]
220        fn get(&self, index: usize) -> Self::Ref {
221            self.0.index_as(index).into()
222        }
223    }
224
225    impl<T: Copy, TC: for<'a> Push<&'a T>> Push<Overflowing<T>> for Overflows<T, TC> {
226        #[inline(always)]
227        fn push(&mut self, item: Overflowing<T>) {
228            self.0.push(&item.0);
229        }
230    }
231
232    impl<T: Copy, TC: Push<T>> Push<&Overflowing<T>> for Overflows<T, TC> {
233        #[inline(always)]
234        fn push(&mut self, item: &Overflowing<T>) {
235            self.0.push(item.0);
236        }
237    }
238
239    impl<T, TC: columnar::HeapSize> columnar::HeapSize for Overflows<T, TC> {
240        #[inline(always)]
241        fn heap_size(&self) -> (usize, usize) {
242            self.0.heap_size()
243        }
244    }
245}
246
247macro_rules! impl_overflowing {
248    ($t:ty) => {
249        impl Overflowing<$t> {
250            /// The value zero.
251            pub const ZERO: Self = Self(0);
252            /// The value one.
253            pub const ONE: Self = Self(1);
254            /// The minimum value.
255            pub const MIN: Self = Self(<$t>::MIN);
256            /// The maximum value.
257            pub const MAX: Self = Self(<$t>::MAX);
258
259            /// Checked addition. Returns `None` if overflow occurred.
260            #[inline(always)]
261            pub fn checked_add(self, rhs: Self) -> Option<Self> {
262                self.0.checked_add(rhs.0).map(Self)
263            }
264
265            /// Wrapping addition.
266            #[inline(always)]
267            pub fn wrapping_add(self, rhs: Self) -> Self {
268                Self(self.0.wrapping_add(rhs.0))
269            }
270
271            /// Checked multiplication. Returns `None` if overflow occurred.
272            #[inline(always)]
273            pub fn checked_mul(self, rhs: Self) -> Option<Self> {
274                self.0.checked_mul(rhs.0).map(Self)
275            }
276
277            /// Wrapping multiplication.
278            #[inline(always)]
279            pub fn wrapping_mul(self, rhs: Self) -> Self {
280                Self(self.0.wrapping_mul(rhs.0))
281            }
282
283            /// Returns `true` if the number is zero.
284            pub fn is_zero(self) -> bool {
285                self == Self::ZERO
286            }
287        }
288
289        impl Add<Self> for Overflowing<$t> {
290            type Output = Self;
291
292            #[inline(always)]
293            fn add(self, rhs: Self) -> Self::Output {
294                match self.0.overflowing_add(rhs.0) {
295                    (result, true) => {
296                        overflowing_support::handle_overflow(result, format_args!("{self} + {rhs}"))
297                    }
298                    (result, false) => Self(result),
299                }
300            }
301        }
302
303        impl<'a> Add<&'a Self> for Overflowing<$t> {
304            type Output = Self;
305
306            #[inline(always)]
307            fn add(self, rhs: &'a Self) -> Self::Output {
308                match self.0.overflowing_add(rhs.0) {
309                    (result, true) => {
310                        overflowing_support::handle_overflow(result, format_args!("{self} + {rhs}"))
311                    }
312                    (result, false) => Self(result),
313                }
314            }
315        }
316
317        impl AddAssign<Self> for Overflowing<$t> {
318            #[inline(always)]
319            fn add_assign(&mut self, rhs: Self) {
320                *self = *self + rhs;
321            }
322        }
323
324        impl AddAssign<&Self> for Overflowing<$t> {
325            #[inline(always)]
326            fn add_assign(&mut self, rhs: &Self) {
327                *self = *self + *rhs;
328            }
329        }
330
331        impl Div<Self> for Overflowing<$t> {
332            type Output = Overflowing<<$t as Div>::Output>;
333
334            #[inline(always)]
335            fn div(self, rhs: Self) -> Self::Output {
336                match self.0.overflowing_div(rhs.0) {
337                    (result, true) => {
338                        overflowing_support::handle_overflow(result, format_args!("{self} / {rhs}"))
339                    }
340                    (result, false) => Self(result),
341                }
342            }
343        }
344
345        impl Rem<Self> for Overflowing<$t> {
346            type Output = Overflowing<<$t as Rem>::Output>;
347
348            #[inline(always)]
349            fn rem(self, rhs: Self) -> Self::Output {
350                match self.0.overflowing_rem(rhs.0) {
351                    (result, true) => {
352                        overflowing_support::handle_overflow(result, format_args!("{self} % {rhs}"))
353                    }
354                    (result, false) => Self(result),
355                }
356            }
357        }
358
359        impl Sub<Self> for Overflowing<$t> {
360            type Output = Self;
361
362            #[inline(always)]
363            fn sub(self, rhs: Self) -> Self::Output {
364                match self.0.overflowing_sub(rhs.0) {
365                    (result, true) => {
366                        overflowing_support::handle_overflow(result, format_args!("{self} - {rhs}"))
367                    }
368                    (result, false) => Self(result),
369                }
370            }
371        }
372
373        impl<'a> Sub<&'a Self> for Overflowing<$t> {
374            type Output = Self;
375
376            #[inline(always)]
377            fn sub(self, rhs: &'a Self) -> Self::Output {
378                match self.0.overflowing_sub(rhs.0) {
379                    (result, true) => {
380                        overflowing_support::handle_overflow(result, format_args!("{self} - {rhs}"))
381                    }
382                    (result, false) => Self(result),
383                }
384            }
385        }
386
387        impl SubAssign<Self> for Overflowing<$t> {
388            #[inline(always)]
389            fn sub_assign(&mut self, rhs: Self) {
390                *self = *self - rhs;
391            }
392        }
393
394        impl SubAssign<&Self> for Overflowing<$t> {
395            #[inline(always)]
396            fn sub_assign(&mut self, rhs: &Self) {
397                *self = *self - *rhs;
398            }
399        }
400
401        impl std::iter::Sum<Overflowing<$t>> for Overflowing<$t> {
402            #[inline(always)]
403            fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
404                iter.fold(Self::ZERO, |a, b| a + b)
405            }
406        }
407
408        impl<'a> std::iter::Sum<&'a Overflowing<$t>> for Overflowing<$t> {
409            #[inline(always)]
410            fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
411                iter.fold(Self::ZERO, |a, b| a + b)
412            }
413        }
414
415        impl Mul for Overflowing<$t> {
416            type Output = Self;
417
418            #[inline(always)]
419            fn mul(self, rhs: Self) -> Self::Output {
420                match self.0.overflowing_mul(rhs.0) {
421                    (result, true) => {
422                        overflowing_support::handle_overflow(result, format_args!("{self} * {rhs}"))
423                    }
424                    (result, false) => Self(result),
425                }
426            }
427        }
428
429        #[cfg(feature = "differential-dataflow")]
430        impl differential_dataflow::difference::IsZero for Overflowing<$t> {
431            #[inline(always)]
432            fn is_zero(&self) -> bool {
433                self.0.is_zero()
434            }
435        }
436
437        #[cfg(feature = "differential-dataflow")]
438        impl differential_dataflow::difference::Semigroup for Overflowing<$t> {
439            #[inline(always)]
440            fn plus_equals(&mut self, rhs: &Self) {
441                *self += *rhs
442            }
443        }
444
445        #[cfg(feature = "differential-dataflow")]
446        impl differential_dataflow::difference::Monoid for Overflowing<$t> {
447            #[inline(always)]
448            fn zero() -> Self {
449                Self::ZERO
450            }
451        }
452
453        #[cfg(feature = "differential-dataflow")]
454        impl differential_dataflow::difference::Multiply<Self> for Overflowing<$t> {
455            type Output = Self;
456            #[inline(always)]
457            fn multiply(self, rhs: &Self) -> Self::Output {
458                self * *rhs
459            }
460        }
461
462        #[cfg(feature = "columnation")]
463        impl columnation::Columnation for Overflowing<$t> {
464            type InnerRegion = columnation::CopyRegion<Self>;
465        }
466
467        impl std::str::FromStr for Overflowing<$t> {
468            type Err = <$t as std::str::FromStr>::Err;
469
470            #[inline(always)]
471            fn from_str(s: &str) -> Result<Self, Self::Err> {
472                <$t>::from_str(s).map(Self)
473            }
474        }
475
476        impl std::hash::Hash for Overflowing<$t> {
477            #[inline(always)]
478            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
479                self.0.hash(state);
480            }
481        }
482
483        impl<T> crate::cast::CastFrom<T> for Overflowing<$t>
484        where
485            $t: crate::cast::CastFrom<T>,
486        {
487            #[inline(always)]
488            fn cast_from(value: T) -> Self {
489                Self(<$t>::cast_from(value))
490            }
491        }
492
493        #[cfg(feature = "num-traits")]
494        impl num_traits::identities::Zero for Overflowing<$t> {
495            #[inline(always)]
496            fn zero() -> Self {
497                Self::ZERO
498            }
499            #[inline(always)]
500            fn is_zero(&self) -> bool {
501                self.0.is_zero()
502            }
503        }
504
505        #[cfg(feature = "num-traits")]
506        impl num_traits::identities::One for Overflowing<$t> {
507            #[inline(always)]
508            fn one() -> Self {
509                Self::ONE
510            }
511        }
512
513        #[cfg(feature = "num-traits")]
514        impl num_traits::Num for Overflowing<$t> {
515            type FromStrRadixErr = <$t as num_traits::Num>::FromStrRadixErr;
516
517            #[inline(always)]
518            fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
519                <$t>::from_str_radix(str, radix).map(Self)
520            }
521        }
522    };
523}
524
525macro_rules! impl_overflowing_from {
526    ($t:ty, $($f:ty)+) => {
527        $(
528            impl From<$f> for Overflowing<$t> {
529                #[inline(always)]
530                fn from(value: $f) -> Self {
531                    Self(value.into())
532                }
533            }
534        )+
535    };
536}
537
538macro_rules! impl_overflowing_from_overflowing {
539    ($t:ty, $($f:ty)+) => {
540        $(
541            impl From<Overflowing<$f>> for Overflowing<$t> {
542                #[inline(always)]
543                fn from(value: Overflowing<$f>) -> Self {
544                    Self(value.0.into())
545                }
546            }
547        )+
548    };
549}
550
551macro_rules! impl_overflowing_try_from {
552    ($t:ty, $($f:ty)+) => {
553        $(
554            impl TryFrom<$f> for Overflowing<$t> {
555                type Error = <$t as TryFrom<$f>>::Error;
556                #[inline(always)]
557                fn try_from(value: $f) -> Result<Self, Self::Error> {
558                    <$t>::try_from(value).map(Self)
559                }
560            }
561
562            impl TryFrom<Overflowing<$f>> for Overflowing<$t> {
563                type Error = <$t as TryFrom<$f>>::Error;
564                #[inline(always)]
565                fn try_from(value: Overflowing<$f>) -> Result<Self, Self::Error> {
566                    <$t>::try_from(value.0).map(Self)
567                }
568            }
569        )+
570    };
571}
572
573// Implement Overflowing for signed types.
574macro_rules! impl_overflowing_signed {
575    ($t:ty, $u:ty) => {
576        impl Overflowing<$t> {
577            /// The value minus one.
578            pub const MINUS_ONE: Self = Self(-1);
579
580            /// Returns the absolute value of the number.
581            pub fn abs(self) -> Self {
582                Self(self.0.abs())
583            }
584
585            /// Returns the absolute value of the number as an unsigned integer.
586            #[inline(always)]
587            pub fn unsigned_abs(self) -> $u {
588                self.0.unsigned_abs()
589            }
590
591            /// Returns `true` if the number is positive and `false` if the number is zero
592            /// or negative.
593            ///
594            /// # Examples
595            ///
596            /// ```
597            /// # use mz_ore::Overflowing;
598            /// assert!(!Overflowing::<i64>::from(-10i32).is_positive());
599            /// assert!(Overflowing::<i64>::from(10i32).is_positive());
600            /// ```
601            pub fn is_positive(self) -> bool {
602                self > Self::ZERO
603            }
604
605            /// Returns `true` if the number is negative and `false` if the number is zero
606            /// or positive.
607            ///
608            /// # Examples
609            ///
610            /// ```
611            /// # use mz_ore::Overflowing;
612            /// assert!(Overflowing::<i64>::from(-10i32).is_negative());
613            /// assert!(!Overflowing::<i64>::from(10i32).is_negative());
614            /// ```
615            pub fn is_negative(self) -> bool {
616                self < Self::ZERO
617            }
618        }
619
620        impl Neg for Overflowing<$t> {
621            type Output = Overflowing<<$t as Neg>::Output>;
622
623            #[inline(always)]
624            fn neg(self) -> Self::Output {
625                match self.0.overflowing_neg() {
626                    (result, true) => {
627                        overflowing_support::handle_overflow(result, format_args!("-{self}"))
628                    }
629                    (result, false) => Self(result),
630                }
631            }
632        }
633
634        impl Neg for &Overflowing<$t> {
635            type Output = Overflowing<<$t as Neg>::Output>;
636
637            #[inline(always)]
638            fn neg(self) -> Self::Output {
639                match self.0.overflowing_neg() {
640                    (result, true) => {
641                        overflowing_support::handle_overflow(result, format_args!("-{self}"))
642                    }
643                    (result, false) => Overflowing(result),
644                }
645            }
646        }
647
648        #[cfg(feature = "differential-dataflow")]
649        impl differential_dataflow::difference::Abelian for Overflowing<$t> {
650            #[inline(always)]
651            fn negate(&mut self) {
652                *self = -*self
653            }
654        }
655
656        #[cfg(feature = "num-traits")]
657        impl num_traits::sign::Signed for Overflowing<$t> {
658            #[inline(always)]
659            fn abs(&self) -> Self {
660                Self(self.0.abs())
661            }
662            #[inline(always)]
663            fn abs_sub(&self, other: &Self) -> Self {
664                Self(self.0.abs_sub(&other.0))
665            }
666            #[inline(always)]
667            fn signum(&self) -> Self {
668                Self(self.0.signum())
669            }
670            #[inline(always)]
671            fn is_positive(&self) -> bool {
672                self.0.is_positive()
673            }
674            #[inline(always)]
675            fn is_negative(&self) -> bool {
676                self.0.is_negative()
677            }
678        }
679    };
680}
681
682macro_rules! overflowing {
683    ($t:ty, $($fit:ty)+, $($may_fit:ty)+ $(, $unsigned:ty)?) => {
684        impl_overflowing!($t);
685        impl_overflowing_from!($t, $($fit)+ $t);
686        impl_overflowing_from_overflowing!($t, $($fit)+);
687        impl_overflowing_try_from!($t, $($may_fit)+);
688        $( impl_overflowing_signed!($t, $unsigned); )?
689    };
690}
691
692// type, types that certainly fit, types that may fit, optional corresponding unsigned type
693overflowing!(u8, bool, u16 u32 u64 u128 i8 i16 i32 i64 i128 isize usize);
694overflowing!(u16, bool u8, u32 u64 u128 i8 i16 i32 i64 i128 isize usize);
695overflowing!(u32, bool u8 u16, u64 u128 i8 i16 i32 i64 i128 isize usize);
696overflowing!(u64, bool u8 u16 u32, u128 i8 i16 i32 i64 i128 isize usize);
697overflowing!(u128, bool u8 u16 u32 u64, i8 i16 i32 i64 i128 isize usize);
698
699overflowing!(i8, bool, u8 i16 u16 i32 u32 i64 u64 i128 u128 isize usize, u8);
700overflowing!(i16, bool i8 u8, u16 i32 u32 i64 u64 i128 u128 isize usize, u16);
701overflowing!(i32, bool i8 u8 i16 u16, u32 i64 u64 i128 u128 isize usize, u32);
702overflowing!(i64, bool i8 u8 i16 u16 i32 u32, u64 i128 u128 isize usize, u64);
703overflowing!(i128, bool i8 u8 i16 u16 i32 u32 i64 u64, u128 isize usize, u128);
704
705mod overflowing_support {
706    use std::sync::atomic::AtomicUsize;
707
708    use crate::overflowing::OverflowingBehavior;
709
710    /// Ignore overflow.
711    const MODE_IGNORE: usize = 0;
712    /// Soft assert on overflow.
713    const MODE_SOFT_PANIC: usize = 1;
714    /// Panic on overflow.
715    const MODE_PANIC: usize = 2;
716
717    static OVERFLOWING_MODE: AtomicUsize = AtomicUsize::new(MODE_IGNORE);
718
719    /// Handles overflow for [`Overflowing`](super::Overflowing) numbers.
720    #[track_caller]
721    #[cold]
722    pub(super) fn handle_overflow<T: Into<O>, O>(result: T, description: std::fmt::Arguments) -> O {
723        let mode = OVERFLOWING_MODE.load(std::sync::atomic::Ordering::Relaxed);
724        match mode {
725            #[cfg(not(target_arch = "wasm32"))]
726            MODE_SOFT_PANIC => crate::soft_panic_or_log!("Overflow: {description}"),
727            // We cannot use the logging `soft_panic_or_log` in wasm, so we panic instead (soft
728            // assertions are always enabled in wasm).
729            #[cfg(target_arch = "wasm32")]
730            MODE_SOFT_PANIC => panic!("Overflow: {description}"),
731            MODE_PANIC => panic!("Overflow: {description}"),
732            // MODE_IGNORE and all other (impossible) values
733            _ => {}
734        }
735        result.into()
736    }
737
738    /// Set the overflowing mode.
739    pub(crate) fn set_overflowing_mode(behavior: OverflowingBehavior) {
740        let value = match behavior {
741            OverflowingBehavior::Panic => MODE_PANIC,
742            OverflowingBehavior::SoftPanic => MODE_SOFT_PANIC,
743            OverflowingBehavior::Ignore => MODE_IGNORE,
744        };
745        OVERFLOWING_MODE.store(value, std::sync::atomic::Ordering::Relaxed);
746    }
747}
748
749#[cfg(test)]
750mod test {
751    use super::*;
752
753    #[cfg(debug_assertions)]
754    #[crate::test]
755    #[should_panic]
756    fn test_panicking_add() {
757        set_behavior(OverflowingBehavior::Panic);
758        let _ = Overflowing::<i8>::MAX + Overflowing::<i8>::ONE;
759    }
760
761    #[crate::test]
762    fn test_wrapping_add() {
763        let result = Overflowing::<i8>::MAX.wrapping_add(Overflowing::<i8>::ONE);
764        assert_eq!(result, Overflowing::<i8>::MIN);
765    }
766
767    #[crate::test]
768    fn test_checked_add() {
769        let result = Overflowing::<i8>::MAX.checked_add(Overflowing::<i8>::ONE);
770        assert_eq!(result, None);
771    }
772}