LCOV - code coverage report
Current view: top level - util/tmpl - fd_set.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 217 217 100.0 %
Date: 2025-09-18 04:41:32 Functions: 95 10710 0.9 %

          Line data    Source code
       1             : /* Declare a bunch of functions for fast manipulation of index sets that
       2             :    can contain a large compile time bounded number of elements and that
       3             :    can be shared between processes.  The implementation is optimized for
       4             :    dense-ish sets with a largish maximum element contains (thousands+).
       5             :    Example:
       6             : 
       7             :      #define SET_NAME my_set
       8             :      #define SET_MAX  65536   ... Should be in [1,2^31-64]
       9             :      #include "util/tmpl/fd_set.c"
      10             : 
      11             :    Will implement and expose the following header only library in the
      12             :    local compilation unit:
      13             : 
      14             :      my_set_t // opaque handle type for the set
      15             : 
      16             :      // interprocess shared memory constructors / destructors
      17             :      // these obey the usual conventions
      18             : 
      19             :      ulong      my_set_align    ( void             ); // required byte alignment of a my_set_t
      20             :      ulong      my_set_footprint( void             ); // required byte footprint of a my_set_t
      21             :      void *     my_set_new      ( void     * shmem ); // format memory region into a my_set, my_set will be empty
      22             :                                                       // (caller not joined on return, mem has required align/footprint, etc)
      23             :      my_set_t * my_set_join     ( void     * shset ); // join a my_set_t (unlimited joins, etc)
      24             :      void *     my_set_leave    ( my_set_t *   set ); // leave a my_set_t (matched with join, etc)
      25             :      void *     my_set_delete   ( void     * shset ); // unformat memory (no active joins, etc)
      26             : 
      27             :      // it is also possible to do declaration based allocation via
      28             : 
      29             :      my_set_t set[ my_set_word_cnt ];
      30             : 
      31             :      // Returns 1 if set appears to be point to a valid set, 0 otherwise
      32             :      // (e.g. set is NULL, there's corruption in the set zero padding,
      33             :      // etc).
      34             : 
      35             :      int my_set_valid( my_set_t const * set )
      36             : 
      37             :      // Returns 1 if idx is a valid set element index (i.e. in [0,max)
      38             :      // (It is okay to pass NULL for set here as it is technically
      39             :      // ignored).
      40             : 
      41             :      int my_set_valid_idx( my_set_t const * set, ulong idx )
      42             : 
      43             :      // Return the maximum number of elements this set can contain.  Set
      44             :      // elements are indexed [0,max).  (It is okay to pass NULL for set
      45             :      // here as it is technically ignored.)
      46             : 
      47             :      ulong my_set_max( my_set_t const * set ); // Return positive
      48             : 
      49             :      // Return the current number of elements this set contains
      50             : 
      51             :      ulong my_set_cnt( my_set_t const * set ); // Return in [0,max]
      52             : 
      53             :      // Return 1 if set contains no elements and 0 if not
      54             : 
      55             :      int my_set_is_null( my_set_t const * set );
      56             : 
      57             :      // Return 1 if set contains all elements and 0 if not
      58             : 
      59             :      int my_set_is_full( my_set_t const * set );
      60             : 
      61             :      // Return the lowest indexed element in the set
      62             : 
      63             :      ulong my_set_first( my_set_t const * set ); // Return in [0,max) on success, >=max if empty set
      64             : 
      65             :      // Return the highest indexed element in the set
      66             : 
      67             :      ulong my_set_last( my_set_t const * set );  // Return in [0,max) on success, >=max if empty set
      68             : 
      69             :      // Two pair of functions for writing efficient iterators over all
      70             :      // members of sparse sets.  The first pair is a destructive
      71             :      // iterator:
      72             :      //
      73             :      //   for( ulong idx=my_set_iter_init( set ); !my_set_iter_done( idx ); idx=my_set_iter_next( set, idx ) ) {
      74             :      //     ... idx is the next element of the set, will be in [0,max)
      75             :      //     ... set elements will be iterated over in increasing order
      76             :      //     ... do not modify set, modify idx; there are no elements
      77             :      //     ... in set before idx at this point
      78             :      //   }
      79             :      //   ... set will be empty at this point
      80             :      //
      81             :      // The second pair is a non-destructive iterator:
      82             :      //
      83             :      //   for( ulong idx=my_set_const_iter_init( set ); !my_set_const_iter_done( idx ); idx=my_set_const_iter_next( set, idx ) ) {
      84             :      //     ... idx is the next element of the set, will be in [0,max)
      85             :      //     ... set elements will be iterated over in increasing order
      86             :      //     ... do not modify set or modify idx; set is unchanged
      87             :      //     ... at this point
      88             :      //   }
      89             :      //   ... set is unchanged at this point
      90             :      //
      91             :      // The performance difference between the two styles are situation
      92             :      // dependent (depends on the sizes of the set, cache conditions,
      93             :      // distribution of elements in the set, etc) but not expected to be
      94             :      // large.  In general though, the above iterators are best for very
      95             :      // sparse sets.  For dense sets, the idiom:
      96             :      //
      97             :      //   ulong max = my_set_max( set );
      98             :      //   for( ulong idx=0UL; idx<max; idx++ ) {
      99             :      //     if( !my_set_test( set, idx ) ) continue;
     100             :      //     ... idx is the next element of the set, will be in [0,max)
     101             :      //     ... set elements will be iterated over in increasing order
     102             :      //     ... do not modify set or modify idx; set is unchanged
     103             :      //     ... at this point
     104             :      //   }
     105             :      //   ... set is unchanged at this point
     106             :      //
     107             :      // or is more predictable branchless speculative variant:
     108             :      //
     109             :      //   ulong max = my_set_max( set );
     110             :      //   for( ulong idx=0UL; idx<max; idx++ ) {
     111             :      //     ... speculate idx is in the set, will be in [0,max)
     112             :      //     ... set elements will be iterated over in increasing order
     113             :      //     ... do not modify set or modify idx; set is unchanged
     114             :      //     ... at this point
     115             :      //     ... commit speculation when my_set_test( set, idx ) is true
     116             :      //   }
     117             :      //   ... set is unchanged at this point
     118             :      //
     119             :      // might be preferable.
     120             : 
     121             :      ulong my_set_iter_init( my_set_t * set );
     122             :      int   my_set_iter_done( ulong idx );
     123             :      ulong my_set_iter_next( my_set_t * set, ulong idx );
     124             : 
     125             :      ulong my_set_const_iter_init( my_set_t const * set );
     126             :      int   my_set_const_iter_done( ulong idx );
     127             :      ulong my_set_const_iter_next( my_set_t const * set, ulong idx );
     128             : 
     129             :      // Insert/remove element idx to a set if not already present (no-op
     130             :      // otherwise).  Returns set.
     131             : 
     132             :      my_set_t * my_set_insert( my_set_t * set, ulong idx ); // in [0,max).
     133             :      my_set_t * my_set_remove( my_set_t * set, ulong idx ); // in [0,max).
     134             : 
     135             :      // Fast version of "c ? my_set_{insert,remove}( set, idx ) : set".
     136             : 
     137             :      my_set_t * my_set_insert_if( my_set_t * set, int c, ulong idx ); // in [0,max).
     138             :      my_set_t * my_set_remove_if( my_set_t * set, int c, ulong idx ); // in [0,max).
     139             : 
     140             :      // Return 1 if idx is in set and 0 otherwise
     141             : 
     142             :      int my_set_test( my_set_t const * set, ulong idx ); // in [0,max).
     143             : 
     144             :      // Returns 1 if x and y contain the same elements, 0 otherwise.  In
     145             :      // place is fine.
     146             : 
     147             :      int my_set_eq( my_set_t const * x, my_set_t const * y );
     148             : 
     149             :      // Returns 1 if x is a subset of y (including x==y), 0 otherwise.
     150             :      // In place is fine.
     151             : 
     152             :      int my_set_subset( my_set_t const * x, my_set_t const * y );
     153             : 
     154             :      // Operations that populate an output set.  All of these return z
     155             :      // and inplace operation is fine.
     156             : 
     157             :      my_set_t * my_set_null      ( my_set_t * z );                                                // z =  {}
     158             :      my_set_t * my_set_full      ( my_set_t * z );                                                // z = ~{}
     159             :      my_set_t * my_set_full_if   ( my_set_t * z, int c );                                         // z = c ? {idx} : {}
     160             :      my_set_t * my_set_ele       ( my_set_t * z, ulong idx );                                     // z = {idx}
     161             :      my_set_t * my_set_ele_if    ( my_set_t * z, int c, ulong idx );                              // z = c ? {idx} : {}
     162             :      my_set_t * my_set_copy      ( my_set_t * z, my_set_t const * x );                            // z = x
     163             :      my_set_t * my_set_complement( my_set_t * z, my_set_t const * x );                            // z = ~x
     164             :      my_set_t * my_set_union     ( my_set_t * z, my_set_t const * x, my_set_t const * y );        // z = x u y
     165             :      my_set_t * my_set_intersect ( my_set_t * z, my_set_t const * x, my_set_t const * y );        // z = x n y
     166             :      my_set_t * my_set_subtract  ( my_set_t * z, my_set_t const * x, my_set_t const * y );        // z = x - y
     167             :      my_set_t * my_set_xor       ( my_set_t * z, my_set_t const * x, my_set_t const * y );        // z = (x u y) - (x n y)
     168             :      my_set_t * my_set_if        ( my_set_t * z, int c, my_set_t const * x, my_set_t const * y ); // z = c ? x : y
     169             : 
     170             :      // Range APIs do fast operations for a contiguous range within a
     171             :      // my_set.  Ranges are specified on the half-open interval [l,h).
     172             :      // These all assume 0<=l<=h<=max.
     173             : 
     174             :      my_set_t * my_set_range( my_set_t * z, ulong l, ulong h );        // z = r where r is the set with elements [l,h), fast O(max)
     175             : 
     176             :      my_set_t * my_set_insert_range( my_set_t * z, ulong l, ulong h ); // z = z u r, fast O(h-l)
     177             :      my_set_t * my_set_select_range( my_set_t * z, ulong l, ulong h ); // z = z n r, fast O(max-(h-l))
     178             :      my_set_t * my_set_remove_range( my_set_t * z, ulong l, ulong h ); // z = z - r, fast O(h-l)
     179             : 
     180             :      ulong my_set_range_cnt( my_set_t const * x, ulong l, ulong h );   // returns cnt( z n r ), in [0,h-l], fast O(h-l)
     181             : 
     182             :    With the exception of my_set_valid_idx and my_set_valid, all of these
     183             :    assume the inputs are value and will produce strictly valid outputs
     184             :    unless otherwise explicitly noted. */
     185             : 
     186             : #include "../bits/fd_bits.h"
     187             : 
     188             : #ifndef SET_NAME
     189             : #error "Define SET_NAME"
     190             : #endif
     191             : 
     192             : #ifndef SET_MAX
     193             : #error "Define SET_MAX"
     194             : #endif
     195             : 
     196             : FD_STATIC_ASSERT( 1<=SET_MAX && SET_MAX<=2147483584 /* 2^31-64 */, invalid_set_max );
     197             : 
     198             : #if FD_TMPL_USE_HANDHOLDING
     199             : #include "../log/fd_log.h"
     200             : #endif
     201             : 
     202             : /* Implementation *****************************************************/
     203             : 
     204  1844584050 : #define SET_(x) FD_EXPAND_THEN_CONCAT3(SET_NAME,_,x)
     205             : 
     206             : typedef ulong SET_(t);
     207             : 
     208             : enum { SET_(word_cnt) = (((int)(SET_MAX))+63)>>6 };
     209             : 
     210             : FD_PROTOTYPES_BEGIN
     211             : 
     212             : /* Private APIs *******************************************************/
     213             : 
     214             : FD_FN_CONST static inline ulong
     215      555912 : SET_(private_full_last_word)( void ) {
     216      555912 :   return fd_ulong_mask_lsb( SET_MAX - ((SET_(word_cnt)-1)<<6) );
     217      555912 : }
     218             : 
     219             : /* Public APIs ********************************************************/
     220             : 
     221      333315 : FD_FN_CONST static inline ulong SET_(align)    ( void ) { return alignof(ulong); }
     222         840 : FD_FN_CONST static inline ulong SET_(footprint)( void ) { return 8UL*(ulong)SET_(word_cnt); }
     223             : 
     224             : static inline void *
     225         801 : SET_(new)( void * shmem ) {
     226             : # if FD_TMPL_USE_HANDHOLDING
     227             :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, SET_(align)() ) ) ) FD_LOG_CRIT(( "unaligned shmem" ));
     228             : # endif
     229         801 :   return fd_memset( shmem, 0, SET_(footprint)() );
     230         801 : }
     231             : 
     232         801 : static inline SET_(t) * SET_(join)  ( void *    shset ) { return (SET_(t) *)shset; }
     233         786 : static inline void    * SET_(leave) ( SET_(t) * set   ) { return (void *)set; }
     234         786 : static inline void    * SET_(delete)( void *    shset ) { return shset; }
     235             : 
     236           3 : FD_FN_CONST static inline ulong SET_(max)( SET_(t) const * set ) { (void)set; return (ulong)SET_MAX; }
     237             : 
     238             : FD_FN_PURE static inline int
     239      333315 : SET_(valid)( SET_(t) const * set ) {
     240      333315 :   if( FD_UNLIKELY( !set | !fd_ulong_is_aligned( (ulong)set, SET_(align)() ) ) ) return 0;
     241      333315 :   return !(set[ SET_(word_cnt)-1 ] & ~SET_(private_full_last_word()));
     242      333315 : }
     243             : 
     244             : FD_FN_CONST static inline int
     245             : SET_(valid_idx)( SET_(t) const * set,
     246       74070 :                  ulong           idx ) {
     247       74070 :   (void)set;
     248       74070 :   return idx<(ulong)SET_MAX;
     249       74070 : }
     250             : 
     251             : FD_FN_PURE static inline ulong
     252      153778 : SET_(cnt)( SET_(t) const * set ) {
     253      153778 :   ulong word_cnt = (ulong)SET_(word_cnt);
     254      153778 :   ulong cnt = 0UL;
     255    29365838 :   for( ulong i=0UL; i<word_cnt; i++ ) cnt += (ulong)fd_ulong_popcnt( set[i] );
     256      153778 :   return cnt;
     257      153778 : }
     258             : 
     259             : FD_FN_PURE static inline int
     260      296286 : SET_(is_null)( SET_(t) const * set ) {
     261      296286 :   ulong word_cnt = (ulong)SET_(word_cnt);
     262    39589563 :   for( ulong i=0UL; i<word_cnt; i++ ) if( set[i] ) return 0;
     263      185181 :   return 1;
     264      296286 : }
     265             : 
     266             : FD_FN_PURE static inline int
     267      148146 : SET_(is_full)( SET_(t) const * set ) {
     268      148146 :   ulong word_cnt = ((ulong)SET_(word_cnt))-1UL;
     269    10813362 :   for( ulong i=0UL; i<word_cnt; i++ ) if( ~set[i] ) return 0;
     270       37212 :   return set[word_cnt]==SET_(private_full_last_word());
     271      148146 : }
     272             : 
     273             : FD_FN_PURE static inline ulong
     274      153206 : SET_(first)( SET_(t) const * set ) {
     275      153206 :   ulong word_cnt = (ulong)SET_(word_cnt);
     276    10854305 :   for( ulong i=0UL; i<word_cnt; i++ ) {
     277    10817270 :     ulong w = set[i];
     278    10817270 :     if( w ) return (i<<6) + (ulong)fd_ulong_find_lsb( w );
     279    10817270 :   }
     280       37035 :   return ~0UL;
     281      153206 : }
     282             : 
     283             : FD_FN_PURE static inline ulong
     284      148146 : SET_(last)( SET_(t) const * set ) {
     285      148146 :   ulong word_cnt = (ulong)SET_(word_cnt);
     286    10853283 :   for( ulong i=word_cnt; i>0UL; i-- ) {
     287    10816248 :     ulong w = set[i-1];
     288    10816248 :     if( w ) return ((i-1)<<6) + (ulong)fd_ulong_find_msb( w );
     289    10816248 :   }
     290       37035 :   return ~0UL;
     291      148146 : }
     292             : 
     293             : FD_FN_UNUSED static ulong /* Work around -Winline */
     294             : SET_(iter_next)( SET_(t) * set,
     295   914542644 :                  ulong     j ) {                     /* We've considered all bits up to and including j */
     296   914542644 :   j++;                                               /* Lowest bit we haven't considered */
     297   914542644 :   ulong word_cnt = (ulong)SET_(word_cnt);
     298   928917882 :   for( ulong i=(j>>6); i<word_cnt; i++ ) {           /* For all words with bits we haven't considered */
     299   928769736 :     ulong w = set[i];                                /* Get the bits we haven't considered for the current word */
     300   928769736 :     if( w ) {                                        /* If any bits are set in this word */
     301   914394498 :       set[i] = fd_ulong_pop_lsb( w );                /* Clear the lsb */
     302   914394498 :       return (i<<6) + (ulong)fd_ulong_find_lsb( w ); /* And return the index */
     303   914394498 :     }
     304   928769736 :   }
     305      148146 :   return ~0UL;                                       /* No more bits to consider */
     306   914542644 : }
     307      148257 : static inline ulong SET_(iter_init)( SET_(t) * set ) { return SET_(iter_next)( set, ~0UL ); }
     308   914542644 : FD_FN_CONST static inline ulong SET_(iter_done)( ulong j ) { return !~j; }
     309             : 
     310             : FD_FN_PURE FD_FN_UNUSED static ulong /* Work around -Winline */
     311             : SET_(const_iter_next)( SET_(t) const * set,
     312   914542290 :                        ulong           j ) {               /* We've considered all bits up to and including j */
     313   914542290 :   j++;                                                     /* Lowest bit we haven't considered */
     314   914542290 :   ulong m = (1UL<<(j&63UL))-1UL;                           /* Bits in first word that have been considered */
     315   914542290 :   ulong word_cnt = (ulong)SET_(word_cnt);
     316   928911870 :   for( ulong i=(j>>6); i<word_cnt; i++ ) {                 /* For all words with bits we haven't considered */
     317   928763730 :     ulong w = set[i] & ~m;                                 /* Get the bits we haven't considered for the current word */
     318   928763730 :     if( w ) return (i<<6) + (ulong)fd_ulong_find_lsb( w ); /* If any bit is set in this word, return its index */
     319    14369580 :     m = 0UL;                                               /* Otherwise, continue to next word (haven't considered any bits) */
     320    14369580 :   }
     321      148140 :   return ~0UL;                                             /* No more bits to consider */
     322   914542290 : }
     323      148140 : FD_FN_PURE  static inline ulong SET_(const_iter_init)( SET_(t) const * set ) { return SET_(const_iter_next)( set, ~0UL ); }
     324   914542290 : FD_FN_CONST static inline ulong SET_(const_iter_done)( ulong j             ) { return !~j; }
     325             : 
     326             : static inline SET_(t) *
     327             : SET_(insert)( SET_(t) * set,
     328    12602634 :               ulong     idx ) {
     329             : # if FD_TMPL_USE_HANDHOLDING
     330             :   if( FD_UNLIKELY( idx>=(ulong)(SET_MAX) ) ) FD_LOG_CRIT(( "idx out of bounds" ));
     331             : # endif
     332    12602634 :   set[ idx >> 6 ] |= 1UL << (idx & 63UL);
     333    12602634 :   return set;
     334    12602634 : }
     335             : 
     336             : static inline SET_(t) *
     337             : SET_(remove)( SET_(t) * set,
     338      547215 :               ulong     idx ) {
     339             : # if FD_TMPL_USE_HANDHOLDING
     340             :   if( FD_UNLIKELY( idx>=(ulong)(SET_MAX) ) ) FD_LOG_CRIT(( "idx out of bounds" ));
     341             : # endif
     342      547215 :   set[ idx >> 6 ] &= ~(1UL << (idx & 63UL));
     343      547215 :   return set;
     344      547215 : }
     345             : 
     346             : static inline SET_(t) *
     347             : SET_(insert_if)( SET_(t) * set,
     348             :                  int       c,
     349    37423224 :                  ulong     idx ) {
     350             : # if FD_TMPL_USE_HANDHOLDING
     351             :   if( FD_UNLIKELY( c && idx>=(ulong)(SET_MAX) ) ) FD_LOG_CRIT(( "idx out of bounds" ));
     352             : # endif
     353    37423224 :   set[ idx >> 6 ] |= ((ulong)!!c) << (idx & 63UL);
     354    37423224 :   return set;
     355    37423224 : }
     356             : 
     357             : static inline SET_(t) *
     358             : SET_(remove_if)( SET_(t) * set,
     359             :                  int       c,
     360      296280 :                  ulong     idx ) {
     361             : # if FD_TMPL_USE_HANDHOLDING
     362             :   if( FD_UNLIKELY( c && idx>=(ulong)(SET_MAX) ) ) FD_LOG_CRIT(( "idx out of bounds" ));
     363             : # endif
     364      296280 :   set[ idx >> 6 ] &= ~(((ulong)!!c) << (idx & 63UL));
     365      296280 :   return set;
     366      296280 : }
     367             : 
     368             : FD_FN_PURE static inline int
     369             : SET_(test)( SET_(t) const * set,
     370      393009 :             ulong           idx ) {
     371             : # if FD_TMPL_USE_HANDHOLDING
     372             :   if( FD_UNLIKELY( idx>=(ulong)(SET_MAX) ) ) FD_LOG_CRIT(( "idx out of bounds" ));
     373             : # endif
     374      393009 :   return (int)((set[ idx >> 6 ] >> (idx & 63UL)) & 1UL);
     375      393009 : }
     376             : 
     377             : FD_FN_PURE static inline int
     378             : SET_(eq)( SET_(t) const * x,
     379     5567271 :           SET_(t) const * y ) {
     380     5567271 :   ulong word_cnt = (ulong)SET_(word_cnt);
     381  1008488187 :   for( ulong i=0UL; i<word_cnt; i++ ) if( x[i]!=y[i] ) return 0;
     382     5122848 :   return 1;
     383     5567271 : }
     384             : 
     385             : FD_FN_PURE static inline int
     386             : SET_(subset)( SET_(t) const * x,
     387      592560 :               SET_(t) const * y ) {
     388      592560 :   ulong word_cnt = (ulong)SET_(word_cnt);
     389    75582387 :   for( ulong i=0UL; i<word_cnt; i++ ) if( x[i]!=(y[i] & x[i]) ) return 0;
     390      333315 :   return 1;
     391      592560 : }
     392             : 
     393             : static inline SET_(t) *
     394      117132 : SET_(null)( SET_(t) * z ) {
     395      117132 :   ulong word_cnt = (ulong)SET_(word_cnt);
     396    22718829 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = 0UL;
     397      117132 :   return z;
     398      117132 : }
     399             : 
     400             : static inline SET_(t) *
     401         204 : SET_(full)( SET_(t) * z ) {
     402         204 :   ulong word_cnt = ((ulong)SET_(word_cnt))-1UL;
     403      115266 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = ~0UL;
     404         204 :   z[word_cnt] = SET_(private_full_last_word)();
     405         204 :   return z;
     406         204 : }
     407             : 
     408             : static inline SET_(t) *
     409             : SET_(full_if)( SET_(t) * z,
     410           6 :                int       c ) {
     411           6 :   ulong word_cnt = ((ulong)SET_(word_cnt))-1UL;
     412           6 :   ulong word     = ((ulong)!c)-1UL;
     413        1158 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = word;
     414           6 :   z[word_cnt] = word & SET_(private_full_last_word)();
     415           6 :   return z;
     416           6 : }
     417             : 
     418             : static inline SET_(t) *
     419             : SET_(ele)( SET_(t) * z,
     420       37035 :            ulong     idx ) {
     421       37035 :   return SET_(insert)( SET_(null)( z ), idx );
     422       37035 : }
     423             : 
     424             : static inline SET_(t) *
     425             : SET_(ele_if)( SET_(t) * z,
     426             :               int       c,
     427       74070 :               ulong     idx ) {
     428       74070 :   return SET_(insert_if)( SET_(null)( z ), c, idx );
     429       74070 : }
     430             : 
     431             : static inline SET_(t) *
     432             : SET_(copy)( SET_(t) *       z,
     433     2527380 :             SET_(t) const * x ) {
     434     2527380 :   ulong word_cnt = (ulong)SET_(word_cnt);
     435   490311720 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i];
     436     2527380 :   return z;
     437     2527380 : }
     438             : 
     439             : static inline SET_(t) *
     440             : SET_(complement)( SET_(t) *       z,
     441      185175 :                   SET_(t) const * x ) {
     442      185175 :   ulong word_cnt = ((ulong)SET_(word_cnt))-1UL;
     443    35738775 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = ~x[i];
     444      185175 :   z[word_cnt] = (~x[word_cnt]) & SET_(private_full_last_word)();
     445      185175 :   return z;
     446      185175 : }
     447             : 
     448             : static inline SET_(t) *
     449             : SET_(union)( SET_(t) *       z,
     450             :              SET_(t) const * x,
     451      632598 :              SET_(t) const * y ) {
     452      632598 :   ulong word_cnt = (ulong)SET_(word_cnt);
     453   122723481 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i] | y[i];
     454      632598 :   return z;
     455      632598 : }
     456             : 
     457             : static inline SET_(t) *
     458             : SET_(intersect)( SET_(t) *       z,
     459             :                  SET_(t) const * x,
     460      595563 :                  SET_(t) const * y ) {
     461      595563 :   ulong word_cnt = (ulong)SET_(word_cnt);
     462   115538691 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i] & y[i];
     463      595563 :   return z;
     464      595563 : }
     465             : 
     466             : static inline SET_(t) *
     467             : SET_(subtract)( SET_(t) *       z,
     468             :                 SET_(t) const * x,
     469      632595 :                 SET_(t) const * y ) {
     470      632595 :   ulong word_cnt = (ulong)SET_(word_cnt);
     471   122723430 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i] & ~y[i];
     472      632595 :   return z;
     473      632595 : }
     474             : 
     475             : static inline SET_(t) *
     476             : SET_(xor)( SET_(t) *       z,
     477             :            SET_(t) const * x,
     478      592563 :            SET_(t) const * y ) {
     479      592563 :   ulong word_cnt = (ulong)SET_(word_cnt);
     480   114956691 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i] ^ y[i];
     481      592563 :   return z;
     482      592563 : }
     483             : 
     484             : static inline SET_(t) *
     485             : SET_(if)( SET_(t) *       z,
     486             :           int             c,
     487             :           SET_(t) const * x,
     488     1185120 :           SET_(t) const * y ) {
     489     1185120 :   return SET_(copy)( z, c ? x : y );
     490     1185120 : }
     491             : 
     492             : static inline SET_(t) *
     493             : SET_(range)( SET_(t) * set,
     494             :              ulong     l,
     495        3000 :              ulong     h ) {
     496             : # if FD_TMPL_USE_HANDHOLDING
     497             :   if( FD_UNLIKELY( !( (l<=h) & (h<=(ulong)(SET_MAX)) ) ) ) FD_LOG_CRIT(( "invalid range" ));
     498             : # endif
     499             : 
     500        3000 :   ulong word_idx = 0UL;
     501             : 
     502             :   /* Handle any complete leading zero words */
     503             : 
     504      190677 :   for( ulong stop_idx=l>>6; word_idx<stop_idx; word_idx++ ) set[ word_idx ] = 0UL; /* FIXME: Consider memset? */
     505             : 
     506             :   /* Handle any mixed leading word.  Note that it is possible the range
     507             :      also ends in this word. */
     508             : 
     509        3000 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     510        3000 :   if( FD_LIKELY( zcnt ) ) set[ word_idx++ ] = ((1UL << fd_ulong_min( 64UL-zcnt, h-l ))-1UL) << zcnt; /* opt large range */
     511             : 
     512             :   /* Handle any complete ones words.  Need to be careful as 64 word_idx
     513             :      might already be past h if the range ended in the mixed leading
     514             :      word. */
     515             : 
     516      193425 :   for( ulong stop_idx=h>>6; word_idx<stop_idx; word_idx++ ) set[ word_idx ] = ~0UL; /* FIXME: Consider memset? */
     517             : 
     518             :   /* Handle any mixed trailing word.  Like the above, 64 word_idx might
     519             :      already be past h at this point. */
     520             : 
     521        3000 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     522        3000 :   if( FD_LIKELY( ocnt ) ) set[ word_idx++ ] = ((1UL << ocnt)-1UL); /* opt large range */
     523             : 
     524             :   /* Handle any complete trailing zero words */
     525             : 
     526      198015 :   for( ulong stop_idx=(ulong)SET_(word_cnt); word_idx<stop_idx; word_idx++ ) set[ word_idx ] = 0UL; /* FIXME: Consider memset? */
     527             : 
     528        3000 :   return set;
     529        3000 : }
     530             : 
     531             : static inline SET_(t) *
     532             : SET_(insert_range)( SET_(t) * set,
     533             :                     ulong     l,
     534        3000 :                     ulong     h ) {
     535             : # if FD_TMPL_USE_HANDHOLDING
     536             :   if( FD_UNLIKELY( !( (l<=h) & (h<=(ulong)(SET_MAX)) ) ) ) FD_LOG_CRIT(( "invalid range" ));
     537             : # endif
     538             : 
     539             :   /* Handle any complete leading zero words */
     540             : 
     541        3000 :   ulong word_idx = l>>6;
     542             : 
     543             :   /* Handle any mixed leading word.  Note that it is possible the range
     544             :      also ends in this word. */
     545             : 
     546        3000 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     547        3000 :   if( FD_LIKELY( zcnt ) ) set[ word_idx++ ] |= ((1UL << fd_ulong_min( 64UL-zcnt, h-l ))-1UL) << zcnt; /* opt large range */
     548             : 
     549             :   /* Handle any complete ones words.  Need to be careful as 64 word_idx
     550             :      might already be past h if the range ended in the mixed leading
     551             :      word. */
     552             : 
     553      193425 :   for( ulong stop_idx=h>>6; word_idx<stop_idx; word_idx++ ) set[ word_idx ] = ~0UL; /* FIXME: Consider memset? */
     554             : 
     555             :   /* Handle any mixed trailing word.  Like the above, 64 word_idx might
     556             :      already be past h at this point. */
     557             : 
     558        3000 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     559        3000 :   if( FD_LIKELY( ocnt ) ) set[ word_idx++ ] |= ((1UL << ocnt)-1UL); /* opt large range */
     560             : 
     561             :   /* Handle any complete trailing zero words */
     562             : 
     563        3000 :   return set;
     564        3000 : }
     565             : 
     566             : static inline SET_(t) *
     567             : SET_(select_range)( SET_(t) * set,
     568             :                     ulong     l,
     569        3000 :                     ulong     h ) {
     570             : # if FD_TMPL_USE_HANDHOLDING
     571             :   if( FD_UNLIKELY( !( (l<=h) & (h<=(ulong)(SET_MAX)) ) ) ) FD_LOG_CRIT(( "invalid range" ));
     572             : # endif
     573             : 
     574        3000 :   ulong word_idx = 0UL;
     575             : 
     576             :   /* Handle any complete leading zero words */
     577             : 
     578      190677 :   for( ulong stop_idx=l>>6; word_idx<stop_idx; word_idx++ ) set[ word_idx ] = 0UL; /* FIXME: Consider memset? */
     579             : 
     580             :   /* Handle any mixed leading word.  Note that it is possible the range
     581             :      also ends in this word. */
     582             : 
     583        3000 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     584        3000 :   if( FD_LIKELY( zcnt ) ) set[ word_idx++ ] &= ((1UL << fd_ulong_min( 64UL-zcnt, h-l ))-1UL) << zcnt; /* opt large range */
     585             : 
     586             :   /* Handle any complete ones words.  Need to be careful as 64 word_idx
     587             :      might already be past h if the range ended in the mixed leading
     588             :      word. */
     589             : 
     590        3000 :   word_idx = fd_ulong_max( word_idx, h>>6 );
     591             : 
     592             :   /* Handle any mixed trailing word.  Like the above, 64 word_idx might
     593             :      already be past h at this point. */
     594             : 
     595        3000 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     596        3000 :   if( FD_LIKELY( ocnt ) ) set[ word_idx++ ] &= ((1UL << ocnt)-1UL); /* opt large range */
     597             : 
     598             :   /* Handle any complete trailing zero words */
     599             : 
     600      198015 :   for( ulong stop_idx=(ulong)SET_(word_cnt); word_idx<stop_idx; word_idx++ ) set[ word_idx ] = 0UL; /* FIXME: Consider memset? */
     601             : 
     602        3000 :   return set;
     603        3000 : }
     604             : 
     605             : static inline SET_(t) *
     606             : SET_(remove_range)( SET_(t) * set,
     607             :                     ulong     l,
     608        3000 :                     ulong     h ) {
     609             : # if FD_TMPL_USE_HANDHOLDING
     610             :   if( FD_UNLIKELY( !( (l<=h) & (h<=(ulong)(SET_MAX)) ) ) ) FD_LOG_CRIT(( "invalid range" ));
     611             : # endif
     612             : 
     613             :   /* Handle any complete leading zero words */
     614             : 
     615        3000 :   ulong word_idx = l>>6;
     616             : 
     617             :   /* Handle any mixed leading word.  Note that it is possible the range
     618             :      also ends in this word. */
     619             : 
     620        3000 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     621        3000 :   if( FD_LIKELY( zcnt ) ) set[ word_idx++ ] &= ~(((1UL << fd_ulong_min( 64UL-zcnt, h-l ))-1UL) << zcnt); /* opt large range */
     622             : 
     623             :   /* Handle any complete ones words.  Need to be careful as 64 word_idx
     624             :      might already be past h if the range ended in the mixed leading
     625             :      word. */
     626             : 
     627      193425 :   for( ulong stop_idx=h>>6; word_idx<stop_idx; word_idx++ ) set[ word_idx ] = 0UL; /* FIXME: Consider memset? */
     628             : 
     629             :   /* Handle any mixed trailing word.  Like the above, 64 word_idx might
     630             :      already be past h at this point. */
     631             : 
     632        3000 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     633        3000 :   if( FD_LIKELY( ocnt ) ) set[ word_idx++ ] &= ~((1UL << ocnt)-1UL); /* opt large range */
     634             : 
     635             :   /* Handle any complete trailing zero words */
     636             : 
     637        3000 :   return set;
     638        3000 : }
     639             : 
     640             : FD_FN_PURE static inline ulong
     641             : SET_(range_cnt)( SET_(t) const * set,
     642             :                  ulong           l,
     643        3000 :                  ulong           h ) {
     644             : # if FD_TMPL_USE_HANDHOLDING
     645             :   if( FD_UNLIKELY( !( (l<=h) & (h<=(ulong)(SET_MAX)) ) ) ) FD_LOG_CRIT(( "invalid range" ));
     646             : # endif
     647             : 
     648        3000 :   ulong cnt = 0UL;
     649             : 
     650             :   /* Handle any complete leading zero words */
     651             : 
     652        3000 :   ulong word_idx = l>>6;
     653             : 
     654             :   /* Handle any mixed leading word.  Note that it is possible the range
     655             :      also ends in this word. */
     656             : 
     657        3000 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     658        3000 :   if( FD_LIKELY( zcnt ) ) cnt += (ulong)fd_ulong_popcnt( set[ word_idx++ ] & (((1UL << fd_ulong_min( 64UL-zcnt, h-l ))-1UL) << zcnt) ); /* opt large range */
     659             : 
     660             :   /* Handle any complete ones words.  Need to be careful as 64 word_idx
     661             :      might already be past h if the range ended in the mixed leading
     662             :      word. */
     663             : 
     664      193425 :   for( ulong stop_idx=h>>6; word_idx<stop_idx; word_idx++ ) cnt += (ulong)fd_ulong_popcnt( set[ word_idx ] );
     665             : 
     666             :   /* Handle any mixed trailing word.  Like the above, 64 word_idx might
     667             :      already be past h at this point. */
     668             : 
     669        3000 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     670        3000 :   if( FD_LIKELY( ocnt ) ) cnt += (ulong)fd_ulong_popcnt( set[ word_idx++ ] & ((1UL << ocnt)-1UL) ); /* opt large range */
     671             : 
     672             :   /* Handle any complete trailing zero words */
     673             : 
     674        3000 :   return cnt;
     675        3000 : }
     676             : 
     677             : FD_PROTOTYPES_END
     678             : 
     679             : #undef SET_
     680             : 
     681             : #undef SET_MAX
     682             : #undef SET_NAME

Generated by: LCOV version 1.14