LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_gossip_hset.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 145 163 89.0 %
Date: 2026-11-12 04:25:03 Functions: 13 13 100.0 %

          Line data    Source code
       1             : #include "fd_gossip_hset.h"
       2             : #include "../../util/log/fd_log.h"
       3             : 
       4             : #include <string.h>
       5             : 
       6     5564259 : #define HSET_NULL (UINT_MAX)
       7             : 
       8             : static ulong
       9         480 : hset_lg_bucket_cnt( ulong ele_max ) {
      10             :   /* ~128 elements per bucket when full, so a pull request mask range
      11             :      (mask_bits<=12 on mainnet sized tables) covers whole buckets and
      12             :      the partially filled head chunks waste few lanes. */
      13         480 :   return fd_ulong_min( fd_ulong_max( (ulong)fd_ulong_find_msb( ele_max ), 8UL )-7UL, 16UL );
      14         480 : }
      15             : 
      16             : static ulong
      17         240 : hset_chunk_max( ulong ele_max ) {
      18             :   /* Every bucket has at most one non-full chunk. */
      19         240 :   return ele_max/8UL + (1UL<<hset_lg_bucket_cnt( ele_max ));
      20         240 : }
      21             : 
      22             : FD_FN_CONST ulong
      23         255 : fd_gossip_hset_align( void ) {
      24         255 :   return FD_GOSSIP_HSET_ALIGN;
      25         255 : }
      26             : 
      27             : FD_FN_CONST ulong
      28         195 : fd_gossip_hset_footprint( ulong ele_max ) {
      29         195 :   if( FD_UNLIKELY( !ele_max || !fd_ulong_is_pow2( ele_max ) || ele_max>(1UL<<31) ) ) return 0UL;
      30         195 :   ulong chunk_max = hset_chunk_max( ele_max );
      31         195 :   ulong l;
      32         195 :   l = FD_LAYOUT_INIT;
      33         195 :   l = FD_LAYOUT_APPEND( l, FD_GOSSIP_HSET_ALIGN, sizeof(fd_gossip_hset_t)                         );
      34         195 :   l = FD_LAYOUT_APPEND( l, alignof(uint),        sizeof(uint)*(1UL<<hset_lg_bucket_cnt( ele_max )) );
      35         195 :   l = FD_LAYOUT_APPEND( l, 64UL,                 sizeof(fd_gossip_hset_chunk_t)*chunk_max          );
      36         195 :   l = FD_LAYOUT_APPEND( l, 64UL,                 256UL*chunk_max                                    );
      37         195 :   l = FD_LAYOUT_APPEND( l, alignof(uint),        sizeof(uint)*8UL*chunk_max                         );
      38         195 :   l = FD_LAYOUT_APPEND( l, alignof(uint),        sizeof(uint)*ele_max                               );
      39         195 :   return FD_LAYOUT_FINI( l, FD_GOSSIP_HSET_ALIGN );
      40         195 : }
      41             : 
      42             : void *
      43             : fd_gossip_hset_new( void * shmem,
      44          45 :                     ulong  ele_max ) {
      45          45 :   if( FD_UNLIKELY( !shmem ) ) {
      46           0 :     FD_LOG_WARNING(( "NULL shmem" ));
      47           0 :     return NULL;
      48           0 :   }
      49             : 
      50          45 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_gossip_hset_align() ) ) ) {
      51           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
      52           0 :     return NULL;
      53           0 :   }
      54             : 
      55          45 :   if( FD_UNLIKELY( !fd_gossip_hset_footprint( ele_max ) ) ) {
      56           0 :     FD_LOG_WARNING(( "bad ele_max" ));
      57           0 :     return NULL;
      58           0 :   }
      59             : 
      60          45 :   ulong lg_bucket_cnt = hset_lg_bucket_cnt( ele_max );
      61          45 :   ulong chunk_max     = hset_chunk_max( ele_max );
      62             : 
      63          45 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
      64          45 :   fd_gossip_hset_t * hset = FD_SCRATCH_ALLOC_APPEND( l, FD_GOSSIP_HSET_ALIGN, sizeof(fd_gossip_hset_t)                  );
      65          45 :   void * _bucket_head     = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),        sizeof(uint)*(1UL<<lg_bucket_cnt)         );
      66          45 :   void * _chunk           = FD_SCRATCH_ALLOC_APPEND( l, 64UL,                 sizeof(fd_gossip_hset_chunk_t)*chunk_max  );
      67          45 :   void * _hash            = FD_SCRATCH_ALLOC_APPEND( l, 64UL,                 256UL*chunk_max                           );
      68          45 :   void * _owner           = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),        sizeof(uint)*8UL*chunk_max                );
      69          45 :   void * _slot            = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),        sizeof(uint)*ele_max                      );
      70          45 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, FD_GOSSIP_HSET_ALIGN )==(ulong)shmem+fd_gossip_hset_footprint( ele_max ) );
      71             : 
      72          45 :   hset->ele_max       = ele_max;
      73          45 :   hset->chunk_max     = chunk_max;
      74          45 :   hset->lg_bucket_cnt = lg_bucket_cnt;
      75          45 :   hset->bucket_head   = (uint *)_bucket_head;
      76          45 :   hset->chunk         = (fd_gossip_hset_chunk_t *)_chunk;
      77          45 :   hset->hash          = (uchar (*)[ 32 ])_hash;
      78          45 :   hset->owner         = (uint *)_owner;
      79          45 :   hset->slot          = (uint *)_slot;
      80             : 
      81        1287 :   for( ulong b=0UL; b<(1UL<<lg_bucket_cnt); b++ ) hset->bucket_head[ b ] = HSET_NULL;
      82       20445 :   for( ulong c=0UL; c<chunk_max; c++ ) {
      83       20400 :     hset->chunk[ c ].next = c+1UL<chunk_max ? (uint)(c+1UL) : HSET_NULL;
      84       20400 :     hset->chunk[ c ].cnt  = 0U;
      85       20400 :   }
      86          45 :   hset->chunk_free = 0U;
      87             :   /* Lanes past a chunk's cnt are read (and ignored) by bloom inserts */
      88          45 :   memset( _hash, 0, 256UL*chunk_max );
      89      153312 :   for( ulong i=0UL; i<ele_max; i++ ) hset->slot[ i ] = HSET_NULL;
      90             : 
      91          45 :   FD_COMPILER_MFENCE();
      92          45 :   FD_VOLATILE( hset->magic ) = FD_GOSSIP_HSET_MAGIC;
      93          45 :   FD_COMPILER_MFENCE();
      94             : 
      95          45 :   return (void *)hset;
      96          45 : }
      97             : 
      98             : fd_gossip_hset_t *
      99          45 : fd_gossip_hset_join( void * shhset ) {
     100          45 :   if( FD_UNLIKELY( !shhset ) ) {
     101           0 :     FD_LOG_WARNING(( "NULL shhset" ));
     102           0 :     return NULL;
     103           0 :   }
     104             : 
     105          45 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shhset, fd_gossip_hset_align() ) ) ) {
     106           0 :     FD_LOG_WARNING(( "misaligned shhset" ));
     107           0 :     return NULL;
     108           0 :   }
     109             : 
     110          45 :   fd_gossip_hset_t * hset = (fd_gossip_hset_t *)shhset;
     111             : 
     112          45 :   if( FD_UNLIKELY( hset->magic!=FD_GOSSIP_HSET_MAGIC ) ) {
     113           0 :     FD_LOG_WARNING(( "bad magic" ));
     114           0 :     return NULL;
     115           0 :   }
     116             : 
     117          45 :   return hset;
     118          45 : }
     119             : 
     120             : static inline ulong
     121             : hset_bucket( fd_gossip_hset_t const * hset,
     122     7337790 :              uchar const *            hash ) {
     123     7337790 :   return fd_ulong_load_8( hash )>>(64UL-hset->lg_bucket_cnt);
     124     7337790 : }
     125             : 
     126             : void
     127             : fd_gossip_hset_insert( fd_gossip_hset_t * hset,
     128             :                        ulong              ele_idx,
     129     3717144 :                        uchar const *      hash ) {
     130     3717144 :   ulong b    = hset_bucket( hset, hash );
     131     3717144 :   ulong head = hset->bucket_head[ b ];
     132     3717144 :   if( FD_UNLIKELY( head==HSET_NULL || hset->chunk[ head ].cnt==8U ) ) {
     133      466767 :     ulong c = hset->chunk_free;
     134      466767 :     FD_TEST( c!=HSET_NULL ); /* impossible, see hset_chunk_max */
     135      466767 :     hset->chunk_free       = hset->chunk[ c ].next;
     136      466767 :     hset->chunk[ c ].next  = (uint)head;
     137      466767 :     hset->chunk[ c ].cnt   = 0U;
     138      466767 :     hset->bucket_head[ b ] = (uint)c;
     139      466767 :     head = c;
     140      466767 :   }
     141     3717144 :   ulong s = 8UL*head + hset->chunk[ head ].cnt++;
     142     3717144 :   memcpy( hset->hash[ s ], hash, 32UL );
     143     3717144 :   hset->owner[ s ]       = (uint)ele_idx;
     144     3717144 :   hset->slot[ ele_idx ]  = (uint)s;
     145     3717144 : }
     146             : 
     147             : void
     148             : fd_gossip_hset_remove( fd_gossip_hset_t * hset,
     149     3620646 :                        ulong              ele_idx ) {
     150     3620646 :   ulong s    = hset->slot[ ele_idx ];
     151     3620646 :   ulong b    = hset_bucket( hset, hset->hash[ s ] );
     152     3620646 :   ulong head = hset->bucket_head[ b ];
     153     3620646 :   ulong last = 8UL*head + hset->chunk[ head ].cnt - 1UL;
     154     3620646 :   if( FD_LIKELY( s!=last ) ) {
     155             :     /* Fill the hole with the head chunk's last hash */
     156     3563577 :     memcpy( hset->hash[ s ], hset->hash[ last ], 32UL );
     157     3563577 :     ulong moved = hset->owner[ last ];
     158     3563577 :     hset->owner[ s ]     = (uint)moved;
     159     3563577 :     hset->slot[ moved ]  = (uint)s;
     160     3563577 :   }
     161     3620646 :   hset->slot[ ele_idx ] = HSET_NULL;
     162     3620646 :   if( FD_UNLIKELY( !--hset->chunk[ head ].cnt ) ) {
     163      454170 :     hset->bucket_head[ b ] = hset->chunk[ head ].next;
     164      454170 :     hset->chunk[ head ].next = hset->chunk_free;
     165      454170 :     hset->chunk_free = (uint)head;
     166      454170 :   }
     167     3620646 : }
     168             : 
     169             : /* hset_seek sets it->chunk to the head of the first non-empty bucket
     170             :    in [it->bucket,it->bucket_end]. */
     171             : 
     172             : static inline void
     173             : hset_seek( fd_gossip_hset_iter_t *  it,
     174     1768569 :            fd_gossip_hset_t const * hset ) {
     175     1768569 :   ulong c = HSET_NULL;
     176     1772124 :   for( ulong b=it->bucket; b<=it->bucket_end; b++ ) {
     177     1735254 :     c = hset->bucket_head[ b ];
     178     1735254 :     if( FD_LIKELY( c!=HSET_NULL ) ) { it->bucket = b; break; }
     179     1735254 :   }
     180     1768569 :   it->chunk = c;
     181     1768569 : }
     182             : 
     183             : static inline void
     184             : hset_prefetch( fd_gossip_hset_t const * hset,
     185    19876953 :                ulong                    c ) {
     186    19876953 :   if( FD_UNLIKELY( c==HSET_NULL ) ) return;
     187    19841706 :   uchar const * h = hset->hash[ 8UL*c ];
     188    19841706 :   __builtin_prefetch( h     ); __builtin_prefetch( h+ 64UL );
     189    19841706 :   __builtin_prefetch( h+128 ); __builtin_prefetch( h+192UL );
     190    19841706 :   __builtin_prefetch( hset->chunk+c );
     191    19841706 : }
     192             : 
     193             : void
     194             : fd_gossip_hset_iter_init( fd_gossip_hset_iter_t *  it,
     195             :                           fd_gossip_hset_t const * hset,
     196             :                           ulong                    start_hash,
     197       36870 :                           ulong                    end_hash ) {
     198       36870 :   ulong shift = 64UL-hset->lg_bucket_cnt;
     199       36870 :   ulong low   = (1UL<<shift)-1UL;
     200       36870 :   it->start      = start_hash;
     201       36870 :   it->end        = end_hash;
     202       36870 :   it->filter     = (start_hash&low)!=0UL || (end_hash&low)!=low;
     203       36870 :   it->bucket     = start_hash>>shift;
     204       36870 :   it->bucket_end = end_hash>>shift;
     205       36870 :   hset_seek( it, hset );
     206       36870 :   if( FD_LIKELY( it->chunk!=HSET_NULL ) ) {
     207       34299 :     hset_prefetch( hset, it->chunk );
     208       34299 :     hset_prefetch( hset, hset->chunk[ it->chunk ].next );
     209       34299 :   }
     210       36870 : }
     211             : 
     212             : void
     213             : fd_gossip_hset_iter_next( fd_gossip_hset_iter_t *  it,
     214    19842654 :                           fd_gossip_hset_t const * hset ) {
     215    19842654 :   ulong c = hset->chunk[ it->chunk ].next;
     216    19842654 :   if( FD_UNLIKELY( c==HSET_NULL ) ) {
     217     1731699 :     it->bucket++;
     218     1731699 :     hset_seek( it, hset );
     219     1731699 :     c = it->chunk;
     220     1731699 :     if( FD_UNLIKELY( c==HSET_NULL ) ) return;
     221     1731699 :   }
     222    19808355 :   it->chunk = c;
     223             :   /* The chunk after c is usually full and random in memory; start its
     224             :      loads now so they overlap hashing c. */
     225    19808355 :   ulong n = hset->chunk[ c ].next;
     226    19808355 :   if( FD_UNLIKELY( n==HSET_NULL && it->bucket<it->bucket_end ) ) n = hset->bucket_head[ it->bucket+1UL ];
     227    19808355 :   hset_prefetch( hset, n );
     228    19808355 : }

Generated by: LCOV version 1.14