1#[cfg(feature = "proptest")]
20use proptest_derive::Arbitrary;
21use serde::{Deserialize, Serialize};
22use std::ops::{Add, AddAssign, Div, Mul, Neg, Rem, Sub, SubAssign};
23
24#[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#[derive(Debug)]
42pub enum OverflowingBehavior {
43 Panic,
45 SoftPanic,
47 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
64pub fn set_behavior(behavior: OverflowingBehavior) {
70 overflowing_support::set_overflowing_mode(behavior);
71}
72
73impl<T> Overflowing<T> {
74 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 #[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 pub const ZERO: Self = Self(0);
229 pub const ONE: Self = Self(1);
231 pub const MIN: Self = Self(<$t>::MIN);
233 pub const MAX: Self = Self(<$t>::MAX);
235
236 #[inline(always)]
238 pub fn checked_add(self, rhs: Self) -> Option<Self> {
239 self.0.checked_add(rhs.0).map(Self)
240 }
241
242 #[inline(always)]
244 pub fn wrapping_add(self, rhs: Self) -> Self {
245 Self(self.0.wrapping_add(rhs.0))
246 }
247
248 #[inline(always)]
250 pub fn checked_mul(self, rhs: Self) -> Option<Self> {
251 self.0.checked_mul(rhs.0).map(Self)
252 }
253
254 #[inline(always)]
256 pub fn wrapping_mul(self, rhs: Self) -> Self {
257 Self(self.0.wrapping_mul(rhs.0))
258 }
259
260 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
550macro_rules! impl_overflowing_signed {
552 ($t:ty, $u:ty) => {
553 impl Overflowing<$t> {
554 pub const MINUS_ONE: Self = Self(-1);
556
557 pub fn abs(self) -> Self {
559 Self(self.0.abs())
560 }
561
562 #[inline(always)]
564 pub fn unsigned_abs(self) -> $u {
565 self.0.unsigned_abs()
566 }
567
568 pub fn is_positive(self) -> bool {
579 self > Self::ZERO
580 }
581
582 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
669overflowing!(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 const MODE_IGNORE: usize = 0;
689 const MODE_SOFT_PANIC: usize = 1;
691 const MODE_PANIC: usize = 2;
693
694 static OVERFLOWING_MODE: AtomicUsize = AtomicUsize::new(MODE_IGNORE);
695
696 #[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 #[cfg(target_arch = "wasm32")]
707 MODE_SOFT_PANIC => panic!("Overflow: {description}"),
708 MODE_PANIC => panic!("Overflow: {description}"),
709 _ => {}
711 }
712 result.into()
713 }
714
715 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}