]> granicus.if.org Git - postgresql/commitdiff
Add overflow-safe math inline functions for unsigned integers
authorMichael Paquier <michael@paquier.xyz>
Mon, 2 Sep 2019 00:38:23 +0000 (09:38 +0900)
committerMichael Paquier <michael@paquier.xyz>
Mon, 2 Sep 2019 00:38:23 +0000 (09:38 +0900)
Similarly to the signed versions added in 4d6ad31, this adds a set of
inline functions for overflow checks with unsigned integers, including
uint16, uint32 and uint64.  This relies on compiler built-in overflow
checks by default if available.  The behavior of unsigned integers is
well-defined so the fallback implementations checks are simple for
additions and subtractions.  Multiplications avoid division-based checks
which are expensive if possible, still this can happen for uint64 if
128-bit integers are not available.

While on it, the code in common/int.h is reorganized to avoid too many
duplicated comments.  The new macros will be used in a follow-up patch.

All thanks to Andres Freund for the input provided.

Author: Fabien Coelho, Michael Paquier
Discussion: https://postgr.es/m/20190830073423.GB2354@paquier.xyz

src/include/common/int.h

index d7547985438c0a0b94facb487f7022e9ee490ccc..8f262bc8b7f048d5b2a3aa6d64359b2cf316b294 100644 (file)
 #ifndef COMMON_INT_H
 #define COMMON_INT_H
 
-/*
- * If a + b overflows, return true, otherwise store the result of a + b into
- * *result. The content of *result is implementation defined in case of
+
+/*---------
+ * The following guidelines apply to all the routines:
+ * - If a + b overflows, return true, otherwise store the result of a + b
+ * into *result. The content of *result is implementation defined in case of
+ * overflow.
+ * - If a - b overflows, return true, otherwise store the result of a - b
+ * into *result. The content of *result is implementation defined in case of
  * overflow.
+ * - If a * b overflows, return true, otherwise store the result of a * b
+ * into *result. The content of *result is implementation defined in case of
+ * overflow.
+ *---------
+ */
+
+/*------------------------------------------------------------------------
+ * Overflow routines for signed integers
+ *------------------------------------------------------------------------
+ */
+
+/*
+ * INT16
  */
 static inline bool
 pg_add_s16_overflow(int16 a, int16 b, int16 *result)
@@ -43,11 +61,6 @@ pg_add_s16_overflow(int16 a, int16 b, int16 *result)
 #endif
 }
 
-/*
- * If a - b overflows, return true, otherwise store the result of a - b into
- * *result. The content of *result is implementation defined in case of
- * overflow.
- */
 static inline bool
 pg_sub_s16_overflow(int16 a, int16 b, int16 *result)
 {
@@ -66,11 +79,6 @@ pg_sub_s16_overflow(int16 a, int16 b, int16 *result)
 #endif
 }
 
-/*
- * If a * b overflows, return true, otherwise store the result of a * b into
- * *result. The content of *result is implementation defined in case of
- * overflow.
- */
 static inline bool
 pg_mul_s16_overflow(int16 a, int16 b, int16 *result)
 {
@@ -90,9 +98,7 @@ pg_mul_s16_overflow(int16 a, int16 b, int16 *result)
 }
 
 /*
- * If a + b overflows, return true, otherwise store the result of a + b into
- * *result. The content of *result is implementation defined in case of
- * overflow.
+ * INT32
  */
 static inline bool
 pg_add_s32_overflow(int32 a, int32 b, int32 *result)
@@ -112,11 +118,6 @@ pg_add_s32_overflow(int32 a, int32 b, int32 *result)
 #endif
 }
 
-/*
- * If a - b overflows, return true, otherwise store the result of a - b into
- * *result. The content of *result is implementation defined in case of
- * overflow.
- */
 static inline bool
 pg_sub_s32_overflow(int32 a, int32 b, int32 *result)
 {
@@ -135,11 +136,6 @@ pg_sub_s32_overflow(int32 a, int32 b, int32 *result)
 #endif
 }
 
-/*
- * If a * b overflows, return true, otherwise store the result of a * b into
- * *result. The content of *result is implementation defined in case of
- * overflow.
- */
 static inline bool
 pg_mul_s32_overflow(int32 a, int32 b, int32 *result)
 {
@@ -159,9 +155,7 @@ pg_mul_s32_overflow(int32 a, int32 b, int32 *result)
 }
 
 /*
- * If a + b overflows, return true, otherwise store the result of a + b into
- * *result. The content of *result is implementation defined in case of
- * overflow.
+ * INT64
  */
 static inline bool
 pg_add_s64_overflow(int64 a, int64 b, int64 *result)
@@ -190,11 +184,6 @@ pg_add_s64_overflow(int64 a, int64 b, int64 *result)
 #endif
 }
 
-/*
- * If a - b overflows, return true, otherwise store the result of a - b into
- * *result. The content of *result is implementation defined in case of
- * overflow.
- */
 static inline bool
 pg_sub_s64_overflow(int64 a, int64 b, int64 *result)
 {
@@ -222,11 +211,6 @@ pg_sub_s64_overflow(int64 a, int64 b, int64 *result)
 #endif
 }
 
-/*
- * If a * b overflows, return true, otherwise store the result of a * b into
- * *result. The content of *result is implementation defined in case of
- * overflow.
- */
 static inline bool
 pg_mul_s64_overflow(int64 a, int64 b, int64 *result)
 {
@@ -270,4 +254,184 @@ pg_mul_s64_overflow(int64 a, int64 b, int64 *result)
 #endif
 }
 
+/*------------------------------------------------------------------------
+ * Overflow routines for unsigned integers
+ *------------------------------------------------------------------------
+ */
+
+/*
+ * UINT16
+ */
+static inline bool
+pg_add_u16_overflow(uint16 a, uint16 b, uint16 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_add_overflow(a, b, result);
+#else
+       uint16          res = a + b;
+
+       if (res < a)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = res;
+       return false;
+#endif
+}
+
+static inline bool
+pg_sub_u16_overflow(uint16 a, uint16 b, uint16 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_sub_overflow(a, b, result);
+#else
+       if (b > a)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = a - b;
+       return false;
+#endif
+}
+
+static inline bool
+pg_mul_u16_overflow(uint16 a, uint16 b, uint16 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_mul_overflow(a, b, result);
+#else
+       uint32          res = (uint32) a * (uint32) b;
+
+       if (res > PG_UINT16_MAX)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = (uint16) res;
+       return false;
+#endif
+}
+
+/*
+ * INT32
+ */
+static inline bool
+pg_add_u32_overflow(uint32 a, uint32 b, uint32 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_add_overflow(a, b, result);
+#else
+       uint32          res = a + b;
+
+       if (res < a)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = res;
+       return false;
+#endif
+}
+
+static inline bool
+pg_sub_u32_overflow(uint32 a, uint32 b, uint32 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_sub_overflow(a, b, result);
+#else
+       if (b > a)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = a - b;
+       return false;
+#endif
+}
+
+static inline bool
+pg_mul_u32_overflow(uint32 a, uint32 b, uint32 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_mul_overflow(a, b, result);
+#else
+       uint64          res = (uint64) a * (uint64) b;
+
+       if (res > PG_UINT32_MAX)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = (uint32) res;
+       return false;
+#endif
+}
+
+/*
+ * UINT64
+ */
+static inline bool
+pg_add_u64_overflow(uint64 a, uint64 b, uint64 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_add_overflow(a, b, result);
+#else
+       uint64          res = a + b;
+
+       if (res < a)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = res;
+       return false;
+#endif
+}
+
+static inline bool
+pg_sub_u64_overflow(uint64 a, uint64 b, uint64 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_sub_overflow(a, b, result);
+#else
+       if (b > a)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = a - b;
+       return false;
+#endif
+}
+
+static inline bool
+pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *result)
+{
+#if defined(HAVE__BUILTIN_OP_OVERFLOW)
+       return __builtin_mul_overflow(a, b, result);
+#elif defined(HAVE_INT128)
+       uint128         res = (uint128) a * (uint128) b;
+
+       if (res > PG_UINT64_MAX)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = (uint64) res;
+       return false;
+#else
+       uint64          res = a * b;
+
+       if (a != 0 && b != res / a)
+       {
+               *result = 0x5EED;               /* to avoid spurious warnings */
+               return true;
+       }
+       *result = res;
+       return false;
+#endif
+}
+
 #endif                                                 /* COMMON_INT_H */