LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_gossip_hset.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 163 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 13 0.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           0 : #define HSET_NULL (UINT_MAX)
       7             : 
       8             : static ulong
       9           0 : 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           0 :   return fd_ulong_min( fd_ulong_max( (ulong)fd_ulong_find_msb( ele_max ), 8UL )-7UL, 16UL );
      14           0 : }
      15             : 
      16             : static ulong
      17           0 : hset_chunk_max( ulong ele_max ) {
      18             :   /* Every bucket has at most one non-full chunk. */
      19           0 :   return ele_max/8UL + (1UL<<hset_lg_bucket_cnt( ele_max ));
      20           0 : }
      21             : 
      22             : FD_FN_CONST ulong
      23           0 : fd_gossip_hset_align( void ) {
      24           0 :   return FD_GOSSIP_HSET_ALIGN;
      25           0 : }
      26             : 
      27             : FD_FN_CONST ulong
      28           0 : fd_gossip_hset_footprint( ulong ele_max ) {
      29           0 :   if( FD_UNLIKELY( !ele_max || !fd_ulong_is_pow2( ele_max ) || ele_max>(1UL<<31) ) ) return 0UL;
      30           0 :   ulong chunk_max = hset_chunk_max( ele_max );
      31           0 :   ulong l;
      32           0 :   l = FD_LAYOUT_INIT;
      33           0 :   l = FD_LAYOUT_APPEND( l, FD_GOSSIP_HSET_ALIGN, sizeof(fd_gossip_hset_t)                         );
      34           0 :   l = FD_LAYOUT_APPEND( l, alignof(uint),        sizeof(uint)*(1UL<<hset_lg_bucket_cnt( ele_max )) );
      35           0 :   l = FD_LAYOUT_APPEND( l, 64UL,                 sizeof(fd_gossip_hset_chunk_t)*chunk_max          );
      36           0 :   l = FD_LAYOUT_APPEND( l, 64UL,                 256UL*chunk_max                                    );
      37           0 :   l = FD_LAYOUT_APPEND( l, alignof(uint),        sizeof(uint)*8UL*chunk_max                         );
      38           0 :   l = FD_LAYOUT_APPEND( l, alignof(uint),        sizeof(uint)*ele_max                               );
      39           0 :   return FD_LAYOUT_FINI( l, FD_GOSSIP_HSET_ALIGN );
      40           0 : }
      41             : 
      42             : void *
      43             : fd_gossip_hset_new( void * shmem,
      44           0 :                     ulong  ele_max ) {
      45           0 :   if( FD_UNLIKELY( !shmem ) ) {
      46           0 :     FD_LOG_WARNING(( "NULL shmem" ));
      47           0 :     return NULL;
      48           0 :   }
      49             : 
      50           0 :   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           0 :   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           0 :   ulong lg_bucket_cnt = hset_lg_bucket_cnt( ele_max );
      61           0 :   ulong chunk_max     = hset_chunk_max( ele_max );
      62             : 
      63           0 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
      64           0 :   fd_gossip_hset_t * hset = FD_SCRATCH_ALLOC_APPEND( l, FD_GOSSIP_HSET_ALIGN, sizeof(fd_gossip_hset_t)                  );
      65           0 :   void * _bucket_head     = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),        sizeof(uint)*(1UL<<lg_bucket_cnt)         );
      66           0 :   void * _chunk           = FD_SCRATCH_ALLOC_APPEND( l, 64UL,                 sizeof(fd_gossip_hset_chunk_t)*chunk_max  );
      67           0 :   void * _hash            = FD_SCRATCH_ALLOC_APPEND( l, 64UL,                 256UL*chunk_max                           );
      68           0 :   void * _owner           = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),        sizeof(uint)*8UL*chunk_max                );
      69           0 :   void * _slot            = FD_SCRATCH_ALLOC_APPEND( l, alignof(uint),        sizeof(uint)*ele_max                      );
      70           0 :   FD_TEST( FD_SCRATCH_ALLOC_FINI( l, FD_GOSSIP_HSET_ALIGN )==(ulong)shmem+fd_gossip_hset_footprint( ele_max ) );
      71             : 
      72           0 :   hset->ele_max       = ele_max;
      73           0 :   hset->chunk_max     = chunk_max;
      74           0 :   hset->lg_bucket_cnt = lg_bucket_cnt;
      75           0 :   hset->bucket_head   = (uint *)_bucket_head;
      76           0 :   hset->chunk         = (fd_gossip_hset_chunk_t *)_chunk;
      77           0 :   hset->hash          = (uchar (*)[ 32 ])_hash;
      78           0 :   hset->owner         = (uint *)_owner;
      79           0 :   hset->slot          = (uint *)_slot;
      80             : 
      81           0 :   for( ulong b=0UL; b<(1UL<<lg_bucket_cnt); b++ ) hset->bucket_head[ b ] = HSET_NULL;
      82           0 :   for( ulong c=0UL; c<chunk_max; c++ ) {
      83           0 :     hset->chunk[ c ].next = c+1UL<chunk_max ? (uint)(c+1UL) : HSET_NULL;
      84           0 :     hset->chunk[ c ].cnt  = 0U;
      85           0 :   }
      86           0 :   hset->chunk_free = 0U;
      87             :   /* Lanes past a chunk's cnt are read (and ignored) by bloom inserts */
      88           0 :   memset( _hash, 0, 256UL*chunk_max );
      89           0 :   for( ulong i=0UL; i<ele_max; i++ ) hset->slot[ i ] = HSET_NULL;
      90             : 
      91           0 :   FD_COMPILER_MFENCE();
      92           0 :   FD_VOLATILE( hset->magic ) = FD_GOSSIP_HSET_MAGIC;
      93           0 :   FD_COMPILER_MFENCE();
      94             : 
      95           0 :   return (void *)hset;
      96           0 : }
      97             : 
      98             : fd_gossip_hset_t *
      99           0 : fd_gossip_hset_join( void * shhset ) {
     100           0 :   if( FD_UNLIKELY( !shhset ) ) {
     101           0 :     FD_LOG_WARNING(( "NULL shhset" ));
     102           0 :     return NULL;
     103           0 :   }
     104             : 
     105           0 :   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           0 :   fd_gossip_hset_t * hset = (fd_gossip_hset_t *)shhset;
     111             : 
     112           0 :   if( FD_UNLIKELY( hset->magic!=FD_GOSSIP_HSET_MAGIC ) ) {
     113           0 :     FD_LOG_WARNING(( "bad magic" ));
     114           0 :     return NULL;
     115           0 :   }
     116             : 
     117           0 :   return hset;
     118           0 : }
     119             : 
     120             : static inline ulong
     121             : hset_bucket( fd_gossip_hset_t const * hset,
     122           0 :              uchar const *            hash ) {
     123           0 :   return fd_ulong_load_8( hash )>>(64UL-hset->lg_bucket_cnt);
     124           0 : }
     125             : 
     126             : void
     127             : fd_gossip_hset_insert( fd_gossip_hset_t * hset,
     128             :                        ulong              ele_idx,
     129           0 :                        uchar const *      hash ) {
     130           0 :   ulong b    = hset_bucket( hset, hash );
     131           0 :   ulong head = hset->bucket_head[ b ];
     132           0 :   if( FD_UNLIKELY( head==HSET_NULL || hset->chunk[ head ].cnt==8U ) ) {
     133           0 :     ulong c = hset->chunk_free;
     134           0 :     FD_TEST( c!=HSET_NULL ); /* impossible, see hset_chunk_max */
     135           0 :     hset->chunk_free       = hset->chunk[ c ].next;
     136           0 :     hset->chunk[ c ].next  = (uint)head;
     137           0 :     hset->chunk[ c ].cnt   = 0U;
     138           0 :     hset->bucket_head[ b ] = (uint)c;
     139           0 :     head = c;
     140           0 :   }
     141           0 :   ulong s = 8UL*head + hset->chunk[ head ].cnt++;
     142           0 :   memcpy( hset->hash[ s ], hash, 32UL );
     143           0 :   hset->owner[ s ]       = (uint)ele_idx;
     144           0 :   hset->slot[ ele_idx ]  = (uint)s;
     145           0 : }
     146             : 
     147             : void
     148             : fd_gossip_hset_remove( fd_gossip_hset_t * hset,
     149           0 :                        ulong              ele_idx ) {
     150           0 :   ulong s    = hset->slot[ ele_idx ];
     151           0 :   ulong b    = hset_bucket( hset, hset->hash[ s ] );
     152           0 :   ulong head = hset->bucket_head[ b ];
     153           0 :   ulong last = 8UL*head + hset->chunk[ head ].cnt - 1UL;
     154           0 :   if( FD_LIKELY( s!=last ) ) {
     155             :     /* Fill the hole with the head chunk's last hash */
     156           0 :     memcpy( hset->hash[ s ], hset->hash[ last ], 32UL );
     157           0 :     ulong moved = hset->owner[ last ];
     158           0 :     hset->owner[ s ]     = (uint)moved;
     159           0 :     hset->slot[ moved ]  = (uint)s;
     160           0 :   }
     161           0 :   hset->slot[ ele_idx ] = HSET_NULL;
     162           0 :   if( FD_UNLIKELY( !--hset->chunk[ head ].cnt ) ) {
     163           0 :     hset->bucket_head[ b ] = hset->chunk[ head ].next;
     164           0 :     hset->chunk[ head ].next = hset->chunk_free;
     165           0 :     hset->chunk_free = (uint)head;
     166           0 :   }
     167           0 : }
     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           0 :            fd_gossip_hset_t const * hset ) {
     175           0 :   ulong c = HSET_NULL;
     176           0 :   for( ulong b=it->bucket; b<=it->bucket_end; b++ ) {
     177           0 :     c = hset->bucket_head[ b ];
     178           0 :     if( FD_LIKELY( c!=HSET_NULL ) ) { it->bucket = b; break; }
     179           0 :   }
     180           0 :   it->chunk = c;
     181           0 : }
     182             : 
     183             : static inline void
     184             : hset_prefetch( fd_gossip_hset_t const * hset,
     185           0 :                ulong                    c ) {
     186           0 :   if( FD_UNLIKELY( c==HSET_NULL ) ) return;
     187           0 :   uchar const * h = hset->hash[ 8UL*c ];
     188           0 :   __builtin_prefetch( h     ); __builtin_prefetch( h+ 64UL );
     189           0 :   __builtin_prefetch( h+128 ); __builtin_prefetch( h+192UL );
     190           0 :   __builtin_prefetch( hset->chunk+c );
     191           0 : }
     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           0 :                           ulong                    end_hash ) {
     198           0 :   ulong shift = 64UL-hset->lg_bucket_cnt;
     199           0 :   ulong low   = (1UL<<shift)-1UL;
     200           0 :   it->start      = start_hash;
     201           0 :   it->end        = end_hash;
     202           0 :   it->filter     = (start_hash&low)!=0UL || (end_hash&low)!=low;
     203           0 :   it->bucket     = start_hash>>shift;
     204           0 :   it->bucket_end = end_hash>>shift;
     205           0 :   hset_seek( it, hset );
     206           0 :   if( FD_LIKELY( it->chunk!=HSET_NULL ) ) {
     207           0 :     hset_prefetch( hset, it->chunk );
     208           0 :     hset_prefetch( hset, hset->chunk[ it->chunk ].next );
     209           0 :   }
     210           0 : }
     211             : 
     212             : void
     213             : fd_gossip_hset_iter_next( fd_gossip_hset_iter_t *  it,
     214           0 :                           fd_gossip_hset_t const * hset ) {
     215           0 :   ulong c = hset->chunk[ it->chunk ].next;
     216           0 :   if( FD_UNLIKELY( c==HSET_NULL ) ) {
     217           0 :     it->bucket++;
     218           0 :     hset_seek( it, hset );
     219           0 :     c = it->chunk;
     220           0 :     if( FD_UNLIKELY( c==HSET_NULL ) ) return;
     221           0 :   }
     222           0 :   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           0 :   ulong n = hset->chunk[ c ].next;
     226           0 :   if( FD_UNLIKELY( n==HSET_NULL && it->bucket<it->bucket_end ) ) n = hset->bucket_head[ it->bucket+1UL ];
     227           0 :   hset_prefetch( hset, n );
     228           0 : }

Generated by: LCOV version 1.14