LCOV - code coverage report
Current view: top level - util/tmpl - fd_set_dynamic.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 55 235 23.4 %
Date: 2026-03-19 18:19:27 Functions: 24 8930 0.3 %

          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     3687390 : #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       54052 : 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       11069 : SET_(private_full_last_word)( ulong max ) {
     219       11069 :   return fd_ulong_mask_lsb( (int)(max - ((SET_(private_word_cnt)( max )-1UL)<<6)) );
     220       11069 : }
     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           0 : SET_(private_hdr_from_set)( SET_(t) * set ) {
     228           0 :   return (SET_(private_t) *)( (ulong)set - offsetof(SET_(private_t), set) );
     229           0 : }
     230             : 
     231             : FD_FN_CONST static inline SET_(private_t) const *
     232     3583483 : SET_(private_hdr_from_set_const)( SET_(t) const * set ) {
     233     3583483 :   return (SET_(private_t) const *)( (ulong)set - offsetof(SET_(private_t), set) );
     234     3583483 : }
     235             : 
     236             : /* Public APIs ********************************************************/
     237             : 
     238      124289 : FD_FN_CONST static inline ulong SET_(align)( void ) { return alignof(SET_(private_t)); }
     239             : 
     240             : FD_FN_CONST static inline ulong
     241       32080 : SET_(footprint)( ulong max ) {
     242       32080 :   return sizeof(SET_(private_t))-sizeof(SET_(t)) + sizeof(SET_(t))*SET_(private_word_cnt)( max );
     243       32080 : }
     244             : 
     245             : FD_FN_UNUSED static void * /* Work around -Winline */
     246             : SET_(new)( void * shmem,
     247       11069 :            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       11069 :   SET_(private_t) * hdr = (SET_(private_t) *)shmem;
     253             : 
     254       11069 :   ulong word_cnt = SET_(private_word_cnt)( max );
     255             : 
     256       11069 :   hdr->max            = max;
     257       11069 :   hdr->word_cnt       = word_cnt;
     258       11069 :   hdr->full_last_word = SET_(private_full_last_word)( max );
     259             : 
     260       11069 :   SET_(t) * set = hdr->set; FD_COMPILER_FORGET( set );
     261       11069 :   fd_memset( set, 0, sizeof(SET_(t))*word_cnt );
     262             : 
     263       11069 :   return hdr;
     264       11069 : }
     265             : 
     266             : static inline SET_(t) *
     267       17425 : SET_(join)( void * shset ) {
     268       17425 :   SET_(private_t) * hdr = (SET_(private_t) *)shset;
     269       17425 :   return hdr->set;
     270       17425 : }
     271             : 
     272           0 : static inline void * SET_(leave) ( SET_(t) * set   ) { return (void *)SET_(private_hdr_from_set)( set ); }
     273           0 : static inline void * SET_(delete)( void *    shset ) { return shset; }
     274             : 
     275           0 : 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           0 : SET_(valid)( SET_(t) const * set ) {
     279           0 :   if( FD_UNLIKELY( !set ) ) return 0;
     280           0 :   SET_(private_t) const * hdr = SET_(private_hdr_from_set_const)( set );
     281           0 :   if( FD_UNLIKELY( !hdr ) ) return 0;
     282           0 :   return !(set[ hdr->word_cnt-1UL ] & ~hdr->full_last_word);
     283           0 : }
     284             : 
     285             : FD_FN_PURE static inline int
     286             : SET_(valid_idx)( SET_(t) const * set,
     287     3450947 :                  ulong           idx ) {
     288     3450947 :   return idx < SET_(private_hdr_from_set_const)( set )->max;
     289     3450947 : }
     290             : 
     291             : FD_FN_PURE static inline ulong
     292         741 : SET_(cnt)( SET_(t) const * set ) {
     293         741 :   ulong word_cnt = SET_(private_hdr_from_set_const)( set )->word_cnt;
     294         741 :   ulong cnt = 0UL;
     295      163507 :   for( ulong i=0UL; i<word_cnt; i++ ) cnt += (ulong)fd_ulong_popcnt( set[i] );
     296         741 :   return cnt;
     297         741 : }
     298             : 
     299             : FD_FN_PURE static inline int
     300           0 : SET_(is_null)( SET_(t) const * set ) {
     301           0 :   ulong word_cnt = SET_(private_hdr_from_set_const)( set )->word_cnt;
     302           0 :   for( ulong i=0UL; i<word_cnt; i++ ) if( set[i] ) return 0;
     303           0 :   return 1;
     304           0 : }
     305             : 
     306             : FD_FN_PURE static inline int
     307           0 : SET_(is_full)( SET_(t) const * set ) {
     308           0 :   SET_(private_t) const * hdr = SET_(private_hdr_from_set_const)( set );
     309           0 :   ulong last_word = hdr->word_cnt - 1UL;
     310           0 :   for( ulong i=0UL; i<last_word; i++ ) if( ~set[i] ) return 0;
     311           0 :   return set[last_word]==hdr->full_last_word;
     312           0 : }
     313             : 
     314             : FD_FN_PURE static inline ulong
     315           0 : SET_(first)( SET_(t) const * set ) {
     316           0 :   ulong word_cnt = SET_(private_hdr_from_set_const)( set )->word_cnt;
     317           0 :   for( ulong i=0UL; i<word_cnt; i++ ) {
     318           0 :     ulong w = set[i];
     319           0 :     if( w ) return (i<<6) + (ulong)fd_ulong_find_lsb( w );
     320           0 :   }
     321           0 :   return ~0UL;
     322           0 : }
     323             : 
     324             : __attribute__((warn_unused_result)) FD_FN_UNUSED static ulong /* Work around -Winline */
     325             : SET_(iter_next)( SET_(t) * set,
     326           0 :                  ulong     j ) {                     /* We've considered all bits up to and including j */
     327           0 :   j++;                                               /* Lowest bit we haven't considered */
     328           0 :   ulong word_cnt = SET_(private_hdr_from_set)( set )->word_cnt;
     329           0 :   for( ulong i=(j>>6); i<word_cnt; i++ ) {           /* For all words with bits we haven't considered */
     330           0 :     ulong w = set[i];                                /* Get the bits we haven't considered for the current word */
     331           0 :     if( w ) {                                        /* If any bits are set in this word */
     332           0 :       set[i] = fd_ulong_pop_lsb( w );                /* Clear the lsb */
     333           0 :       return (i<<6) + (ulong)fd_ulong_find_lsb( w ); /* And return the index */
     334           0 :     }
     335           0 :   }
     336           0 :   return ~0UL;                                       /* No more bits to consider */
     337           0 : }
     338           0 : static inline ulong SET_(iter_init)( SET_(t) * set ) { return SET_(iter_next)( set, ~0UL ); }
     339           0 : FD_FN_CONST static inline ulong SET_(iter_done)( ulong j ) { return !~j; }
     340             : 
     341             : __attribute__((warn_unused_result)) FD_FN_PURE FD_FN_UNUSED static ulong /* Work around -Winline */
     342             : SET_(const_iter_next)( SET_(t) const * set,
     343      130111 :                        ulong           j ) {               /* We've considered all bits up to and including j */
     344      130111 :   j++;                                                     /* Lowest bit we haven't considered */
     345      130111 :   ulong m = (1UL<<(j&63UL))-1UL;                           /* Bits in first word that have considered */
     346      130111 :   ulong word_cnt = SET_(private_hdr_from_set_const)( set )->word_cnt;
     347      290799 :   for( ulong i=(j>>6); i<word_cnt; i++ ) {                 /* For all words with bits we haven't considered */
     348      290059 :     ulong w = set[i] & ~m;                                 /* Get the bits we haven't considered for the current word */
     349      290059 :     if( w ) return (i<<6) + (ulong)fd_ulong_find_lsb( w ); /* If any bit is set in this word, return its index */
     350      160688 :     m = 0UL;                                               /* Otherwise, continue to next word (haven't considered any bits) */
     351      160688 :   }
     352         740 :   return ~0UL;                                             /* No more bits to consider */
     353      130111 : }
     354         741 : FD_FN_PURE  static inline ulong SET_(const_iter_init)( SET_(t) const * set ) { return SET_(const_iter_next)( set, ~0UL ); }
     355      130115 : 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     3451916 :               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     3451916 :   set[ idx >> 6 ] |= 1UL << (idx & 63UL);
     364     3451916 :   return set;
     365     3451916 : }
     366             : 
     367             : static inline SET_(t) *
     368             : SET_(remove)( SET_(t) * set,
     369           0 :               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           0 :   set[ idx >> 6 ] &= ~(1UL << (idx & 63UL));
     374           0 :   return set;
     375           0 : }
     376             : 
     377             : static inline SET_(t) *
     378             : SET_(insert_if)( SET_(t) * set,
     379             :                  int       c,
     380           0 :                  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           0 :   set[ idx >> 6 ] |= ((ulong)!!c) << (idx & 63UL);
     385           0 :   return set;
     386           0 : }
     387             : 
     388             : static inline SET_(t) *
     389             : SET_(remove_if)( SET_(t) * set,
     390             :                  int       c,
     391           0 :                  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           0 :   set[ idx >> 6 ] &= ~(((ulong)!!c) << (idx & 63UL));
     396           0 :   return set;
     397           0 : }
     398             : 
     399             : FD_FN_PURE static inline int
     400             : SET_(test)( SET_(t) const * set,
     401       90802 :             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       90802 :   return (int)((set[ idx >> 6 ] >> (idx & 63UL)) & 1UL);
     406       90802 : }
     407             : 
     408             : FD_FN_PURE static inline int
     409             : SET_(eq)( SET_(t) const * x,
     410           0 :           SET_(t) const * y ) {
     411           0 :   ulong word_cnt = SET_(private_hdr_from_set_const)( x )->word_cnt;
     412           0 :   for( ulong i=0UL; i<word_cnt; i++ ) if( x[i]!=y[i] ) return 0;
     413           0 :   return 1;
     414           0 : }
     415             : 
     416             : FD_FN_PURE static inline int
     417             : SET_(subset)( SET_(t) const * x,
     418           0 :               SET_(t) const * y ) {
     419           0 :   ulong word_cnt = SET_(private_hdr_from_set_const)( x )->word_cnt;
     420           0 :   for( ulong i=0UL; i<word_cnt; i++ ) if( x[i]!=(y[i] & x[i]) ) return 0;
     421           0 :   return 1;
     422           0 : }
     423             : 
     424             : static inline SET_(t) *
     425           0 : SET_(null)( SET_(t) * z ) {
     426           0 :   ulong word_cnt = SET_(private_hdr_from_set_const)( z )->word_cnt;
     427           0 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = 0UL;
     428           0 :   return z;
     429           0 : }
     430             : 
     431             : static inline SET_(t) *
     432           0 : SET_(full)( SET_(t) * z ) {
     433           0 :   SET_(private_t) * hdr = SET_(private_hdr_from_set)( z );
     434           0 :   ulong last_word = hdr->word_cnt - 1UL;
     435           0 :   for( ulong i=0UL; i<last_word; i++ ) z[i] = ~0UL;
     436           0 :   z[last_word] = hdr->full_last_word;
     437           0 :   return z;
     438           0 : }
     439             : 
     440             : static inline SET_(t) *
     441             : SET_(full_if)( SET_(t) * z,
     442           0 :                int       c ) {
     443           0 :   SET_(private_t) * hdr = SET_(private_hdr_from_set)( z );
     444           0 :   ulong last_word = hdr->word_cnt - 1UL;
     445           0 :   ulong word      = ((ulong)!c)-1UL;
     446           0 :   for( ulong i=0UL; i<last_word; i++ ) z[i] = word;
     447           0 :   z[last_word] = word & hdr->full_last_word;
     448           0 :   return z;
     449           0 : }
     450             : 
     451             : static inline SET_(t) *
     452             : SET_(ele)( SET_(t) * z,
     453           0 :            ulong     idx ) {
     454           0 :   return SET_(insert)( SET_(null)( z ), idx );
     455           0 : }
     456             : 
     457             : static inline SET_(t) *
     458             : SET_(ele_if)( SET_(t) * z,
     459             :               int       c,
     460           0 :               ulong     idx ) {
     461           0 :   return SET_(insert_if)( SET_(null)( z ), c, idx );
     462           0 : }
     463             : 
     464             : static inline SET_(t) *
     465             : SET_(copy)( SET_(t) *       z,
     466           0 :             SET_(t) const * x ) {
     467           0 :   ulong word_cnt = SET_(private_hdr_from_set)( z )->word_cnt;
     468           0 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i];
     469           0 :   return z;
     470           0 : }
     471             : 
     472             : FD_FN_UNUSED static SET_(t) * /* Work around -Winline */
     473             : SET_(complement)( SET_(t) *       z,
     474           0 :                   SET_(t) const * x ) {
     475           0 :   SET_(private_t) * hdr = SET_(private_hdr_from_set)( z );
     476           0 :   ulong last_word = hdr->word_cnt - 1UL;
     477           0 :   for( ulong i=0UL; i<last_word; i++ ) z[i] = ~x[i];
     478           0 :   z[last_word] = (~x[last_word]) & hdr->full_last_word;
     479           0 :   return z;
     480           0 : }
     481             : 
     482             : static inline SET_(t) *
     483             : SET_(union)( SET_(t) *       z,
     484             :              SET_(t) const * x,
     485           0 :              SET_(t) const * y ) {
     486           0 :   ulong word_cnt = SET_(private_hdr_from_set)( z )->word_cnt;
     487           0 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i] | y[i];
     488           0 :   return z;
     489           0 : }
     490             : 
     491             : static inline SET_(t) *
     492             : SET_(intersect)( SET_(t) *       z,
     493             :                  SET_(t) const * x,
     494           0 :                  SET_(t) const * y ) {
     495           0 :   ulong word_cnt = SET_(private_hdr_from_set)( z )->word_cnt;
     496           0 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i] & y[i];
     497           0 :   return z;
     498           0 : }
     499             : 
     500             : static inline SET_(t) *
     501             : SET_(subtract)( SET_(t) *       z,
     502             :                 SET_(t) const * x,
     503           0 :                 SET_(t) const * y ) {
     504           0 :   ulong word_cnt = SET_(private_hdr_from_set)( z )->word_cnt;
     505           0 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i] & ~y[i];
     506           0 :   return z;
     507           0 : }
     508             : 
     509             : static inline SET_(t) *
     510             : SET_(xor)( SET_(t) *       z,
     511             :            SET_(t) const * x,
     512           0 :            SET_(t) const * y ) {
     513           0 :   ulong word_cnt = SET_(private_hdr_from_set)( z )->word_cnt;
     514           0 :   for( ulong i=0UL; i<word_cnt; i++ ) z[i] = x[i] ^ y[i];
     515           0 :   return z;
     516           0 : }
     517             : 
     518             : static inline SET_(t) *
     519             : SET_(if)( SET_(t) *       z,
     520             :           int             c,
     521             :           SET_(t) const * x,
     522           0 :           SET_(t) const * y ) {
     523           0 :   return SET_(copy)( z, c ? x : y );
     524           0 : }
     525             : 
     526             : static inline SET_(t) *
     527             : SET_(range)( SET_(t) * set,
     528             :              ulong     l,
     529           0 :              ulong     h ) {
     530           0 :   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           0 :   ulong word_idx = 0UL;
     537             : 
     538             :   /* Handle any complete leading zero words */
     539             : 
     540           0 :   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           0 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     546           0 :   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           0 :   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           0 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     558           0 :   if( FD_LIKELY( ocnt ) ) set[ word_idx++ ] = ((1UL << ocnt)-1UL); /* opt large range */
     559             : 
     560             :   /* Handle any complete trailing zero words */
     561             : 
     562           0 :   for( ulong stop_idx=hdr->word_cnt; word_idx<stop_idx; word_idx++ ) set[ word_idx ] = 0UL; /* FIXME: Consider memset? */
     563             : 
     564           0 :   return set;
     565           0 : }
     566             : 
     567             : static inline SET_(t) *
     568             : SET_(insert_range)( SET_(t) * set,
     569             :                     ulong     l,
     570           0 :                     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           0 :   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           0 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     585           0 :   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           0 :   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           0 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     597           0 :   if( FD_LIKELY( ocnt ) ) set[ word_idx++ ] |= ((1UL << ocnt)-1UL); /* opt large range */
     598             : 
     599             :   /* Handle any complete trailing zero words */
     600             : 
     601           0 :   return set;
     602           0 : }
     603             : 
     604             : static inline SET_(t) *
     605             : SET_(select_range)( SET_(t) * set,
     606             :                     ulong     l,
     607           0 :                     ulong     h ) {
     608           0 :   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           0 :   ulong word_idx = 0UL;
     615             : 
     616             :   /* Handle any complete leading zero words */
     617             : 
     618           0 :   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           0 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     624           0 :   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           0 :   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           0 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     636           0 :   if( FD_LIKELY( ocnt ) ) set[ word_idx++ ] &= ((1UL << ocnt)-1UL); /* opt large range */
     637             : 
     638             :   /* Handle any complete trailing zero words */
     639             : 
     640           0 :   for( ulong stop_idx=hdr->word_cnt; word_idx<stop_idx; word_idx++ ) set[ word_idx ] = 0UL; /* FIXME: Consider memset? */
     641             : 
     642           0 :   return set;
     643           0 : }
     644             : 
     645             : static inline SET_(t) *
     646             : SET_(remove_range)( SET_(t) * set,
     647             :                     ulong     l,
     648           0 :                     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           0 :   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           0 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     663           0 :   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           0 :   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           0 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     675           0 :   if( FD_LIKELY( ocnt ) ) set[ word_idx++ ] &= ~((1UL << ocnt)-1UL); /* opt large range */
     676             : 
     677             :   /* Handle any complete trailing zero words */
     678             : 
     679           0 :   return set;
     680           0 : }
     681             : 
     682             : FD_FN_PURE static inline ulong
     683             : SET_(range_cnt)( SET_(t) const * set,
     684             :                  ulong           l,
     685           0 :                  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           0 :   ulong cnt = 0UL;
     693             : 
     694             :   /* Handle any complete leading zero words */
     695             : 
     696           0 :   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           0 :   ulong zcnt = l & 63UL; // == l - (word_idx<<6); /* In [0,63] */
     702           0 :   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           0 :   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           0 :   ulong ocnt = h - fd_ulong_min( word_idx<<6, h ); /* in [0,63] */
     714           0 :   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           0 :   return cnt;
     719           0 : }
     720             : 
     721             : FD_PROTOTYPES_END
     722             : 
     723             : #undef SET_
     724             : 
     725             : #undef SET_NAME

Generated by: LCOV version 1.14