From 861ad644ce1e363a6761af1039188cfbe79b045d Mon Sep 17 00:00:00 2001
From: Dale Curtis <dalecurtis@chromium.org>
Date: Thu, 30 Apr 2020 15:16:31 -0700
Subject: [PATCH] [libavutil] Add saturated add/sub operations for int64_t.

Many places are using their own custom code for handling overflow
around timestamps or other int64_t values. There are enough of these
now that having some common saturated math functions seems sound.

This adds implementations that just use the builtin functions for
recent gcc, clang when available or implements its own version for
older compilers.

Signed-off-by: Dale Curtis <dalecurtis@chromium.org>
---
 libavutil/mathematics.c | 26 ++++++++++++++++++++++++++
 libavutil/mathematics.h | 19 +++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/libavutil/mathematics.c b/libavutil/mathematics.c
index 0485db7222..a73a2364df 100644
--- a/libavutil/mathematics.c
+++ b/libavutil/mathematics.c
@@ -213,3 +213,29 @@ int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t i
         return av_rescale_q(old + 1, inc_tb, ts_tb) + (ts - old_ts);
     }
 }
+
+int64_t av_saturated_add(int64_t a, int64_t b) {
+#if AV_GCC_VERSION_AT_LEAST(5,0) || defined(__clang__)
+  int64_t tmp;
+  return !__builtin_add_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN);
+#else
+  if (b >= 0 && a >= INT64_MAX - b)
+    return INT64_MAX;
+  if (b <= 0 && a <= INT64_MIN - b)
+    return INT64_MIN;
+  return a + b;
+#endif
+}
+
+int64_t av_saturated_sub(int64_t a, int64_t b) {
+#if AV_GCC_VERSION_AT_LEAST(5,0) || defined(__clang__)
+  int64_t tmp;
+  return !__builtin_sub_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN);
+#else
+  if (b <= 0 && a >= INT64_MAX + b) {
+    return INT64_MAX;
+  if (b >= 0 && a <= INT64_MIN + b) {
+    return INT64_MIN;
+  return a - b;
+#endif
+}
diff --git a/libavutil/mathematics.h b/libavutil/mathematics.h
index 54901800ba..1f8b792e55 100644
--- a/libavutil/mathematics.h
+++ b/libavutil/mathematics.h
@@ -234,6 +234,25 @@ int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts,  AVRational fs_tb, int
  */
 int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t inc);
 
+/**
+ * Adds two values without overflowing; result will saturate to the range
+ * [INT64_MIN, INT64_MAX].
+ *
+ * @param a,b Operands
+ * @return a + b if result wouldn't overflow, INT64_MIN if the result overflows in the
+ * negative direction and INT64_MAX if the result overflows in the positive direction.
+ */
+int64_t av_saturated_add(int64_t a, int64_t b);
+
+/**
+ * Subtracts two values without overflowing; result will saturate to the range
+ * [INT64_MIN, INT64_MAX].
+ *
+ * @param a,b Operands
+ * @return a - b if result wouldn't overflow, INT64_MIN if the result overflows in the
+ * negative direction and INT64_MAX if the result overflows in the positive direction.
+ */
+int64_t av_saturated_sub(int64_t a, int64_t b);
 
 /**
  * @}
-- 
2.26.2.526.g744177e7f7-goog

