LCOV - code coverage report
Current view: top level - util/tmpl - fd_smallset.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 58 58 100.0 %
Date: 2025-07-01 05:00:49 Functions: 49 175 28.0 %

          Line data    Source code
       1             : /* Declare a header-only API for fast manipulation of index sets where
       2             :    the set itself is represented in a primitive unsigned integer type.
       3             :    Example:
       4             : 
       5             :      #define SET_NAME myset
       6             :      #include "util/tmpl/fd_smallset.c"
       7             : 
       8             :    will declare the following in the compile unit:
       9             : 
      10             :      enum { myset_MAX = 64 }; // maximum number of elements in set (for use in compile time constructors)
      11             : 
      12             :      // Set constructors
      13             :      myset_t myset_null   ( void           ); // return {}
      14             :      myset_t myset_full   ( void           ); // return ~{}
      15             :      myset_t myset_full_if( int c          ); // return c ? ~{} : {}
      16             :      myset_t myset_ele    ( ulong i        ); // return { i }          // Assumes 0<=i<max
      17             :      myset_t myset_ele_if ( int c, ulong i ); // return c ? { i } : {} // Assumes 0<=i<max
      18             : 
      19             :      // Index operations
      20             :      ulong myset_max  ( void      ); // return the maximum number of elements that can be held by the set,
      21             :                                      // will be in (0,8*sizeof(myset_t)]
      22             :      ulong myset_cnt  ( myset_t x ); // return the current number of elements in the set, will be in [0,myset_max()]
      23             :      ulong myset_first( myset_t x ); // return the index of the first element in the set, will be in [0,myset_max()),
      24             :                                      // U.B. if set is null
      25             : 
      26             :      // Boolean operations
      27             :      int myset_valid_idx( ulong i              ); // returns 1 if i is a valid set index (i.e. idx < myset_max())
      28             :      int myset_valid    ( myset_t x            ); // returns 1 if x a valid set (i.e. no bits idx >= myset_max() are set)
      29             :      int myset_is_null  ( myset_t x            ); // returns 1 if x is the null set
      30             :      int myset_is_full  ( myset_t x            ); // returns 1 if x is the full set
      31             :      int myset_test     ( myset_t x, ulong i   ); // returns 1 if element i is in set x
      32             :      int myset_eq       ( myset_t x, myset_t y ); // returns 1 if x and y are the same sets
      33             :      int myset_subset   ( myset_t x, myset_t y ); // returns 1 if x is a subset of y
      34             : 
      35             :      // Unary operations
      36             :      myset_t myset_copy      ( myset_t x ); // returns x
      37             :      myset_t myset_complement( myset_t x ); // returns ~x
      38             : 
      39             :      // Binary operations
      40             :      myset_t myset_union    ( myset_t x, myset_t y ); // returns x u y
      41             :      myset_t myset_intersect( myset_t x, myset_t y ); // returns x n y
      42             :      myset_t myset_subtract ( myset_t x, myset_t y ); // returns x - y
      43             :      myset_t myset_xor      ( myset_t x, myset_t y ); // returns (x u y) - (x n y)
      44             : 
      45             :      // Trinary operations
      46             :      myset_t myset_if( int c, myset_t t, myset_t f ); // returns c ? t : f
      47             : 
      48             :      // Iteration
      49             :      //
      50             :      // for( myset_iter_t iter=myset_iter_init(set); !myset_iter_done(iter); iter=myset_iter_next(iter) ) {
      51             :      //   ulong idx = myset_iter_idx(iter);
      52             :      //   ... process element idx of set here
      53             :      //   ... do not touch iter
      54             :      // }
      55             :      //
      56             :      // will efficiently iterate over the elements of set in ascending
      57             :      // order.
      58             : 
      59             :      myset_iter_t myset_iter_init( myset_t      set  );
      60             :      myset_iter_t myset_iter_done( myset_iter_t iter );
      61             :      myset_iter_t myset_iter_next( myset_iter_t iter );
      62             :      ulong        myset_iter_idx ( myset_iter_t iter );
      63             : 
      64             :      // Misc
      65             : 
      66             :      myset_t myset_insert( myset_t x, ulong i ); // short for myset_union   ( x, myset_ele( i ) )
      67             :      myset_t myset_remove( myset_t x, ulong i ); // short for myset_subtract( x, myset_ele( i ) )
      68             : 
      69             :      myset_t myset_insert_if( int c, myset_t x, ulong i ); // Fast impl of "c ? myset_insert( x, i ) : x;"
      70             :      myset_t myset_remove_if( int c, myset_t x, ulong i ); // Fast impl of "c ? myset_remove( x, i ) : x;"
      71             : 
      72             :      // Range APIs do fast O(1) operations for a contiguous range within
      73             :      // a myset.  Ranges are specified on the half-open interval [l,h).
      74             :      // These all assume 0<=l<=h<=max.
      75             : 
      76             :      myset_t myset_range( ulong l, ulong h );                   // returns set containing elements [l,h).
      77             : 
      78             :      myset_t myset_insert_range( myset_t x, ulong l, ulong h ); // returns myset_union    ( x, myset_range( l, h ) )
      79             :      myset_t myset_select_range( myset_t x, ulong l, ulong h ); // returns myset_intersect( x, myset_range( l, h ) )
      80             :      myset_t myset_remove_range( myset_t x, ulong l, ulong h ); // returns myset_subtract ( x, myset_range( l, h ) )
      81             : 
      82             :      ulong myset_range_cnt( myset_t x, ulong l, ulong h );      // returns myset_cnt( myset_select_range( x, l, h ) )
      83             : 
      84             :      // With the exceptions of myidx_valid_idx and myset_valid, all
      85             :      // these assume their inputs are valid and produce valid well
      86             :      // defined outputs unless explicitly noted otherwise
      87             : 
      88             :    This is safe for multiple inclusion and other options exist for fine
      89             :    tuning described below. */
      90             : 
      91             : #include "../bits/fd_bits.h"
      92             : 
      93             : #ifndef SET_NAME
      94             : #error "Define SET_NAME"
      95             : #endif
      96             : 
      97             : /* SET_TYPE is a type that behaves like a primitive integral type and
      98             :    is efficient to pass around by value.  Defaults to ulong. */
      99             : 
     100             : #ifndef SET_TYPE
     101             : #define SET_TYPE ulong
     102             : #endif
     103             : 
     104             : /* SET_MAX is an integer expression that gives the maximum number of
     105             :    elements this set can hold.  Should be [1,WIDTH_SET_TYPE].  Defaults
     106             :    to the number of bits in SET_TYPE. */
     107             : 
     108             : #ifndef SET_MAX
     109             : #define SET_MAX (8*(int)sizeof(SET_TYPE))
     110             : #endif
     111             : 
     112             : /* SET_IDX_T is the integral type used to index set elements.  Defaults
     113             :    to ulong. */
     114             : 
     115             : #ifndef SET_IDX_T
     116             : #define SET_IDX_T ulong
     117             : #endif
     118             : 
     119             : /* Define SET_POPCNT, SET_FIND_LSB AND SET_POP_LSB to the most efficient
     120             :    way to compute the population count of a small set.  Defaults to
     121             :    corresponding APIs for the SET_TYPE in fd_bits. */
     122             : 
     123             : #ifndef SET_POPCNT
     124    26322145 : #define SET_POPCNT FD_EXPAND_THEN_CONCAT3(fd_,SET_TYPE,_popcnt)
     125             : #endif
     126             : 
     127             : #ifndef SET_FIND_LSB
     128    26209214 : #define SET_FIND_LSB FD_EXPAND_THEN_CONCAT3(fd_,SET_TYPE,_find_lsb)
     129             : #endif
     130             : 
     131             : #ifndef SET_POP_LSB
     132       23814 : #define SET_POP_LSB FD_EXPAND_THEN_CONCAT3(fd_,SET_TYPE,_pop_lsb)
     133             : #endif
     134             : 
     135             : #if FD_TMPL_USE_HANDHOLDING
     136             : #include "../log/fd_log.h"
     137             : #endif
     138             : 
     139             : /* Implementation *****************************************************/
     140             : 
     141      859596 : #define SET_(x)FD_EXPAND_THEN_CONCAT3(SET_NAME,_,x)
     142             : 
     143             : enum {
     144             :   SET_(MAX)              = (SET_MAX),
     145             :   SET_(PRIVATE_BIT_CNT)  = 8*(int)sizeof(SET_TYPE),
     146             :   SET_(PRIVATE_ZP_CNT)   = SET_(PRIVATE_BIT_CNT) - SET_(MAX),
     147             :   SET_(PRIVATE_IDX_MASK) = SET_(PRIVATE_BIT_CNT) - 1
     148             : };
     149             : 
     150             : FD_STATIC_ASSERT( 0<SET_(MAX) && SET_(MAX)<=SET_(PRIVATE_BIT_CNT),              range );
     151             : FD_STATIC_ASSERT( (ulong)SET_(PRIVATE_BIT_CNT)<=(1UL<<(8*sizeof(SET_IDX_T)-1)), range );
     152             : 
     153             : typedef SET_TYPE SET_(t);
     154             : typedef SET_TYPE SET_(iter_t);
     155             : 
     156             : FD_PROTOTYPES_BEGIN
     157             : 
     158       64176 : FD_FN_CONST static inline SET_(t) SET_(null)( void ) { return (SET_(t))0; }
     159             : 
     160       54702 : FD_FN_CONST static inline SET_(t) SET_(full)( void ) { return (SET_(t))((((SET_(t))~((SET_(t))0))) >> SET_(PRIVATE_ZP_CNT)); }
     161             : 
     162             : FD_FN_CONST static inline SET_(t)
     163           6 : SET_(full_if)( int c ) {
     164           6 :   return (SET_(t))(((SET_(t))(((SET_(t))!c)-((SET_(t))1))) & SET_(full)());
     165           6 : }
     166             : 
     167             : /* Handles >=0 for negative types too */
     168         378 : FD_FN_CONST static inline int SET_(valid_idx)( SET_IDX_T i ) { return ((ulong)(long)i)<((ulong)SET_(MAX)); }
     169             : 
     170             : FD_FN_CONST static inline SET_(t)
     171    60321112 : SET_(ele)( SET_IDX_T i ) {
     172             : # if FD_TMPL_USE_HANDHOLDING
     173             :   if( FD_UNLIKELY( !SET_(valid_idx)( i ) ) ) FD_LOG_CRIT(( "invalid index" ));
     174             : # endif
     175    60321112 :   return (SET_(t))(((SET_(t))1) << i);
     176    60321112 : }
     177             : 
     178             : FD_FN_CONST static inline SET_(t)
     179      396522 : SET_(ele_if)( int c, SET_IDX_T i ) {
     180             : # if FD_TMPL_USE_HANDHOLDING
     181             :   if( FD_UNLIKELY( !SET_(valid_idx)( i ) ) ) FD_LOG_CRIT(( "invalid index" ));
     182             : # endif
     183      396522 :   return (SET_(t))(((SET_(t))!!c) << i);
     184      396522 : }
     185             : 
     186           3 : FD_FN_CONST static inline SET_IDX_T SET_(max)  ( void      ) { return (SET_IDX_T)SET_(MAX);       }
     187    26315905 : FD_FN_CONST static inline SET_IDX_T SET_(cnt)  ( SET_(t) x ) { return (SET_IDX_T)SET_POPCNT(x);   }
     188    26185400 : FD_FN_CONST static inline SET_IDX_T SET_(first)( SET_(t) x ) { return (SET_IDX_T)SET_FIND_LSB(x); }
     189             : 
     190        1512 : FD_FN_CONST static inline int SET_(valid)    ( SET_(t)   x ) { return !(x & (SET_(t))~SET_(full)()); }
     191      750441 : FD_FN_CONST static inline int SET_(is_null)  ( SET_(t)   x ) { return !x;                            }
     192       52230 : FD_FN_CONST static inline int SET_(is_full)  ( SET_(t)   x ) { return x==SET_(full)();               }
     193             : 
     194             : FD_FN_CONST static inline int
     195    21780213 : SET_(test) ( SET_(t) x, SET_IDX_T i ) {
     196             : # if FD_TMPL_USE_HANDHOLDING
     197             :   if( FD_UNLIKELY( !SET_(valid_idx)( i ) ) ) FD_LOG_CRIT(( "invalid index" ));
     198             : # endif
     199    21780213 :   return (int)((x>>i) & ((SET_(t))1));
     200    21780213 : }
     201             : 
     202       52560 : FD_FN_CONST static inline int SET_(eq)       ( SET_(t)   x, SET_(t)   y ) { return x==y;       }
     203        3024 : FD_FN_CONST static inline int SET_(subset)   ( SET_(t)   x, SET_(t)   y ) { return x==(x & y); }
     204             : 
     205         756 : FD_FN_CONST static inline SET_(t) SET_(copy)      ( SET_(t) x ) { return x;                }
     206         945 : FD_FN_CONST static inline SET_(t) SET_(complement)( SET_(t) x ) { return x ^ SET_(full)(); }
     207             : 
     208        9453 : FD_FN_CONST static inline SET_(t) SET_(union)    ( SET_(t) x, SET_(t) y ) { return x | y;  }
     209      758934 : FD_FN_CONST static inline SET_(t) SET_(intersect)( SET_(t) x, SET_(t) y ) { return x & y;  }
     210        9453 : FD_FN_CONST static inline SET_(t) SET_(subtract) ( SET_(t) x, SET_(t) y ) { return x & (SET_(t))~y; }
     211        3024 : FD_FN_CONST static inline SET_(t) SET_(xor)      ( SET_(t) x, SET_(t) y ) { return x ^ y;  }
     212             : 
     213        6048 : FD_FN_CONST static inline SET_(t) SET_(if)( int c, SET_(t) t, SET_(t) f ) { return c ? t : f; }
     214             : 
     215         756 : FD_FN_CONST static inline SET_(iter_t) SET_(iter_init)( SET_(t)      set  ) { return set;                             }
     216       24570 : FD_FN_CONST static inline SET_(iter_t) SET_(iter_done)( SET_(iter_t) iter ) { return !iter;                           }
     217       23814 : FD_FN_CONST static inline SET_(iter_t) SET_(iter_next)( SET_(iter_t) iter ) { return SET_POP_LSB(  iter );            }
     218       23814 : FD_FN_CONST static inline SET_IDX_T    SET_(iter_idx) ( SET_(iter_t) iter ) { return (SET_IDX_T)SET_FIND_LSB( iter ); }
     219             : 
     220      202971 : FD_FN_CONST static inline SET_(t) SET_(insert)( SET_(t) x, SET_IDX_T i ) { return x | SET_(ele)(i); }
     221         945 : FD_FN_CONST static inline SET_(t) SET_(remove)( SET_(t) x, SET_IDX_T i ) { return x & (SET_(t))~SET_(ele)(i); }
     222             : 
     223      394632 : FD_FN_CONST static inline SET_(t) SET_(insert_if)( int c, SET_(t) x, SET_IDX_T i ) { return x |  SET_(ele_if)(c,i); }
     224        1512 : FD_FN_CONST static inline SET_(t) SET_(remove_if)( int c, SET_(t) x, SET_IDX_T i ) { return x & (SET_(t))~SET_(ele_if)(c,i); }
     225             : 
     226       31200 : FD_FN_CONST static inline SET_(t) SET_(range)( SET_IDX_T l, SET_IDX_T h ) {
     227             : # if FD_TMPL_USE_HANDHOLDING
     228             :   /* Note: the 0<=l test is commented because compilers make babies cry
     229             :      with superfluous warnings when SET_IDX_T is an unsigned type. */
     230             :   if( FD_UNLIKELY( !( /*(((SET_(t))0)<=l) &*/ (l<=h) & (h<=SET_(max)()) ) ) ) FD_LOG_CRIT(( "invalid range" ));
     231             : # endif
     232             :   /* Compute (2^h) - (2^l) == ones for bits [l,h), with no UB in the
     233             :      case where l and/or h == BIT_CNT */
     234       31200 :   return (SET_(t))( (((SET_(t))(h<=(SET_IDX_T)SET_(PRIVATE_IDX_MASK))) << (h & (SET_IDX_T)SET_(PRIVATE_IDX_MASK)))
     235       31200 :                   - (((SET_(t))(l<=(SET_IDX_T)SET_(PRIVATE_IDX_MASK))) << (l & (SET_IDX_T)SET_(PRIVATE_IDX_MASK))) );
     236       31200 : }
     237             : 
     238        6240 : FD_FN_CONST static inline SET_(t) SET_(insert_range)( SET_(t) x, SET_IDX_T l, SET_IDX_T h ) {
     239        6240 :   return x | SET_(range)(l,h);
     240        6240 : }
     241             : 
     242        6240 : FD_FN_CONST static inline SET_(t) SET_(select_range)( SET_(t) x, SET_IDX_T l, SET_IDX_T h ) {
     243        6240 :   return x & SET_(range)(l,h);
     244        6240 : }
     245             : 
     246        6240 : FD_FN_CONST static inline SET_(t) SET_(remove_range)( SET_(t) x, SET_IDX_T l, SET_IDX_T h ) {
     247        6240 :   return x & (SET_(t))~SET_(range)(l,h);
     248        6240 : }
     249             : 
     250        6240 : FD_FN_CONST static inline SET_IDX_T SET_(range_cnt)( SET_(t) x, SET_IDX_T l, SET_IDX_T h ) {
     251        6240 :   return (SET_IDX_T)SET_POPCNT( x & SET_(range)(l,h) );
     252        6240 : }
     253             : 
     254             : FD_PROTOTYPES_END
     255             : 
     256             : #undef SET_
     257             : 
     258             : #undef SET_POP_LSB
     259             : #undef SET_FIND_LSB
     260             : #undef SET_POPCNT
     261             : #undef SET_MAX
     262             : #undef SET_TYPE
     263             : #undef SET_NAME

Generated by: LCOV version 1.14