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