LCOV - code coverage report
Current view: top level - funkier - fd_funkier_rec.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 239 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 16 0.0 %

          Line data    Source code
       1             : #include "fd_funkier.h"
       2             : 
       3             : /* Provide the actual record map implementation */
       4             : 
       5             : #define POOL_NAME          fd_funkier_rec_pool
       6           0 : #define POOL_ELE_T         fd_funkier_rec_t
       7             : #define POOL_IDX_T         uint
       8           0 : #define POOL_NEXT          map_next
       9             : #define POOL_IMPL_STYLE    2
      10             : #include "../util/tmpl/fd_pool_para.c"
      11             : 
      12             : #define MAP_NAME              fd_funkier_rec_map
      13           0 : #define MAP_ELE_T             fd_funkier_rec_t
      14           0 : #define MAP_KEY_T             fd_funkier_xid_key_pair_t
      15           0 : #define MAP_KEY               pair
      16             : #define MAP_KEY_EQ(k0,k1)     fd_funkier_xid_key_pair_eq((k0),(k1))
      17             : #define MAP_KEY_HASH(k0,seed) fd_funkier_xid_key_pair_hash((k0),(seed))
      18           0 : #define MAP_NEXT              map_next
      19           0 : #define MAP_MEMO              map_hash
      20           0 : #define MAP_MAGIC             (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
      21             : #define MAP_MEMOIZE           1
      22             : #define MAP_IMPL_STYLE        2
      23             : #include "../util/tmpl/fd_map_para.c"
      24             : 
      25             : fd_funkier_rec_t const *
      26             : fd_funkier_rec_query_try( fd_funkier_t *               funk,
      27             :                           fd_funkier_txn_t const *     txn,
      28             :                           fd_funkier_rec_key_t const * key,
      29           0 :                           fd_funkier_rec_query_t *     query ) {
      30             : #ifdef FD_FUNKIER_HANDHOLDING
      31             :   if( FD_UNLIKELY( funk==NULL || key==NULL || query==NULL ) ) {
      32             :     return NULL;
      33             :   }
      34             :   if( FD_UNLIKELY( txn && !fd_funkier_txn_valid( funk, txn ) ) ) {
      35             :     return NULL;
      36             :   }
      37             : #endif
      38             : 
      39           0 :   fd_wksp_t * wksp = fd_funkier_wksp( funk );
      40           0 :   fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( funk, wksp );
      41           0 :   fd_funkier_xid_key_pair_t pair[1];
      42           0 :   if( txn == NULL ) {
      43           0 :     fd_funkier_txn_xid_set_root( pair->xid );
      44           0 :   } else {
      45           0 :     fd_funkier_txn_xid_copy( pair->xid, &txn->xid );
      46           0 :   }
      47           0 :   fd_funkier_rec_key_copy( pair->key, key );
      48           0 :   for(;;) {
      49           0 :     int err = fd_funkier_rec_map_query_try( &rec_map, pair, NULL, query );
      50           0 :     if( err == FD_MAP_SUCCESS )   break;
      51           0 :     if( err == FD_MAP_ERR_KEY )   return NULL;
      52           0 :     if( err == FD_MAP_ERR_AGAIN ) continue;
      53           0 :     FD_LOG_CRIT(( "query returned err %d", err ));
      54           0 :   }
      55           0 :   return fd_funkier_rec_map_query_ele_const( query );
      56           0 : }
      57             : 
      58             : fd_funkier_rec_t const *
      59             : fd_funkier_rec_query_try_global( fd_funkier_t *               funk,
      60             :                                  fd_funkier_txn_t const *     txn,
      61             :                                  fd_funkier_rec_key_t const * key,
      62             :                                  fd_funkier_txn_t const **    txn_out,
      63           0 :                                  fd_funkier_rec_query_t *     query ) {
      64             : #ifdef FD_FUNKIER_HANDHOLDING
      65             :   if( FD_UNLIKELY( funk==NULL || key==NULL || query==NULL ) ) {
      66             :     return NULL;
      67             :   }
      68             :   if( FD_UNLIKELY( txn && !fd_funkier_txn_valid( funk, txn ) ) ) {
      69             :     return NULL;
      70             :   }
      71             : #endif
      72             : 
      73             :   /* Look for the first element in the hash chain with the right
      74             :      record key. This takes advantage of the fact that elements with
      75             :      the same record key appear on the same hash chain in order of
      76             :      newest to oldest. */
      77             : 
      78           0 :   fd_wksp_t * wksp = fd_funkier_wksp( funk );
      79           0 :   fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( funk, wksp );
      80           0 :   fd_funkier_txn_pool_t txn_pool = fd_funkier_txn_pool( funk, wksp );
      81             : 
      82           0 :   fd_funkier_xid_key_pair_t pair[1];
      83           0 :   if( txn == NULL ) {
      84           0 :     fd_funkier_txn_xid_set_root( pair->xid );
      85           0 :   } else {
      86           0 :     fd_funkier_txn_xid_copy( pair->xid, &txn->xid );
      87           0 :   }
      88           0 :   fd_funkier_rec_key_copy( pair->key, key );
      89           0 :   ulong hash  = fd_funkier_rec_map_key_hash( pair, rec_map.map->seed );
      90           0 :   ulong chain_idx = (hash & (rec_map.map->chain_cnt-1UL) );
      91           0 :   if( fd_funkier_rec_map_iter_lock( &rec_map, &chain_idx, 1, FD_MAP_FLAG_BLOCKING) ) {
      92           0 :     FD_LOG_CRIT(( "failed to lock hash chain" ));
      93           0 :   }
      94             : 
      95           0 :   fd_funkier_rec_map_shmem_private_chain_t * chain = (fd_funkier_rec_map_shmem_private_chain_t *)(rec_map.map+1) + chain_idx;
      96           0 :   query->ele     = NULL;
      97           0 :   query->chain   = chain;
      98           0 :   query->ver_cnt = chain->ver_cnt + (1UL<<43U); /* After unlock */
      99             : 
     100           0 :   for( fd_funkier_rec_map_iter_t iter = fd_funkier_rec_map_iter( &rec_map, chain_idx );
     101           0 :        !fd_funkier_rec_map_iter_done( iter );
     102           0 :        iter = fd_funkier_rec_map_iter_next( iter ) ) {
     103           0 :     fd_funkier_rec_t const * ele = fd_funkier_rec_map_iter_ele_const( iter );
     104           0 :     if( FD_LIKELY( hash == ele->map_hash ) && FD_LIKELY( fd_funkier_rec_key_eq( key, ele->pair.key ) ) ) {
     105             : 
     106             :       /* For cur_txn in path from [txn] to [root] where root is NULL */
     107             : 
     108           0 :       for( fd_funkier_txn_t const * cur_txn = txn; ; cur_txn = fd_funkier_txn_parent( cur_txn, &txn_pool ) ) {
     109             :         /* If record ele is part of transaction cur_txn, we have a
     110             :            match. According to the property above, this will be the
     111             :            youngest descendent in the transaction stack. */
     112             : 
     113           0 :         int match = FD_UNLIKELY( cur_txn ) ? /* opt for root find (FIXME: eliminate branch with cmov into txn_xid_eq?) */
     114           0 :           fd_funkier_txn_xid_eq( &cur_txn->xid, ele->pair.xid ) :
     115           0 :           fd_funkier_txn_xid_eq_root( ele->pair.xid );
     116             : 
     117           0 :         if( FD_LIKELY( match ) ) {
     118           0 :           if( txn_out ) *txn_out = cur_txn;
     119           0 :           query->ele = ( FD_UNLIKELY( ele->flags & FD_FUNKIER_REC_FLAG_ERASE ) ? NULL :
     120           0 :                          (fd_funkier_rec_t *)ele );
     121           0 :           fd_funkier_rec_map_iter_unlock( &rec_map, &chain_idx, 1 );
     122           0 :           return query->ele;
     123           0 :         }
     124             : 
     125           0 :         if( cur_txn == NULL ) break;
     126           0 :       }
     127           0 :     }
     128           0 :   }
     129           0 :   fd_funkier_rec_map_iter_unlock( &rec_map, &chain_idx, 1 );
     130           0 :   return NULL;
     131           0 : }
     132             : 
     133             : int
     134           0 : fd_funkier_rec_query_test( fd_funkier_rec_query_t * query ) {
     135           0 :   return fd_funkier_rec_map_query_test( query );
     136           0 : }
     137             : 
     138             : fd_funkier_rec_t *
     139             : fd_funkier_rec_prepare( fd_funkier_t *               funk,
     140             :                         fd_funkier_txn_t *           txn,
     141             :                         fd_funkier_rec_key_t const * key,
     142             :                         fd_funkier_rec_prepare_t *   prepare,
     143           0 :                         int *                        opt_err ) {
     144             : #ifdef FD_FUNKIER_HANDHOLDING
     145             :   if( FD_UNLIKELY( funk==NULL || key==NULL || prepare==NULL ) ) {
     146             :     fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_INVAL );
     147             :     return NULL;
     148             :   }
     149             :   if( FD_UNLIKELY( txn && !fd_funkier_txn_valid( funk, txn ) ) ) {
     150             :     fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_INVAL );
     151             :     return NULL;
     152             :   }
     153             : #endif
     154             : 
     155           0 :   if( !txn ) { /* Modifying last published */
     156           0 :     if( FD_UNLIKELY( fd_funkier_last_publish_is_frozen( funk ) ) ) {
     157           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_FROZEN );
     158           0 :       return NULL;
     159           0 :     }
     160           0 :   } else {
     161           0 :     if( FD_UNLIKELY( fd_funkier_txn_is_frozen( txn ) ) ) {
     162           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_FROZEN );
     163           0 :       return NULL;
     164           0 :     }
     165           0 :   }
     166             : 
     167           0 :   prepare->funk = funk;
     168           0 :   prepare->wksp = fd_funkier_wksp( funk );
     169           0 :   fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( funk, prepare->wksp );
     170           0 :   fd_funkier_rec_t * rec = prepare->rec = fd_funkier_rec_pool_acquire( &rec_pool, NULL, 1, opt_err );
     171           0 :   if( rec != NULL ) {
     172           0 :     if( txn == NULL ) {
     173           0 :       fd_funkier_txn_xid_set_root( rec->pair.xid );
     174           0 :       rec->txn_cidx = fd_funkier_txn_cidx( FD_FUNKIER_TXN_IDX_NULL );
     175           0 :       prepare->rec_head_idx = &funk->rec_head_idx;
     176           0 :       prepare->rec_tail_idx = &funk->rec_tail_idx;
     177           0 :     } else {
     178           0 :       fd_funkier_txn_xid_copy( rec->pair.xid, &txn->xid );
     179           0 :       fd_funkier_txn_pool_t txn_pool = fd_funkier_txn_pool( funk, prepare->wksp );
     180           0 :       rec->txn_cidx = fd_funkier_txn_cidx( (ulong)( txn - txn_pool.ele ) );
     181           0 :       prepare->rec_head_idx = &txn->rec_head_idx;
     182           0 :       prepare->rec_tail_idx = &txn->rec_tail_idx;
     183           0 :     }
     184           0 :     fd_funkier_rec_key_copy( rec->pair.key, key );
     185           0 :     fd_funkier_val_init( rec );
     186           0 :     rec->tag = 0;
     187           0 :     rec->flags = 0;
     188           0 :     rec->prev_idx = FD_FUNKIER_REC_IDX_NULL;
     189           0 :     rec->next_idx = FD_FUNKIER_REC_IDX_NULL;
     190           0 :   } else {
     191           0 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_REC );
     192           0 :   }
     193           0 :   return rec;
     194           0 : }
     195             : 
     196             : void
     197           0 : fd_funkier_rec_publish( fd_funkier_rec_prepare_t * prepare ) {
     198           0 :   fd_funkier_rec_t * rec = prepare->rec;
     199           0 :   ulong * rec_head_idx = prepare->rec_head_idx;
     200           0 :   ulong * rec_tail_idx = prepare->rec_tail_idx;
     201           0 :   fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( prepare->funk, prepare->wksp );
     202           0 :   fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( prepare->funk, prepare->wksp );
     203             : 
     204             :   /* Use the tail idx to establish an order even if there is concurrency */
     205           0 :   ulong rec_prev_idx;
     206           0 :   ulong rec_idx = (ulong)( rec - rec_pool.ele );
     207           0 :   for(;;) {
     208           0 :     rec_prev_idx = *rec_tail_idx;
     209           0 :     if( FD_ATOMIC_CAS( rec_tail_idx, rec_prev_idx, rec_idx ) == rec_prev_idx ) break;
     210           0 :   }
     211           0 :   rec->prev_idx = rec_prev_idx;
     212           0 :   if( fd_funkier_rec_idx_is_null( rec_prev_idx ) ) {
     213           0 :     *rec_head_idx = rec_idx;
     214           0 :   } else {
     215           0 :     rec_pool.ele[ rec_prev_idx ].next_idx = rec_idx;
     216           0 :   }
     217             : 
     218           0 :   if( fd_funkier_rec_map_insert( &rec_map, rec, FD_MAP_FLAG_BLOCKING ) ) {
     219           0 :     FD_LOG_CRIT(( "fd_funkier_rec_map_insert failed" ));
     220           0 :   }
     221           0 : }
     222             : 
     223             : void
     224           0 : fd_funkier_rec_cancel( fd_funkier_rec_prepare_t * prepare ) {
     225           0 :   fd_funkier_val_flush( prepare->rec, fd_funkier_alloc( prepare->funk, prepare->wksp ), prepare->wksp );
     226           0 :   fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( prepare->funk, prepare->wksp );
     227           0 :   fd_funkier_rec_pool_release( &rec_pool, prepare->rec, 1 );
     228           0 : }
     229             : 
     230             : fd_funkier_rec_t *
     231             : fd_funkier_rec_clone( fd_funkier_t *               funk,
     232             :                       fd_funkier_txn_t *           txn,
     233             :                       fd_funkier_rec_key_t const * key,
     234             :                       fd_funkier_rec_prepare_t *   prepare,
     235           0 :                       int *                        opt_err ) {
     236           0 :   fd_funkier_rec_t * new_rec = fd_funkier_rec_prepare( funk, txn, key, prepare, opt_err );
     237           0 :   if( !new_rec ) return NULL;
     238             : 
     239           0 :   for(;;) {
     240           0 :     fd_funkier_rec_query_t query[1];
     241           0 :     fd_funkier_rec_t const * old_rec = fd_funkier_rec_query_try_global( funk, txn, key, NULL, query );
     242           0 :     if( !old_rec ) {
     243           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNKIER_ERR_KEY );
     244           0 :       fd_funkier_rec_cancel( prepare );
     245           0 :       return NULL;
     246           0 :     }
     247             : 
     248           0 :     fd_wksp_t * wksp = fd_funkier_wksp( funk );
     249           0 :     ulong val_sz = old_rec->val_sz;
     250           0 :     void * buf = fd_funkier_val_truncate( new_rec, val_sz, fd_funkier_alloc( funk, wksp ), wksp, opt_err );
     251           0 :     if( !buf ) {
     252           0 :       fd_funkier_rec_cancel( prepare );
     253           0 :       return NULL;
     254           0 :     }
     255           0 :     memcpy( buf, fd_funkier_val( old_rec, wksp ), val_sz );
     256             : 
     257           0 :     if( fd_funkier_rec_query_test( query ) ) {
     258           0 :       return new_rec;
     259           0 :     }
     260           0 :   }
     261           0 : }
     262             : 
     263             : 
     264             : 
     265             : int
     266           0 : fd_funkier_rec_is_full( fd_funkier_t * funk ) {
     267           0 :   fd_wksp_t * wksp = fd_funkier_wksp( funk );
     268           0 :   fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( funk, wksp );
     269           0 :   return fd_funkier_rec_pool_is_empty( &rec_pool );
     270           0 : }
     271             : 
     272             : int
     273             : fd_funkier_rec_remove( fd_funkier_t *               funk,
     274             :                        fd_funkier_txn_t *           txn,
     275             :                        fd_funkier_rec_key_t const * key,
     276             :                        fd_funkier_rec_t **          rec_out,
     277           0 :                        ulong                        erase_data ) {
     278             : #ifdef FD_FUNKIER_HANDHOLDING
     279             :   if( FD_UNLIKELY( funk==NULL || key==NULL ) ) {
     280             :     return FD_FUNKIER_ERR_INVAL;
     281             :   }
     282             :   if( FD_UNLIKELY( txn && !fd_funkier_txn_valid( funk, txn ) ) ) {
     283             :     return FD_FUNKIER_ERR_INVAL;
     284             :   }
     285             : #endif
     286             : 
     287           0 :   if( !txn ) { /* Modifying last published */
     288           0 :     if( FD_UNLIKELY( fd_funkier_last_publish_is_frozen( funk ) ) ) {
     289           0 :       return FD_FUNKIER_ERR_FROZEN;
     290           0 :     }
     291           0 :   } else {
     292           0 :     if( FD_UNLIKELY( fd_funkier_txn_is_frozen( txn ) ) ) {
     293           0 :       return FD_FUNKIER_ERR_FROZEN;
     294           0 :     }
     295           0 :   }
     296             : 
     297           0 :   fd_wksp_t * wksp = fd_funkier_wksp( funk );
     298           0 :   fd_funkier_rec_map_t rec_map = fd_funkier_rec_map( funk, wksp );
     299           0 :   fd_funkier_xid_key_pair_t pair[1];
     300           0 :   if( txn == NULL ) {
     301           0 :     fd_funkier_txn_xid_set_root( pair->xid );
     302           0 :   } else {
     303           0 :     fd_funkier_txn_xid_copy( pair->xid, &txn->xid );
     304           0 :   }
     305           0 :   fd_funkier_rec_key_copy( pair->key, key );
     306           0 :   fd_funkier_rec_query_t query[ 1 ];
     307           0 :   for(;;) {
     308           0 :     int err = fd_funkier_rec_map_query_try( &rec_map, pair, NULL, query );
     309           0 :     if( err == FD_MAP_SUCCESS )   break;
     310           0 :     if( err == FD_MAP_ERR_KEY )   return FD_FUNKIER_SUCCESS;
     311           0 :     if( err == FD_MAP_ERR_AGAIN ) continue;
     312           0 :     FD_LOG_CRIT(( "query returned err %d", err ));
     313           0 :   }
     314             : 
     315           0 :   fd_funkier_rec_t * rec = fd_funkier_rec_map_query_ele( query );
     316           0 :   if( rec_out ) *rec_out = rec;
     317             : 
     318             :   /* Access the flags atomically */
     319           0 :   ulong old_flags;
     320           0 :   for(;;) {
     321           0 :     old_flags = rec->flags;
     322           0 :     if( FD_UNLIKELY( old_flags & FD_FUNKIER_REC_FLAG_ERASE ) ) return FD_FUNKIER_SUCCESS;
     323           0 :     if( FD_ATOMIC_CAS( &rec->flags, old_flags, old_flags | FD_FUNKIER_REC_FLAG_ERASE ) == old_flags ) break;
     324           0 :   }
     325             : 
     326             :   /* Flush the value and leave a tombstone behind. In theory, this can
     327             :      lead to an unbounded number of records, but for application
     328             :      reasons, we need to remember what was deleted. */
     329             : 
     330           0 :   fd_funkier_val_flush( rec, fd_funkier_alloc( funk, wksp ), wksp );
     331             : 
     332             :   /* At this point, the 5 most significant bytes should store data about the
     333             :      transaction that the record was updated in. */
     334             : 
     335           0 :   fd_funkier_rec_set_erase_data( rec, erase_data );
     336             : 
     337           0 :   return FD_FUNKIER_SUCCESS;
     338           0 : }
     339             : 
     340             : void
     341           0 : fd_funkier_rec_set_erase_data( fd_funkier_rec_t * rec, ulong erase_data ) {
     342           0 :   rec->flags |= ((erase_data & 0xFFFFFFFFFFUL) << (sizeof(unsigned long) * 8 - 40));
     343           0 : }
     344             : 
     345             : ulong
     346           0 : fd_funkier_rec_get_erase_data( fd_funkier_rec_t const * rec ) {
     347           0 :   return (rec->flags >> (sizeof(unsigned long) * 8 - 40)) & 0xFFFFFFFFFFUL;
     348           0 : }
     349             : 
     350             : static void
     351           0 : fd_funkier_all_iter_skip_nulls( fd_funkier_all_iter_t * iter ) {
     352           0 :   if( iter->chain_idx == iter->chain_cnt ) return;
     353           0 :   while( fd_funkier_rec_map_iter_done( iter->rec_map_iter ) ) {
     354           0 :     if( ++(iter->chain_idx) == iter->chain_cnt ) break;
     355           0 :     iter->rec_map_iter = fd_funkier_rec_map_iter( &iter->rec_map, iter->chain_idx );
     356           0 :   }
     357           0 : }
     358             : 
     359             : void
     360           0 : fd_funkier_all_iter_new( fd_funkier_t * funk, fd_funkier_all_iter_t * iter ) {
     361           0 :   fd_wksp_t * wksp = fd_funkier_wksp( funk );
     362           0 :   iter->rec_map = fd_funkier_rec_map( funk, wksp );
     363           0 :   iter->chain_cnt = fd_funkier_rec_map_chain_cnt( &iter->rec_map );
     364           0 :   iter->chain_idx = 0;
     365           0 :   iter->rec_map_iter = fd_funkier_rec_map_iter( &iter->rec_map, 0 );
     366           0 :   fd_funkier_all_iter_skip_nulls( iter );
     367           0 : }
     368             : 
     369             : int
     370           0 : fd_funkier_all_iter_done( fd_funkier_all_iter_t * iter ) {
     371           0 :   return ( iter->chain_idx == iter->chain_cnt );
     372           0 : }
     373             : 
     374             : void
     375           0 : fd_funkier_all_iter_next( fd_funkier_all_iter_t * iter ) {
     376           0 :   iter->rec_map_iter = fd_funkier_rec_map_iter_next( iter->rec_map_iter );
     377           0 :   fd_funkier_all_iter_skip_nulls( iter );
     378           0 : }
     379             : 
     380             : fd_funkier_rec_t const *
     381           0 : fd_funkier_all_iter_ele_const( fd_funkier_all_iter_t * iter ) {
     382           0 :   return fd_funkier_rec_map_iter_ele_const( iter->rec_map_iter );
     383           0 : }
     384             : 
     385             : #ifdef FD_FUNKIER_HANDHOLDING
     386             : int
     387             : fd_funkier_rec_verify( fd_funkier_t * funk ) {
     388             :   fd_wksp_t *           wksp     = fd_funkier_wksp( funk );          /* Previously verified */
     389             :   fd_funkier_txn_map_t  txn_map  = fd_funkier_txn_map( funk, wksp ); /* Previously verified */
     390             :   fd_funkier_rec_map_t  rec_map  = fd_funkier_rec_map( funk, wksp ); /* Previously verified */
     391             :   fd_funkier_txn_pool_t txn_pool = fd_funkier_txn_pool( funk, wksp ); /* Previously verified */
     392             :   fd_funkier_rec_pool_t rec_pool = fd_funkier_rec_pool( funk, wksp ); /* Previously verified */
     393             :   ulong                 txn_max  = funk->txn_max;                 /* Previously verified */
     394             :   ulong                 rec_max  = funk->rec_max;                 /* Previously verified */
     395             : 
     396             :   /* At this point, txn_map has been extensively verified */
     397             : 
     398             : # define TEST(c) do {                                                                           \
     399             :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNKIER_ERR_INVAL; } \
     400             :   } while(0)
     401             : 
     402             :   TEST( !fd_funkier_rec_map_verify( &rec_map ) );
     403             :   TEST( !fd_funkier_rec_pool_verify( &rec_pool ) );
     404             : 
     405             :   /* Iterate over all records in use */
     406             : 
     407             :   fd_funkier_all_iter_t iter[1];
     408             :   for( fd_funkier_all_iter_new( funk, iter ); !fd_funkier_all_iter_done( iter ); fd_funkier_all_iter_next( iter ) ) {
     409             :     fd_funkier_rec_t const * rec = fd_funkier_all_iter_ele_const( iter );
     410             : 
     411             :     /* Make sure every record either links up with the last published
     412             :        transaction or an in-prep transaction and the flags are sane. */
     413             : 
     414             :     fd_funkier_txn_xid_t const * txn_xid = fd_funkier_rec_xid( rec );
     415             :     ulong                        txn_idx = fd_funkier_txn_idx( rec->txn_cidx );
     416             : 
     417             :     if( fd_funkier_txn_idx_is_null( txn_idx ) ) { /* This is a record from the last published transaction */
     418             : 
     419             :       TEST( fd_funkier_txn_xid_eq_root( txn_xid ) );
     420             : 
     421             :     } else { /* This is a record from an in-prep transaction */
     422             : 
     423             :       TEST( txn_idx<txn_max );
     424             :       fd_funkier_txn_t const * txn = fd_funkier_txn_query( txn_xid, &txn_map );
     425             :       TEST( txn );
     426             :       TEST( txn==(txn_pool.ele+txn_idx) );
     427             : 
     428             :     }
     429             :   }
     430             : 
     431             :   /* Clear record tags and then verify the forward and reverse linkage */
     432             : 
     433             :   for( ulong rec_idx=0UL; rec_idx<rec_max; rec_idx++ ) rec_pool.ele[ rec_idx ].tag = 0U;
     434             : 
     435             :   do {
     436             :     ulong txn_idx = FD_FUNKIER_TXN_IDX_NULL;
     437             :     ulong rec_idx = funk->rec_head_idx;
     438             :     while( !fd_funkier_rec_idx_is_null( rec_idx ) ) {
     439             :       TEST( (rec_idx<rec_max) && (fd_funkier_txn_idx( rec_pool.ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool.ele[ rec_idx ].tag==0U );
     440             :       rec_pool.ele[ rec_idx ].tag = 1U;
     441             :       fd_funkier_rec_query_t query[1];
     442             :       fd_funkier_rec_t const * rec2 = fd_funkier_rec_query_try_global( funk, NULL, rec_pool.ele[ rec_idx ].pair.key, NULL, query );
     443             :       if( FD_UNLIKELY( rec_pool.ele[ rec_idx ].flags & FD_FUNKIER_REC_FLAG_ERASE ) )
     444             :         TEST( rec2 == NULL );
     445             :       else
     446             :         TEST( rec2 = rec_pool.ele + rec_idx );
     447             :       ulong next_idx = rec_pool.ele[ rec_idx ].next_idx;
     448             :       if( !fd_funkier_rec_idx_is_null( next_idx ) ) TEST( rec_pool.ele[ next_idx ].prev_idx==rec_idx );
     449             :       rec_idx = next_idx;
     450             :     }
     451             :     fd_funkier_txn_all_iter_t txn_iter[1];
     452             :     for( fd_funkier_txn_all_iter_new( funk, txn_iter ); !fd_funkier_txn_all_iter_done( txn_iter ); fd_funkier_txn_all_iter_next( txn_iter ) ) {
     453             :       fd_funkier_txn_t const * txn = fd_funkier_txn_all_iter_ele_const( txn_iter );
     454             : 
     455             :       ulong txn_idx = (ulong)(txn-txn_pool.ele);
     456             :       ulong rec_idx = txn->rec_head_idx;
     457             :       while( !fd_funkier_rec_idx_is_null( rec_idx ) ) {
     458             :         TEST( (rec_idx<rec_max) && (fd_funkier_txn_idx( rec_pool.ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool.ele[ rec_idx ].tag==0U );
     459             :         rec_pool.ele[ rec_idx ].tag = 1U;
     460             :         fd_funkier_rec_query_t query[1];
     461             :         fd_funkier_rec_t const * rec2 = fd_funkier_rec_query_try_global( funk, txn, rec_pool.ele[ rec_idx ].pair.key, NULL, query );
     462             :         if( FD_UNLIKELY( rec_pool.ele[ rec_idx ].flags & FD_FUNKIER_REC_FLAG_ERASE ) )
     463             :           TEST( rec2 == NULL );
     464             :         else
     465             :           TEST( rec2 = rec_pool.ele + rec_idx );
     466             :         ulong next_idx = rec_pool.ele[ rec_idx ].next_idx;
     467             :         if( !fd_funkier_rec_idx_is_null( next_idx ) ) TEST( rec_pool.ele[ next_idx ].prev_idx==rec_idx );
     468             :         rec_idx = next_idx;
     469             :       }
     470             :     }
     471             :   } while(0);
     472             : 
     473             :   do {
     474             :     ulong txn_idx = FD_FUNKIER_TXN_IDX_NULL;
     475             :     ulong rec_idx = funk->rec_tail_idx;
     476             :     while( !fd_funkier_rec_idx_is_null( rec_idx ) ) {
     477             :       TEST( (rec_idx<rec_max) && (fd_funkier_txn_idx( rec_pool.ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool.ele[ rec_idx ].tag==1U );
     478             :       rec_pool.ele[ rec_idx ].tag = 2U;
     479             :       ulong prev_idx = rec_pool.ele[ rec_idx ].prev_idx;
     480             :       if( !fd_funkier_rec_idx_is_null( prev_idx ) ) TEST( rec_pool.ele[ prev_idx ].next_idx==rec_idx );
     481             :       rec_idx = prev_idx;
     482             :     }
     483             : 
     484             :     fd_funkier_txn_all_iter_t txn_iter[1];
     485             :     for( fd_funkier_txn_all_iter_new( funk, txn_iter ); !fd_funkier_txn_all_iter_done( txn_iter ); fd_funkier_txn_all_iter_next( txn_iter ) ) {
     486             :       fd_funkier_txn_t const * txn = fd_funkier_txn_all_iter_ele_const( txn_iter );
     487             : 
     488             :       ulong txn_idx = (ulong)(txn-txn_pool.ele);
     489             :       ulong rec_idx = txn->rec_tail_idx;
     490             :       while( !fd_funkier_rec_idx_is_null( rec_idx ) ) {
     491             :         TEST( (rec_idx<rec_max) && (fd_funkier_txn_idx( rec_pool.ele[ rec_idx ].txn_cidx )==txn_idx) && rec_pool.ele[ rec_idx ].tag==1U );
     492             :         rec_pool.ele[ rec_idx ].tag = 2U;
     493             :         ulong prev_idx = rec_pool.ele[ rec_idx ].prev_idx;
     494             :         if( !fd_funkier_rec_idx_is_null( prev_idx ) ) TEST( rec_pool.ele[ prev_idx ].next_idx==rec_idx );
     495             :         rec_idx = prev_idx;
     496             :       }
     497             :     }
     498             :   } while(0);
     499             : 
     500             : # undef TEST
     501             : 
     502             :   return FD_FUNKIER_SUCCESS;
     503             : }
     504             : #endif

Generated by: LCOV version 1.14