LCOV - code coverage report
Current view: top level - src/include/common - int128.h (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 13 13 100.0 %
Date: 2024-04-24 06:11:09 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * int128.h
       4             :  *    Roll-our-own 128-bit integer arithmetic.
       5             :  *
       6             :  * We make use of the native int128 type if there is one, otherwise
       7             :  * implement things the hard way based on two int64 halves.
       8             :  *
       9             :  * See src/tools/testint128.c for a simple test harness for this file.
      10             :  *
      11             :  * Copyright (c) 2017-2024, PostgreSQL Global Development Group
      12             :  *
      13             :  * src/include/common/int128.h
      14             :  *
      15             :  *-------------------------------------------------------------------------
      16             :  */
      17             : #ifndef INT128_H
      18             : #define INT128_H
      19             : 
      20             : /*
      21             :  * For testing purposes, use of native int128 can be switched on/off by
      22             :  * predefining USE_NATIVE_INT128.
      23             :  */
      24             : #ifndef USE_NATIVE_INT128
      25             : #ifdef HAVE_INT128
      26             : #define USE_NATIVE_INT128 1
      27             : #else
      28             : #define USE_NATIVE_INT128 0
      29             : #endif
      30             : #endif
      31             : 
      32             : 
      33             : #if USE_NATIVE_INT128
      34             : 
      35             : typedef int128 INT128;
      36             : 
      37             : /*
      38             :  * Add an unsigned int64 value into an INT128 variable.
      39             :  */
      40             : static inline void
      41             : int128_add_uint64(INT128 *i128, uint64 v)
      42             : {
      43             :     *i128 += v;
      44             : }
      45             : 
      46             : /*
      47             :  * Add a signed int64 value into an INT128 variable.
      48             :  */
      49             : static inline void
      50             : int128_add_int64(INT128 *i128, int64 v)
      51             : {
      52             :     *i128 += v;
      53             : }
      54             : 
      55             : /*
      56             :  * Add the 128-bit product of two int64 values into an INT128 variable.
      57             :  *
      58             :  * XXX with a stupid compiler, this could actually be less efficient than
      59             :  * the other implementation; maybe we should do it by hand always?
      60             :  */
      61             : static inline void
      62      253050 : int128_add_int64_mul_int64(INT128 *i128, int64 x, int64 y)
      63             : {
      64      253050 :     *i128 += (int128) x * (int128) y;
      65      253050 : }
      66             : 
      67             : /*
      68             :  * Compare two INT128 values, return -1, 0, or +1.
      69             :  */
      70             : static inline int
      71      127780 : int128_compare(INT128 x, INT128 y)
      72             : {
      73      127780 :     if (x < y)
      74       53460 :         return -1;
      75       74320 :     if (x > y)
      76       52742 :         return 1;
      77       21578 :     return 0;
      78             : }
      79             : 
      80             : /*
      81             :  * Widen int64 to INT128.
      82             :  */
      83             : static inline INT128
      84      257894 : int64_to_int128(int64 v)
      85             : {
      86      257894 :     return (INT128) v;
      87             : }
      88             : 
      89             : /*
      90             :  * Convert INT128 to int64 (losing any high-order bits).
      91             :  * This also works fine for casting down to uint64.
      92             :  */
      93             : static inline int64
      94        2334 : int128_to_int64(INT128 val)
      95             : {
      96        2334 :     return (int64) val;
      97             : }
      98             : 
      99             : #else                           /* !USE_NATIVE_INT128 */
     100             : 
     101             : /*
     102             :  * We lay out the INT128 structure with the same content and byte ordering
     103             :  * that a native int128 type would (probably) have.  This makes no difference
     104             :  * for ordinary use of INT128, but allows union'ing INT128 with int128 for
     105             :  * testing purposes.
     106             :  */
     107             : typedef struct
     108             : {
     109             : #ifdef WORDS_BIGENDIAN
     110             :     int64       hi;             /* most significant 64 bits, including sign */
     111             :     uint64      lo;             /* least significant 64 bits, without sign */
     112             : #else
     113             :     uint64      lo;             /* least significant 64 bits, without sign */
     114             :     int64       hi;             /* most significant 64 bits, including sign */
     115             : #endif
     116             : } INT128;
     117             : 
     118             : /*
     119             :  * Add an unsigned int64 value into an INT128 variable.
     120             :  */
     121             : static inline void
     122             : int128_add_uint64(INT128 *i128, uint64 v)
     123             : {
     124             :     /*
     125             :      * First add the value to the .lo part, then check to see if a carry needs
     126             :      * to be propagated into the .hi part.  A carry is needed if both inputs
     127             :      * have high bits set, or if just one input has high bit set while the new
     128             :      * .lo part doesn't.  Remember that .lo part is unsigned; we cast to
     129             :      * signed here just as a cheap way to check the high bit.
     130             :      */
     131             :     uint64      oldlo = i128->lo;
     132             : 
     133             :     i128->lo += v;
     134             :     if (((int64) v < 0 && (int64) oldlo < 0) ||
     135             :         (((int64) v < 0 || (int64) oldlo < 0) && (int64) i128->lo >= 0))
     136             :         i128->hi++;
     137             : }
     138             : 
     139             : /*
     140             :  * Add a signed int64 value into an INT128 variable.
     141             :  */
     142             : static inline void
     143             : int128_add_int64(INT128 *i128, int64 v)
     144             : {
     145             :     /*
     146             :      * This is much like the above except that the carry logic differs for
     147             :      * negative v.  Ordinarily we'd need to subtract 1 from the .hi part
     148             :      * (corresponding to adding the sign-extended bits of v to it); but if
     149             :      * there is a carry out of the .lo part, that cancels and we do nothing.
     150             :      */
     151             :     uint64      oldlo = i128->lo;
     152             : 
     153             :     i128->lo += v;
     154             :     if (v >= 0)
     155             :     {
     156             :         if ((int64) oldlo < 0 && (int64) i128->lo >= 0)
     157             :             i128->hi++;
     158             :     }
     159             :     else
     160             :     {
     161             :         if (!((int64) oldlo < 0 || (int64) i128->lo >= 0))
     162             :             i128->hi--;
     163             :     }
     164             : }
     165             : 
     166             : /*
     167             :  * INT64_AU32 extracts the most significant 32 bits of int64 as int64, while
     168             :  * INT64_AL32 extracts the least significant 32 bits as uint64.
     169             :  */
     170             : #define INT64_AU32(i64) ((i64) >> 32)
     171             : #define INT64_AL32(i64) ((i64) & UINT64CONST(0xFFFFFFFF))
     172             : 
     173             : /*
     174             :  * Add the 128-bit product of two int64 values into an INT128 variable.
     175             :  */
     176             : static inline void
     177             : int128_add_int64_mul_int64(INT128 *i128, int64 x, int64 y)
     178             : {
     179             :     /* INT64_AU32 must use arithmetic right shift */
     180             :     StaticAssertDecl(((int64) -1 >> 1) == (int64) -1,
     181             :                      "arithmetic right shift is needed");
     182             : 
     183             :     /*----------
     184             :      * Form the 128-bit product x * y using 64-bit arithmetic.
     185             :      * Considering each 64-bit input as having 32-bit high and low parts,
     186             :      * we can compute
     187             :      *
     188             :      *   x * y = ((x.hi << 32) + x.lo) * (((y.hi << 32) + y.lo)
     189             :      *         = (x.hi * y.hi) << 64 +
     190             :      *           (x.hi * y.lo) << 32 +
     191             :      *           (x.lo * y.hi) << 32 +
     192             :      *           x.lo * y.lo
     193             :      *
     194             :      * Each individual product is of 32-bit terms so it won't overflow when
     195             :      * computed in 64-bit arithmetic.  Then we just have to shift it to the
     196             :      * correct position while adding into the 128-bit result.  We must also
     197             :      * keep in mind that the "lo" parts must be treated as unsigned.
     198             :      *----------
     199             :      */
     200             : 
     201             :     /* No need to work hard if product must be zero */
     202             :     if (x != 0 && y != 0)
     203             :     {
     204             :         int64       x_u32 = INT64_AU32(x);
     205             :         uint64      x_l32 = INT64_AL32(x);
     206             :         int64       y_u32 = INT64_AU32(y);
     207             :         uint64      y_l32 = INT64_AL32(y);
     208             :         int64       tmp;
     209             : 
     210             :         /* the first term */
     211             :         i128->hi += x_u32 * y_u32;
     212             : 
     213             :         /* the second term: sign-extend it only if x is negative */
     214             :         tmp = x_u32 * y_l32;
     215             :         if (x < 0)
     216             :             i128->hi += INT64_AU32(tmp);
     217             :         else
     218             :             i128->hi += ((uint64) tmp) >> 32;
     219             :         int128_add_uint64(i128, ((uint64) INT64_AL32(tmp)) << 32);
     220             : 
     221             :         /* the third term: sign-extend it only if y is negative */
     222             :         tmp = x_l32 * y_u32;
     223             :         if (y < 0)
     224             :             i128->hi += INT64_AU32(tmp);
     225             :         else
     226             :             i128->hi += ((uint64) tmp) >> 32;
     227             :         int128_add_uint64(i128, ((uint64) INT64_AL32(tmp)) << 32);
     228             : 
     229             :         /* the fourth term: always unsigned */
     230             :         int128_add_uint64(i128, x_l32 * y_l32);
     231             :     }
     232             : }
     233             : 
     234             : /*
     235             :  * Compare two INT128 values, return -1, 0, or +1.
     236             :  */
     237             : static inline int
     238             : int128_compare(INT128 x, INT128 y)
     239             : {
     240             :     if (x.hi < y.hi)
     241             :         return -1;
     242             :     if (x.hi > y.hi)
     243             :         return 1;
     244             :     if (x.lo < y.lo)
     245             :         return -1;
     246             :     if (x.lo > y.lo)
     247             :         return 1;
     248             :     return 0;
     249             : }
     250             : 
     251             : /*
     252             :  * Widen int64 to INT128.
     253             :  */
     254             : static inline INT128
     255             : int64_to_int128(int64 v)
     256             : {
     257             :     INT128      val;
     258             : 
     259             :     val.lo = (uint64) v;
     260             :     val.hi = (v < 0) ? -INT64CONST(1) : INT64CONST(0);
     261             :     return val;
     262             : }
     263             : 
     264             : /*
     265             :  * Convert INT128 to int64 (losing any high-order bits).
     266             :  * This also works fine for casting down to uint64.
     267             :  */
     268             : static inline int64
     269             : int128_to_int64(INT128 val)
     270             : {
     271             :     return (int64) val.lo;
     272             : }
     273             : 
     274             : #endif                          /* USE_NATIVE_INT128 */
     275             : 
     276             : #endif                          /* INT128_H */

Generated by: LCOV version 1.14