LCOV - code coverage report
Current view: top level - util/tmpl - fd_dlist.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 264 264 100.0 %
Date: 2025-06-30 04:56:04 Functions: 71 17526 0.4 %

          Line data    Source code
       1             : /* Generate prototypes, inlines and/or implementations for HPC doubly
       2             :    linked lists.  A dlist can store a practically unbounded number of
       3             :    elements.  Typical dlist operations are generally a fast O(1) time
       4             :    and dlist element memory overhead is a small O(1) space.
       5             : 
       6             :    This API is designed for ultra tight coupling with pools, treaps,
       7             :    heaps, maps, other dlists, etc.  Likewise, a dlist can be persisted
       8             :    beyond the lifetime of the creating process, used concurrently in
       9             :    many common operations, used inter-process, relocated in memory,
      10             :    naively serialized/deserialized, moved between hosts, supports index
      11             :    compression for cache and memory bandwidth efficiency, etc.
      12             : 
      13             :    Memory efficiency and flexible footprint are prioritized.
      14             : 
      15             :    Typical usage:
      16             : 
      17             :      struct myele {
      18             :        ulong prev; // Technically "DLIST_IDX_T DLIST_PREV" (default is ulong prev), do not modify while element is in the dlist
      19             :        ulong next; // Technically "DLIST_IDX_T DLIST_NEXT" (default is ulong next), do not modify while element is in the dlist
      20             :        ... prev and next can be located arbitrarily in the element and
      21             :        ... can be reused for other purposes when the element is not in a
      22             :        ... dlist.  An element should not be moved / released while an
      23             :        ... element is in a dlist
      24             :      };
      25             : 
      26             :      typedef struct myele myele_t;
      27             : 
      28             :      #define DLIST_NAME  mydlist
      29             :      #define DLIST_ELE_T myele_t
      30             :      #include "tmpl/fd_dlist.c"
      31             : 
      32             :    will declare the following APIs as a header only style library in the
      33             :    compilation unit:
      34             : 
      35             :      // mydlist_ele_max returns the theoretical maximum number of
      36             :      // elements that can be held in a mydlist.
      37             : 
      38             :      ulong mydlist_ele_max( void );
      39             : 
      40             :      // mydlist_{align,footprint} returns the alignment and footprint
      41             :      // needed for a memory region to be used as a mydlist.  align will
      42             :      // be an integer power-of-two and footprint will be a multiple of
      43             :      // align.  The values will be compile-time declaration friendly
      44             :      // (e.g. "mydlist_t mem[1];" will have the correct alignment and
      45             :      // footprint and not be larger than 4096).
      46             :      //
      47             :      // mydlist_new formats a memory region with the appropriate
      48             :      // alignment and footprint whose first byte in the caller's address
      49             :      // space is pointed to by shmem as a mydlist.  Returns shmem on
      50             :      // success and NULL on failure (logs details).  Caller is not
      51             :      // joined on return.  The dlist will be empty.
      52             :      //
      53             :      // mydlist_join joins a mydlist.  Assumes shdlist points at a
      54             :      // memory region formatted as a mydlist in the caller's address
      55             :      // space.  Returns a handle to the caller's local join on success
      56             :      // and NULL on failure (logs details).  Do not assume this is a
      57             :      // simple cast of shdlist!
      58             :      //
      59             :      // mydlist_leave leaves a mydlist.  Assumes join points to a
      60             :      // current local join.  Returns shdlist used on join.  Do not
      61             :      // assume this is a simple cast of join!
      62             :      //
      63             :      // mydlist_delete unformats a memory region used as a mydlist.
      64             :      // Assumes shdlist points to a memory region in the caller's local
      65             :      // address space formatted as a mydlist, that there are no joins to
      66             :      // the mydlist and that any application cleanup of the entries has
      67             :      // already been done.  Returns shdlist on success and NULL on
      68             :      // failure.
      69             : 
      70             :      ulong       mydlist_align    ( void );
      71             :      ulong       mydlist_footprint( void );
      72             :      void *      mydlist_new      ( void *      shmem   );
      73             :      mydlist_t * mydlist_join     ( void *      shdlist );
      74             :      void *      mydlist_leave    ( mydlist_t * join    );
      75             :      void *      mydlist_delete   ( void *      shdlist );
      76             : 
      77             :      // The below APIs assume join is a current local join to a mydlist
      78             :      // and pool is a current local join to the element storage backing
      79             :      // the dlist.
      80             :      //
      81             :      // mydlist_is_empty returns 1 if the dlist is empty and 0
      82             :      // otherwise.
      83             :      //
      84             :      // mydlist_idx_peek_{head,tail} returns the pool index of the
      85             :      // dlist's {head,tail}.  Assumes dlist is not empty.
      86             : 
      87             :      int mydlist_is_empty( mydlist_t const * join, myele_t const * pool );
      88             : 
      89             :      ulong mydlist_idx_peek_head( mydlist_t const * join, myele_t const * pool );
      90             :      ulong mydlist_idx_peek_tail( mydlist_t const * join, myele_t const * pool );
      91             : 
      92             :      // mydlist_idx_push_{head,tail} pushes the pool element whose index
      93             :      // is ele_idx to the dlist's {head,tail} and returns join.  Assumes
      94             :      // ele_idx valid and not already in the dlist.
      95             :      /
      96             :      // mydlist_idx_pop_{head,tail} pops the pool element at the dlist's
      97             :      // {head,tail} and returns its pool index.  Assumes dlist is not
      98             :      // empty.
      99             :      //
     100             :      // mydlist_idx_insert_{before,after} inserts the pool element whose
     101             :      // index is ele_idx into the dlist immediately {before,after} the
     102             :      // pool element whose index is {next_idx,prev_idx} and returns
     103             :      // join.  Assumes ele_idx is valid and not already in the dlist and
     104             :      // {next_idx,prev_idx} are already in the dlist.
     105             :      //
     106             :      // mydlist_idx_remove removes the pool element whose index is
     107             :      // ele_idx from the dlist and returns join.  Assumes ele_idx is in
     108             :      // the dlist already.
     109             :      //
     110             :      // mydlist_idx_replace replaces the pool element whose index is
     111             :      // old_idx with the pool element whose index is ele_idx in the
     112             :      // dlist and returns join.  Assumes ele_idx is not in the dlist and
     113             :      // old_idx is in the dlist already.
     114             : 
     115             :      mydlist_t * mydlist_idx_push_head    ( mydlist_t * join, ulong ele_idx,                 myele_t * pool );
     116             :      mydlist_t * mydlist_idx_push_tail    ( mydlist_t * join, ulong ele_idx,                 myele_t * pool );
     117             :      ulong       mydlist_idx_pop_head     ( mydlist_t * join,                                myele_t * pool );
     118             :      ulong       mydlist_idx_pop_tail     ( mydlist_t * join,                                myele_t * pool );
     119             :      mydlist_t * mydlist_idx_insert_before( mydlist_t * join, ulong ele_idx, ulong next_idx, myele_t * pool );
     120             :      mydlist_t * mydlist_idx_insert_after ( mydlist_t * join, ulong ele_idx, ulong prev_idx, myele_t * pool );
     121             :      mydlist_t * mydlist_idx_remove       ( mydlist_t * join, ulong ele_idx,                 myele_t * pool );
     122             :      mydlist_t * mydlist_idx_replace      ( mydlist_t * join, ulong ele_idx, ulong old_idx,  myele_t * pool );
     123             : 
     124             :      // mydlist_remove_all removes all elements from the dlist and
     125             :      // returns join.  It is the caller's responsibility to release all
     126             :      // elements to the pool as might be necessary.
     127             : 
     128             :      mydlist_t * mydlist_remove_all( mydlist_t * join, myele_t * pool );
     129             : 
     130             :      // mydlist_iter_* support fast ordered forward (head to tail) and
     131             :      // reverse (tail to head) iteration over all the elements in a
     132             :      // dlist.  Example usage:
     133             :      //
     134             :      //   for( mydlist_iter_t iter = mydlist_iter_rev_init( join, pool );
     135             :      //        !mydlist_iter_done( iter, join, pool );
     136             :      //        iter = mydlist_iter_rev_next( iter, join, pool ) ) {
     137             :      //     ulong ele_idx = mydlist_iter_idx( iter, join, pool );
     138             :      //
     139             :      //     ... process element here
     140             :      //
     141             :      //     ... IMPORTANT!  It is generally safe to insert elements
     142             :      //     ... here (though they might not be covered by this
     143             :      //     ... iteration).  It is also generally safe to remove any
     144             :      //     ... element but the current element here (the removed
     145             :      //     ... element might have already been iterated over).  It is
     146             :      //     ... straightforward to make a variant of this iterator
     147             :      //     ... that would support removing the current element here
     148             :      //     ... if desired.
     149             :      //   }
     150             : 
     151             :      struct mydlist_iter_private { ... internal use only ... };
     152             :      typedef struct mydlist_iter_private mydlist_iter_t;
     153             : 
     154             :      mydlist_iter_t  mydlist_iter_fwd_init(                      mydlist_t const * join, myele_t const * pool );
     155             :      mydlist_iter_t  mydlist_iter_rev_init(                      mydlist_t const * join, myele_t const * pool );
     156             :      int             mydlist_iter_done    ( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool );
     157             :      mydlist_iter_t  mydlist_iter_fwd_next( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool ); // assumes !done
     158             :      mydlist_iter_t  mydlist_iter_rev_next( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool ); // assumes !done
     159             :      ulong           mydlist_iter_idx     ( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool ); // assumes !done
     160             : 
     161             :      // mydlist_verify returns 0 if the mydlist is not obviously corrupt
     162             :      // or -1 (i.e. ERR_INVAL) otherwise (logs details).
     163             : 
     164             :      int
     165             :      mydlist_verify( mydlist_t const * join,    // Current local join to a mydlist.
     166             :                      ulong             ele_cnt, // Element storage size, in [0,mydlist_ele_max()]
     167             :                      myele_t const *   pool );  // Current local join to element storage, indexed [0,ele_cnt)
     168             : 
     169             :      // The above APIs have helpers that operate purely in the caller's
     170             :      // local address space when applicable.  The various elements
     171             :      // passed to / returned from these functions should be / will be
     172             :      // from the dlist's underlying pool.
     173             : 
     174             :      myele_t * mydlist_ele_peek_head( mydlist_t const * join, myele_t * pool );
     175             :      myele_t * mydlist_ele_peek_tail( mydlist_t const * join, myele_t * pool );
     176             : 
     177             :      mydlist_t * mydlist_ele_push_head    ( mydlist_t * join, myele_t * ele,                 myele_t * pool );
     178             :      mydlist_t * mydlist_ele_push_tail    ( mydlist_t * join, myele_t * ele,                 myele_t * pool );
     179             :      myele_t *   mydlist_ele_pop_head     ( mydlist_t * join,                                myele_t * pool );
     180             :      myele_t *   mydlist_ele_pop_tail     ( mydlist_t * join,                                myele_t * pool );
     181             :      mydlist_t * mydlist_ele_insert_before( mydlist_t * join, myele_t * ele, myele_t * next, myele_t * pool );
     182             :      mydlist_t * mydlist_ele_insert_after ( mydlist_t * join, myele_t * ele, myele_t * prev, myele_t * pool );
     183             :      mydlist_t * mydlist_ele_replace      ( mydlist_t * join, myele_t * ele, myele_t * old,  myele_t * pool );
     184             :      mydlist_t * mydlist_ele_remove       ( mydlist_t * join, myele_t * ele,                 myele_t * pool );
     185             : 
     186             :      myele_t * mydlist_iter_ele( mydlist_iter_t iter, mydlist_t const * join, myele_t * pool );
     187             : 
     188             :      // ... and const correct helpers when applicable
     189             : 
     190             :      myele_t const * mydlist_ele_peek_head_const( mydlist_t const * join, myele_t const * pool );
     191             :      myele_t const * mydlist_ele_peek_tail_const( mydlist_t const * join, myele_t const * pool );
     192             : 
     193             :      myele_t const * mydlist_iter_ele_const( mydlist_iter_t iter, mydlist_t const * join, myele_t const * pool );
     194             : 
     195             :    You can do this as often as you like in a compilation unit to get
     196             :    different types of dlists.  Variants exist for making header
     197             :    prototypes only and/or implementations only if making a library for
     198             :    use across multiple compilation units.  Further, options exist to use
     199             :    different hashing functions, comparison functions, etc as detailed
     200             :    below. */
     201             : 
     202             : /* TODO: DOC CONCURRENCY REQUIREMENTS */
     203             : 
     204             : /* DLIST_NAME gives the API prefix to use for a dlist */
     205             : 
     206             : #ifndef DLIST_NAME
     207             : #error "Define DLIST_NAME"
     208             : #endif
     209             : 
     210             : /* DLIST_ELE_T is the dlist element type. */
     211             : 
     212             : #ifndef DLIST_ELE_T
     213             : #error "Define DLIST_ELE_T"
     214             : #endif
     215             : 
     216             : /* DLIST_IDX_T is the type used for the prev and next fields in the
     217             :    DLIST_ELE_T.  Should be a primitive unsigned integer type.  Defaults
     218             :    to ulong.  A dlist can't use element memory regions with more
     219             :    elements than the maximum value that can be represented by a
     220             :    DLIST_IDX_T.  (E.g. if ushort, the maximum size element store
     221             :    supported by the dlist is 65535 elements.) */
     222             : 
     223             : #ifndef DLIST_IDX_T
     224             : #define DLIST_IDX_T ulong
     225             : #endif
     226             : 
     227             : /* DLIST_PREV is the DLIST_ELE_T prev field */
     228             : 
     229             : #ifndef DLIST_PREV
     230       24315 : #define DLIST_PREV prev
     231             : #endif
     232             : 
     233             : /* DLIST_NEXT is the DLIST_ELE_T next field */
     234             : 
     235             : #ifndef DLIST_NEXT
     236       24414 : #define DLIST_NEXT next
     237             : #endif
     238             : 
     239             : /* DLIST_MAGIC is the magic number to use for the structure to aid in
     240             :    persistent and/or IPC usage. */
     241             : 
     242             : #ifndef DLIST_MAGIC
     243        3381 : #define DLIST_MAGIC (0xf17eda2c37d71570UL) /* firedancer dlist version 0 */
     244             : #endif
     245             : 
     246             : /* 0 - local use only
     247             :    1 - library header declaration
     248             :    2 - library implementation */
     249             : 
     250             : #ifndef DLIST_IMPL_STYLE
     251             : #define DLIST_IMPL_STYLE 0
     252             : #endif
     253             : 
     254             : #if FD_TMPL_USE_HANDHOLDING
     255             : #include "../log/fd_log.h"
     256             : #endif
     257             : 
     258             : /* Implementation *****************************************************/
     259             : 
     260             : /* Constructors and verification log details on failure (rest only needs
     261             :    fd_bits.h, consider making logging a compile time option). */
     262             : 
     263 34565604315 : #define DLIST_(n) FD_EXPAND_THEN_CONCAT3(DLIST_NAME,_,n)
     264             : 
     265             : #if DLIST_IMPL_STYLE==0 || DLIST_IMPL_STYLE==1 /* need structures and inlines */
     266             : 
     267             : struct DLIST_(private) {
     268             : 
     269             :   /* join points here */
     270             : 
     271             :   ulong       magic; /* == DLIST_MAGIC */
     272             :   DLIST_IDX_T head;  /* index of first list element (or idx_null if empty list) */
     273             :   DLIST_IDX_T tail;  /* index of last  list element (or idx_null if empty list) */
     274             : };
     275             : 
     276             : typedef struct DLIST_(private) DLIST_(private_t);
     277             : 
     278             : typedef DLIST_(private_t) DLIST_(t);
     279             : 
     280             : typedef ulong DLIST_(iter_t);
     281             : 
     282             : FD_PROTOTYPES_BEGIN
     283             : 
     284             : /* dlist_private returns the location of the dlist header for a current
     285             :    local join.  Assumes join is a current local join.
     286             :    dlist_private_const is a const correct version. */
     287             : 
     288             : FD_FN_CONST static inline DLIST_(private_t) *
     289  1331348004 : DLIST_(private)( DLIST_(t) * join ) {
     290  1331348004 :   return (DLIST_(private_t) *)join;
     291  1331348004 : }
     292             : 
     293             : FD_FN_CONST static inline DLIST_(private_t) const *
     294  7462087932 : DLIST_(private_const)( DLIST_(t) const * join ) {
     295  7462087932 :   return (DLIST_(private_t) const *)join;
     296  7462087932 : }
     297             : 
     298             : /* dlist_private_{cidx,idx} compress / decompress 64-bit in-register
     299             :    indices to/from their in-memory representations. */
     300             : 
     301  4125310920 : FD_FN_CONST static inline DLIST_IDX_T DLIST_(private_cidx)( ulong       idx  ) { return (DLIST_IDX_T)idx;  }
     302 12374989575 : FD_FN_CONST static inline ulong       DLIST_(private_idx) ( DLIST_IDX_T cidx ) { return (ulong)      cidx; }
     303             : 
     304             : /* dlist_private_idx_null returns the element storage index that
     305             :    represents NULL. */
     306             : 
     307   825063822 : FD_FN_CONST static inline ulong DLIST_(private_idx_null)( void ) { return (ulong)(DLIST_IDX_T)~0UL; }
     308             : 
     309             : /* dlist_private_idx_is_null returns 1 if idx is the NULL dlist index
     310             :    and 0 otherwise. */
     311             : 
     312  5812740267 : FD_FN_CONST static inline int DLIST_(private_idx_is_null)( ulong idx ) { return idx==(ulong)(DLIST_IDX_T)~0UL; }
     313             : 
     314   187477731 : FD_FN_CONST static ulong DLIST_(ele_max)( void ) { return (ulong)(DLIST_IDX_T)~0UL; }
     315             : 
     316             : FD_FN_PURE static inline int
     317             : DLIST_(is_empty)( DLIST_(t) const *   join,
     318  1124963763 :                   DLIST_ELE_T const * pool ) {
     319  1124963763 :   (void)pool;
     320  1124963763 :   return DLIST_(private_idx_is_null)( DLIST_(private_idx)( DLIST_(private_const)( join )->head ) );
     321  1124963763 : }
     322             : 
     323             : FD_FN_PURE static inline ulong
     324             : DLIST_(idx_peek_head)( DLIST_(t) const *   join,
     325  2699814876 :                        DLIST_ELE_T const * pool ) {
     326  2699814876 :   (void)pool;
     327             : # if FD_TMPL_USE_HANDHOLDING
     328             :   if( FD_UNLIKELY( DLIST_(is_empty)( join, pool ) ) ) FD_LOG_CRIT(( "cannot peek on empty dlist" ));
     329             : # endif
     330  2699814876 :   return DLIST_(private_idx)( DLIST_(private_const)( join )->head );
     331  2699814876 : }
     332             : 
     333             : FD_FN_PURE static inline ulong
     334             : DLIST_(idx_peek_tail)( DLIST_(t) const *   join,
     335  2699814870 :                        DLIST_ELE_T const * pool ) {
     336  2699814870 :   (void)pool;
     337             : # if FD_TMPL_USE_HANDHOLDING
     338             :   if( FD_UNLIKELY( DLIST_(is_empty)( join, pool ) ) ) FD_LOG_CRIT(( "cannot peek on empty dlist" ));
     339             : # endif
     340  2699814870 :   return DLIST_(private_idx)( DLIST_(private_const)( join )->tail );
     341  2699814870 : }
     342             : 
     343             : static inline DLIST_(t) *
     344             : DLIST_(idx_push_head)( DLIST_(t) *   join,
     345             :                        ulong         ele_idx,
     346   168756039 :                        DLIST_ELE_T * pool ) {
     347   168756039 :   DLIST_(private_t) * dlist = DLIST_(private)( join );
     348             : 
     349   168756039 :   ulong head_idx = DLIST_(private_idx)( dlist->head );
     350             : 
     351   168756039 :   pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
     352   168756039 :   pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( head_idx );
     353             : 
     354   168756039 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( head_idx ), &pool[ head_idx ].DLIST_PREV, &dlist->tail ) =
     355   168756039 :     DLIST_(private_cidx)( ele_idx );
     356             : 
     357   168756039 :   dlist->head = DLIST_(private_cidx)( ele_idx );
     358   168756039 :   return join;
     359   168756039 : }
     360             : 
     361             : static inline DLIST_(t) *
     362             : DLIST_(idx_push_tail)( DLIST_(t) *   join,
     363             :                        ulong         ele_idx,
     364   168766356 :                        DLIST_ELE_T * pool ) {
     365   168766356 :   DLIST_(private_t) * dlist = DLIST_(private)( join );
     366             : 
     367   168766356 :   ulong tail_idx = DLIST_(private_idx)( dlist->tail );
     368             : 
     369   168766356 :   pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( tail_idx );
     370   168766356 :   pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
     371             : 
     372   168766356 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( tail_idx ), &pool[ tail_idx ].DLIST_NEXT, &dlist->head ) =
     373   168766356 :     DLIST_(private_cidx)( ele_idx );
     374             : 
     375   168766356 :   dlist->tail = DLIST_(private_cidx)( ele_idx );
     376   168766356 :   return join;
     377   168766356 : }
     378             : 
     379             : static inline ulong
     380             : DLIST_(idx_pop_head)( DLIST_(t) *   join,
     381   150031173 :                       DLIST_ELE_T * pool ) {
     382             : # if FD_TMPL_USE_HANDHOLDING
     383             :   if( FD_UNLIKELY( DLIST_(is_empty)( join, pool ) ) ) FD_LOG_CRIT(( "cannot pop from empty dlist" ));
     384             : # endif
     385   150031173 :   DLIST_(private_t) * dlist = DLIST_(private)( join );
     386             : 
     387   150031173 :   ulong ele_idx  = DLIST_(private_idx)( dlist->head ); /* Not NULL as per contract */
     388   150031173 :   ulong next_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_NEXT );
     389             : 
     390   150031173 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( next_idx ), &pool[ next_idx ].DLIST_PREV, &dlist->tail ) =
     391   150031173 :     DLIST_(private_cidx)( DLIST_(private_idx_null)() );
     392             : 
     393   150031173 :   dlist->head = DLIST_(private_cidx)( next_idx );
     394   150031173 :   return ele_idx;
     395   150031173 : }
     396             : 
     397             : static inline ulong
     398             : DLIST_(idx_pop_tail)( DLIST_(t) *   join,
     399   150025710 :                       DLIST_ELE_T * pool ) {
     400             : # if FD_TMPL_USE_HANDHOLDING
     401             :   if( FD_UNLIKELY( DLIST_(is_empty)( join, pool ) ) ) FD_LOG_CRIT(( "cannot pop from empty dlist" ));
     402             : # endif
     403   150025710 :   DLIST_(private_t) * dlist = DLIST_(private)( join );
     404             : 
     405   150025710 :   ulong ele_idx  = DLIST_(private_idx)( dlist->tail ); /* Not NULL as per contract */
     406   150025710 :   ulong prev_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_PREV );
     407             : 
     408   150025710 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( prev_idx ), &pool[ prev_idx ].DLIST_NEXT, &dlist->head ) =
     409   150025710 :     DLIST_(private_cidx)( DLIST_(private_idx_null)() );
     410             : 
     411   150025710 :   dlist->tail = DLIST_(private_cidx)( prev_idx );
     412   150025710 :   return ele_idx;
     413   150025710 : }
     414             : 
     415             : static inline DLIST_(t) *
     416             : DLIST_(idx_insert_before)( DLIST_(t) *   join,
     417             :                            ulong         ele_idx,
     418             :                            ulong         next_idx,
     419   131251101 :                            DLIST_ELE_T * pool ) {
     420   131251101 :   ulong prev_idx = DLIST_(private_idx)( pool[ next_idx ].DLIST_PREV );
     421             : 
     422   131251101 :   pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( prev_idx );
     423   131251101 :   pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( next_idx );
     424             : 
     425   131251101 :   pool[ next_idx ].DLIST_PREV = DLIST_(private_cidx)( ele_idx );
     426             : 
     427   131251101 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( prev_idx ), &pool[ prev_idx ].DLIST_NEXT, &DLIST_(private)( join )->head ) =
     428   131251101 :     DLIST_(private_cidx)( ele_idx );
     429             : 
     430   131251101 :   return join;
     431   131251101 : }
     432             : 
     433             : static inline DLIST_(t) *
     434             : DLIST_(idx_insert_after)( DLIST_(t) *   join,
     435             :                           ulong         ele_idx,
     436             :                           ulong         prev_idx,
     437   131270532 :                           DLIST_ELE_T * pool ) {
     438   131270532 :   ulong next_idx = DLIST_(private_idx)( pool[ prev_idx ].DLIST_NEXT );
     439             : 
     440   131270532 :   pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( prev_idx );
     441   131270532 :   pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( next_idx );
     442             : 
     443   131270532 :   pool[ prev_idx ].DLIST_NEXT = DLIST_(private_cidx)( ele_idx );
     444             : 
     445   131270532 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( next_idx ), &pool[ next_idx ].DLIST_PREV, &DLIST_(private)( join )->tail ) =
     446   131270532 :     DLIST_(private_cidx)( ele_idx );
     447             : 
     448   131270532 :   return join;
     449   131270532 : }
     450             : 
     451             : static inline DLIST_(t) *
     452             : DLIST_(idx_remove)( DLIST_(t) *   join,
     453             :                     ulong         ele_idx,
     454   299987019 :                     DLIST_ELE_T * pool ) {
     455   299987019 :   DLIST_(private_t) * dlist = DLIST_(private)( join );
     456             : 
     457   299987019 :   ulong prev_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_PREV );
     458   299987019 :   ulong next_idx = DLIST_(private_idx)( pool[ ele_idx ].DLIST_NEXT );
     459             : 
     460   299987019 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( next_idx ), &pool[ next_idx ].DLIST_PREV, &dlist->tail ) =
     461   299987019 :     DLIST_(private_cidx)( prev_idx );
     462             : 
     463   299987019 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( prev_idx ), &pool[ prev_idx ].DLIST_NEXT, &dlist->head ) =
     464   299987019 :     DLIST_(private_cidx)( next_idx );
     465             : 
     466   299987019 :   return join;
     467   299987019 : }
     468             : 
     469             : static inline DLIST_(t) *
     470             : DLIST_(idx_replace)( DLIST_(t) *   join,
     471             :                      ulong         ele_idx,
     472             :                      ulong         old_idx,
     473   131260047 :                      DLIST_ELE_T * pool ) {
     474   131260047 :   DLIST_(private_t) * dlist = DLIST_(private)( join );
     475             : 
     476   131260047 :   ulong prev_idx = DLIST_(private_idx)( pool[ old_idx ].DLIST_PREV );
     477   131260047 :   ulong next_idx = DLIST_(private_idx)( pool[ old_idx ].DLIST_NEXT );
     478             : 
     479   131260047 :   pool[ ele_idx ].DLIST_PREV = DLIST_(private_cidx)( prev_idx );
     480   131260047 :   pool[ ele_idx ].DLIST_NEXT = DLIST_(private_cidx)( next_idx );
     481             : 
     482   131260047 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( next_idx ), &pool[ next_idx ].DLIST_PREV, &dlist->tail ) =
     483   131260047 :     DLIST_(private_cidx)( ele_idx );
     484             : 
     485   131260047 :   *fd_ptr_if( !DLIST_(private_idx_is_null)( prev_idx ), &pool[ prev_idx ].DLIST_NEXT, &dlist->head ) =
     486   131260047 :     DLIST_(private_cidx)( ele_idx );
     487             : 
     488   131260047 :   return join;
     489   131260047 : }
     490             : 
     491             : static inline DLIST_(t) *
     492             : DLIST_(remove_all)( DLIST_(t) *   join,
     493          27 :                     DLIST_ELE_T * pool ) {
     494          27 :   (void)pool;
     495          27 :   DLIST_(private_t) * dlist = DLIST_(private)( join );
     496          27 :   dlist->head = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
     497          27 :   dlist->tail = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
     498          27 :   return join;
     499          27 : }
     500             : 
     501             : FD_FN_PURE static inline DLIST_(iter_t)
     502             : DLIST_(iter_fwd_init)( DLIST_(t) const *   join,
     503   449975742 :                        DLIST_ELE_T const * pool ) {
     504   449975742 :   (void)pool;
     505   449975742 :   return DLIST_(private_idx)( DLIST_(private_const)( join )->head );
     506   449975742 : }
     507             : 
     508             : FD_FN_PURE static inline DLIST_(iter_t)
     509             : DLIST_(iter_rev_init)( DLIST_(t) const *   join,
     510   300040953 :                        DLIST_ELE_T const * pool ) {
     511   300040953 :   (void)pool;
     512   300040953 :   return DLIST_(private_idx)( DLIST_(private_const)( join )->tail );
     513   300040953 : }
     514             : 
     515             : FD_FN_CONST static inline int
     516             : DLIST_(iter_done)( DLIST_(iter_t)      iter,
     517             :                    DLIST_(t) const *   join,
     518  2062618782 :                    DLIST_ELE_T const * pool ) {
     519  2062618782 :   (void)join; (void)pool;
     520  2062618782 :   return DLIST_(private_idx_is_null)( iter );
     521  2062618782 : }
     522             : 
     523             : FD_FN_PURE static inline DLIST_(iter_t)
     524             : DLIST_(iter_fwd_next)( DLIST_(iter_t)      iter,
     525             :                        DLIST_(t) const *   join,
     526   787505682 :                        DLIST_ELE_T const * pool ) {
     527   787505682 :   (void)join;
     528   787505682 :   return DLIST_(private_idx)( pool[ iter ].DLIST_NEXT );
     529   787505682 : }
     530             : 
     531             : FD_FN_PURE static inline DLIST_(iter_t)
     532             : DLIST_(iter_rev_next)( DLIST_(iter_t)      iter,
     533             :                        DLIST_(t) const *   join,
     534   525096405 :                        DLIST_ELE_T const * pool ) {
     535   525096405 :   (void)join;
     536   525096405 :   return DLIST_(private_idx)( pool[ iter ].DLIST_PREV );
     537   525096405 : }
     538             : 
     539             : FD_FN_CONST static inline ulong
     540             : DLIST_(iter_idx)( DLIST_(iter_t)      iter,
     541             :                   DLIST_(t) const *   join,
     542   375025248 :                   DLIST_ELE_T const * pool ) {
     543   375025248 :   (void)join; (void)pool;
     544   375025248 :   return iter;
     545   375025248 : }
     546             : 
     547             : FD_FN_PURE static inline DLIST_ELE_T *
     548             : DLIST_(ele_peek_head)( DLIST_(t) const * join,
     549   899938296 :                        DLIST_ELE_T *     pool ) {
     550   899938296 :   return pool + DLIST_(idx_peek_head)( join, pool );
     551   899938296 : }
     552             : 
     553             : FD_FN_PURE static inline DLIST_ELE_T const *
     554             : DLIST_(ele_peek_head_const)( DLIST_(t) const *   join,
     555   899938290 :                              DLIST_ELE_T const * pool ) {
     556   899938290 :   return pool + DLIST_(idx_peek_head)( join, pool );
     557   899938290 : }
     558             : 
     559             : FD_FN_PURE static inline DLIST_ELE_T *
     560             : DLIST_(ele_peek_tail)( DLIST_(t) const * join,
     561   899938290 :                        DLIST_ELE_T *     pool ) {
     562   899938290 :   return pool + DLIST_(idx_peek_tail)( join, pool );
     563   899938290 : }
     564             : 
     565             : FD_FN_PURE static inline DLIST_ELE_T const *
     566             : DLIST_(ele_peek_tail_const)( DLIST_(t) const *   join,
     567   899938290 :                              DLIST_ELE_T const * pool ) {
     568   899938290 :   return pool + DLIST_(idx_peek_tail)( join, pool );
     569   899938290 : }
     570             : 
     571             : static inline DLIST_(t) *
     572             : DLIST_(ele_push_head)( DLIST_(t) *   join,
     573             :                        DLIST_ELE_T * ele,
     574    84373425 :                        DLIST_ELE_T * pool ) {
     575    84373425 :   return DLIST_(idx_push_head)( join, (ulong)(ele-pool), pool );
     576    84373425 : }
     577             : 
     578             : static inline DLIST_(t) *
     579             : DLIST_(ele_push_tail)( DLIST_(t) *   join,
     580             :                        DLIST_ELE_T * ele,
     581    84393804 :                        DLIST_ELE_T * pool ) {
     582    84393804 :   return DLIST_(idx_push_tail)( join, (ulong)(ele-pool), pool );
     583    84393804 : }
     584             : 
     585             : static inline DLIST_ELE_T *
     586             : DLIST_(ele_pop_head)( DLIST_(t) *   join,
     587    75015066 :                       DLIST_ELE_T * pool ) {
     588    75015066 :   return pool + DLIST_(idx_pop_head)( join, pool );
     589    75015066 : }
     590             : 
     591             : static inline DLIST_ELE_T*
     592             : DLIST_(ele_pop_tail)( DLIST_(t) *   join,
     593    75016527 :                       DLIST_ELE_T * pool ) {
     594    75016527 :   return pool + DLIST_(idx_pop_tail)( join, pool );
     595    75016527 : }
     596             : 
     597             : static inline DLIST_(t) *
     598             : DLIST_(ele_insert_before)( DLIST_(t) *   join,
     599             :                            DLIST_ELE_T * ele,
     600             :                            DLIST_ELE_T * next,
     601    65610312 :                            DLIST_ELE_T * pool ) {
     602    65610312 :   return DLIST_(idx_insert_before)( join, (ulong)(ele-pool), (ulong)(next-pool), pool );
     603    65610312 : }
     604             : 
     605             : static inline DLIST_(t) *
     606             : DLIST_(ele_insert_after)( DLIST_(t) *   join,
     607             :                           DLIST_ELE_T * ele,
     608             :                           DLIST_ELE_T * prev,
     609    65643615 :                           DLIST_ELE_T * pool ) {
     610    65643615 :   return DLIST_(idx_insert_after)( join, (ulong)(ele-pool), (ulong)(prev-pool), pool );
     611    65643615 : }
     612             : 
     613             : static inline DLIST_(t) *
     614             : DLIST_(ele_replace)( DLIST_(t) *   join,
     615             :                      DLIST_ELE_T * ele,
     616             :                      DLIST_ELE_T * old,
     617    65623485 :                      DLIST_ELE_T * pool ) {
     618    65623485 :   return DLIST_(idx_replace)( join, (ulong)(ele-pool), (ulong)(old-pool), pool );
     619    65623485 : }
     620             : 
     621             : static inline DLIST_(t) *
     622             : DLIST_(ele_remove)( DLIST_(t) *   join,
     623             :                     DLIST_ELE_T * ele,
     624   149999544 :                     DLIST_ELE_T * pool ) {
     625   149999544 :   return DLIST_(idx_remove)( join, (ulong)(ele-pool), pool );
     626   149999544 : }
     627             : 
     628             : FD_FN_CONST static inline DLIST_ELE_T *
     629             : DLIST_(iter_ele)( DLIST_(iter_t)    iter,
     630             :                   DLIST_(t) const * join,
     631   150008859 :                   DLIST_ELE_T *     pool ) {
     632   150008859 :   (void)join;
     633   150008859 :   return pool + iter;
     634   150008859 : }
     635             : 
     636             : FD_FN_CONST static inline DLIST_ELE_T const *
     637             : DLIST_(iter_ele_const)( DLIST_(iter_t)      iter,
     638             :                         DLIST_(t) const *   join,
     639   224982660 :                         DLIST_ELE_T const * pool ) {
     640   224982660 :   (void)join;
     641   224982660 :   return pool + iter;
     642   224982660 : }
     643             : 
     644             : FD_PROTOTYPES_END
     645             : 
     646             : #endif
     647             : 
     648             : #if DLIST_IMPL_STYLE==1 /* need prototypes */
     649             : 
     650             : FD_PROTOTYPES_BEGIN
     651             : 
     652             : FD_FN_CONST ulong DLIST_(align)    ( void                );
     653             : FD_FN_CONST ulong DLIST_(footprint)( void                );
     654             : void *            DLIST_(new)      ( void *      shmem   );
     655             : DLIST_(t) *       DLIST_(join)     ( void *      shdlist );
     656             : void *            DLIST_(leave)    ( DLIST_(t) * join    );
     657             : void *            DLIST_(delete)   ( void *      shdlist );
     658             : 
     659             : int
     660             : DLIST_(verify)( DLIST_(t) const *   join,
     661             :                 ulong               ele_cnt,
     662             :                 DLIST_ELE_T const * pool );
     663             : 
     664             : FD_PROTOTYPES_END
     665             : 
     666             : #else /* need implementations */
     667             : 
     668             : #if DLIST_IMPL_STYLE==0 /* local only */
     669             : #define DLIST_IMPL_STATIC FD_FN_UNUSED static
     670             : #else
     671             : #define DLIST_IMPL_STATIC
     672             : #endif
     673             : 
     674             : FD_PROTOTYPES_BEGIN
     675             : 
     676       10107 : FD_FN_CONST DLIST_IMPL_STATIC ulong DLIST_(align)    ( void ) { return alignof(DLIST_(t)); }
     677           6 : FD_FN_CONST DLIST_IMPL_STATIC ulong DLIST_(footprint)( void ) { return sizeof( DLIST_(t)); }
     678             : 
     679             : DLIST_IMPL_STATIC void *
     680        3387 : DLIST_(new)( void * shmem ) {
     681             : 
     682        3387 :   if( FD_UNLIKELY( !shmem ) ) {
     683           3 :     FD_LOG_WARNING(( "NULL shmem" ));
     684           3 :     return NULL;
     685           3 :   }
     686             : 
     687        3384 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, DLIST_(align)() ) ) ) {
     688           3 :     FD_LOG_WARNING(( "misaligned shmem" ));
     689           3 :     return NULL;
     690           3 :   }
     691             : 
     692             :   // Note: Guaranteed non-zero and not otherwise used
     693             : //ulong footprint = DLIST_(footprint)();
     694             : //if( FD_UNLIKELY( !footprint ) ) {
     695             : //  FD_LOG_WARNING(( "bad footprint" ));
     696             : //  return NULL;
     697             : //}
     698             : 
     699        3381 :   DLIST_(private_t) * dlist = (DLIST_(private_t) *)shmem;
     700             : 
     701        3381 :   dlist->head = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
     702        3381 :   dlist->tail = DLIST_(private_cidx)( DLIST_(private_idx_null)() );
     703             : 
     704        3381 :   FD_COMPILER_MFENCE();
     705        3381 :   FD_VOLATILE( dlist->magic ) = DLIST_MAGIC;
     706        3381 :   FD_COMPILER_MFENCE();
     707             : 
     708        3381 :   return shmem;
     709        3384 : }
     710             : 
     711             : DLIST_IMPL_STATIC DLIST_(t) *
     712        3390 : DLIST_(join)( void * shdlist ) {
     713        3390 :   DLIST_(private_t) * dlist = (DLIST_(private_t) *)shdlist;
     714             : 
     715        3390 :   if( FD_UNLIKELY( !dlist ) ) {
     716           3 :     FD_LOG_WARNING(( "NULL shdlist" ));
     717           3 :     return NULL;
     718           3 :   }
     719             : 
     720        3387 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)dlist, DLIST_(align)() ) ) ) {
     721           3 :     FD_LOG_WARNING(( "misaligned shdlist" ));
     722           3 :     return NULL;
     723           3 :   }
     724             : 
     725        3384 :   if( FD_UNLIKELY( dlist->magic!=DLIST_MAGIC ) ) {
     726           3 :     FD_LOG_WARNING(( "bad magic" ));
     727           3 :     return NULL;
     728           3 :   }
     729             : 
     730        3381 :   return (DLIST_(t) *)dlist;
     731        3384 : }
     732             : 
     733             : DLIST_IMPL_STATIC void *
     734        3330 : DLIST_(leave)( DLIST_(t) * join ) {
     735             : 
     736        3330 :   if( FD_UNLIKELY( !join ) ) {
     737           3 :     FD_LOG_WARNING(( "NULL join" ));
     738           3 :     return NULL;
     739           3 :   }
     740             : 
     741        3327 :   return (void *)join;
     742        3330 : }
     743             : 
     744             : DLIST_IMPL_STATIC void *
     745        3336 : DLIST_(delete)( void * shdlist ) {
     746        3336 :   DLIST_(private_t) * dlist = (DLIST_(private_t) *)shdlist;
     747             : 
     748        3336 :   if( FD_UNLIKELY( !dlist ) ) {
     749           3 :     FD_LOG_WARNING(( "NULL shdlist" ));
     750           3 :     return NULL;
     751           3 :   }
     752             : 
     753        3333 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)dlist, DLIST_(align)() ) ) ) {
     754           3 :     FD_LOG_WARNING(( "misaligned shdlist" ));
     755           3 :     return NULL;
     756           3 :   }
     757             : 
     758        3330 :   if( FD_UNLIKELY( dlist->magic!=DLIST_MAGIC ) ) {
     759           3 :     FD_LOG_WARNING(( "bad magic" ));
     760           3 :     return NULL;
     761           3 :   }
     762             : 
     763        3327 :   FD_COMPILER_MFENCE();
     764        3327 :   FD_VOLATILE( dlist->magic ) = 0UL;
     765        3327 :   FD_COMPILER_MFENCE();
     766             : 
     767        3327 :   return shdlist;
     768        3330 : }
     769             : 
     770             : DLIST_IMPL_STATIC int
     771             : DLIST_(verify)( DLIST_(t) const *   join,
     772             :                 ulong               ele_cnt,
     773   187477728 :                 DLIST_ELE_T const * pool ) {
     774             : 
     775  2962643493 : # define DLIST_TEST(c) do {                                                      \
     776  2962643493 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return -1; } \
     777  2962643493 :   } while(0)
     778             : 
     779             :   /* Validate input args */
     780             : 
     781   187477728 :   DLIST_TEST( join                       );
     782   187477728 :   DLIST_TEST( ele_cnt<=DLIST_(ele_max)() );
     783   187477728 :   DLIST_TEST( (!!pool) | (!ele_cnt)      );
     784             : 
     785             :   /* Iterate forward through the dlist, validating as we go */
     786             : 
     787   187477728 :   DLIST_(private_t) const * dlist = DLIST_(private_const)( join );
     788             : 
     789   187477728 :   DLIST_TEST( dlist->magic==DLIST_MAGIC );
     790             : 
     791   187477728 :   ulong rem      = ele_cnt;
     792   187477728 :   ulong prev_idx = DLIST_(private_idx_null)();
     793   187477728 :   ulong ele_idx  = DLIST_(private_idx)( dlist->head );
     794   862562679 :   while( !DLIST_(private_idx_is_null)( ele_idx ) ) {
     795             : 
     796             :     /* Visit ele_idx */
     797             : 
     798   675084951 :     DLIST_TEST( rem ); rem--;                                                  /* Test for cycles */
     799   675084951 :     DLIST_TEST( ele_idx<ele_cnt );                                             /* Test valid ele_idx */
     800   675084951 :     DLIST_TEST( DLIST_(private_idx)( pool[ ele_idx ].DLIST_PREV )==prev_idx ); /* Test reverse link integrity */
     801             : 
     802             :     /* Advance to next element */
     803             : 
     804   675084951 :     prev_idx = ele_idx;
     805   675084951 :     ele_idx  = DLIST_(private_idx)( pool[ ele_idx ].DLIST_NEXT );
     806   675084951 :   }
     807             : 
     808   187477728 :   DLIST_TEST( DLIST_(private_idx)( dlist->tail )==prev_idx );
     809             : 
     810   187477728 : # undef DLIST_TEST
     811             : 
     812   187477728 :   return 0;
     813   187477728 : }
     814             : 
     815             : FD_PROTOTYPES_END
     816             : 
     817             : #undef DLIST_IMPL_STATIC
     818             : 
     819             : #endif
     820             : 
     821             : #undef DLIST_
     822             : 
     823             : #undef DLIST_IMPL_STYLE
     824             : #undef DLIST_MAGIC
     825             : #undef DLIST_NEXT
     826             : #undef DLIST_PREV
     827             : #undef DLIST_IDX_T
     828             : #undef DLIST_ELE_T
     829             : #undef DLIST_NAME

Generated by: LCOV version 1.14