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