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