LCOV - code coverage report
Current view: top level - util/tmpl - fd_set_dynamic.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 235 235 100.0 %
Date: 2025-08-05 05:04:49 Functions: 71 4324 1.6 %

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

Generated by: LCOV version 1.14