LCOV - code coverage report
Current view: top level - util/tmpl - fd_map_chain.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 309 309 100.0 %
Date: 2026-06-27 05:37:02 Functions: 1669 22902 7.3 %

          Line data    Source code
       1             : /* Generate prototypes, inlines and/or implementations for ultra high
       2             :    performance maps based on hash chains.  A map can store a practically
       3             :    unbounded number of elements but, if sized on creation for the
       4             :    maximum number of mapped elements, typical map operations are a fast
       5             :    O(1) time and map element overhead is a small O(1) space.
       6             : 
       7             :    This API is designed for ultra tight coupling with pools, treaps,
       8             :    heaps, lists, other maps, etc.  Likewise, a map can be persisted
       9             :    beyond the lifetime of the creating process, used concurrently in
      10             :    many common operations, used inter-process, relocated in memory,
      11             :    naively serialized/deserialized, moved between hosts, supports index
      12             :    compression for cache and memory bandwidth efficiency, etc.
      13             : 
      14             :    Memory efficiency and flexible footprint are prioritized.  Elements
      15             :    that are recently queried can be optionally moved to the front of
      16             :    hash chains to adaptively optimize the maps for recent queries in
      17             :    non-concurrent use cases.
      18             : 
      19             :    Typical usage:
      20             : 
      21             :      struct myele {
      22             :        ulong key;  // Technically "MAP_KEY_T MAP_KEY"  (default is ulong key),  do not modify while the element is in the map
      23             :        ulong next; // Technically "MAP_IDX_T MAP_NEXT" (default is ulong next), do not modify while the element is in the map
      24             :        ... key and next can be located arbitrarily in the element and
      25             :        ... can be reused for other purposes when the element is not in a
      26             :        ... map.  The mapping of a key to an element storage index is
      27             :        ... arbitrary but an element should not be moved / released while
      28             :        ... an element is in a map.
      29             : 
      30             :        ... if MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL is set to 1, elements
      31             :        ... must also have a prev field.  This field is used to optimize
      32             :        ... removing elements from a chain quickly, if they are pointed
      33             :        ... to by some other index.
      34             :        ulong prev; // Technically "MAP_IDX_T MAP_PREV" (default is ulong prev), do not modify while the element is in the map
      35             :      };
      36             : 
      37             :      typedef struct myele myele_t;
      38             : 
      39             :      #define MAP_NAME  mymap
      40             :      #define MAP_ELE_T myele_t
      41             :      #include "tmpl/fd_map_chain.c"
      42             : 
      43             :    will declare the following APIs as a header only style library in the
      44             :    compilation unit:
      45             : 
      46             :      // mymap_ele_max returns the theoretical maximum number of elements
      47             :      // that can be mapped by a mymap.
      48             : 
      49             :      ulong mymap_ele_max( void );
      50             : 
      51             :      // mymap_chain_max returns the theoretical maximum number possible
      52             :      // chains in a mymap.  Will be an integer power-of-two.
      53             : 
      54             :      ulong mymap_chain_max( void );
      55             : 
      56             :      // mymap_chain_cnt_est returns a reasonable number of chains to use
      57             :      // for a map that is expected to hold up to ele_max_est elements.
      58             :      // ele_max_est will be clamped to be in [1,mymap_ele_max()] and the
      59             :      // return value will be an integer power-of-two in
      60             :      // [1,mymap_chain_max()].
      61             : 
      62             :      ulong mymap_chain_cnt_est( ulong ele_max_est );
      63             : 
      64             :      // mymap_{align,footprint} returns the alignment and footprint
      65             :      // needed for a memory region to be used as a mymap.  align will be
      66             :      // an integer power-of-two and footprint will be a multiple of
      67             :      // align.  footprint returns 0 if chain_cnt is not an integer
      68             :      // power-of-two in [1,mymap_chain_max()].
      69             :      //
      70             :      // mymap_new formats a memory region with the appropriate alignment
      71             :      // and footprint whose first byte in the caller's address space is
      72             :      // pointed by shmem for use as a mymap.  Returns shmem on success
      73             :      // and NULL on failure (logs details).  Caller is not joined on
      74             :      // return.  The map will be empty.
      75             :      //
      76             :      // mymap_join joins a mymap.  Assumes shmap points at a memory
      77             :      // region formatted as a mymap in the caller's address space.
      78             :      // Returns a handle to the caller's local join on success and NULL
      79             :      // on failure (logs details).  Do not assume this is a simple cast
      80             :      // of shmap!
      81             :      //
      82             :      // mymap_leave leaves a mymap.  Assumes join points to a current
      83             :      // local join.  Returns shmap used on join.  Do not assume this is
      84             :      // a simple cast of join!
      85             :      //
      86             :      // mymap_delete unformats a memory region used as a mymap.  Assumes
      87             :      // shmap points to a memory region in the caller's local address
      88             :      // space formatted as a mymap, that there are no joins to the mymap
      89             :      // and that any application cleanup of the entries has already been
      90             :      // done.  Returns shmap on success and NULL on failure.
      91             : 
      92             :      ulong     mymap_align    ( void );
      93             :      ulong     mymap_footprint( ulong chain_cnt );
      94             :      void *    mymap_new      ( void * shmem, ulong chain_cnt, ulong seed );
      95             :      mymap_t * mymap_join     ( void * shmap );
      96             :      void *    mymap_leave    ( mymap_t * join  );
      97             :      void *    mymap_delete   ( void * shmap );
      98             : 
      99             :      // mymap_{chain_cnt,seed} return the values used to construct the
     100             :      // map.  They assume join is a current local join.  The values will
     101             :      // be constant for the map lifetime.
     102             : 
     103             :      ulong mymap_chain_cnt( mymap_t const * join );
     104             :      ulong mymap_seed     ( mymap_t const * join );
     105             : 
     106             :      // mymap_ele_cnt returns the current number of elements in the map.
     107             :      // Requires MAP_COUNT.
     108             : 
     109             :      ulong mymap_ele_cnt( mymap_t const * join );
     110             : 
     111             :      // mymap_key_{eq,hash} expose the provided MAP_KEY_{EQ,HASH} macros
     112             :      // as inline functions with strict semantics.  They assume that the
     113             :      // provided pointers are in the caller's address space to keys that
     114             :      // will not be changed concurrently.  They retain no interest in
     115             :      // any keys on return.
     116             :      //
     117             :      // mymap_key_eq returns 1 if *k0 and *k1 are equal and 0 otherwise.
     118             :      //
     119             :      // mymap_key_hash returns the hash of *key using the hash function
     120             :      // seed.  Should ideally be a random mapping from MAP_KEY_T to
     121             :      // ulong but this depends on what the user actually passed in for
     122             :      // MAP_KEY_HASH.  The seed used by a particular instance of a map
     123             :      // can be obtained above.
     124             : 
     125             :      int   mymap_key_eq  ( ulong * k0,  ulong * k1 );
     126             :      ulong mymap_key_hash( ulong * key, ulong seed );
     127             : 
     128             :      // mymap_idx_insert inserts an element into the map.  The caller
     129             :      // promises the element is not currently in the map and that
     130             :      // element key is not equal to the key of any other element
     131             :      // currently in the map, except if MAP_MULTI is enabled.  Assumes
     132             :      // there are no concurrent operations on the map.  This always
     133             :      // succeeds.
     134             : 
     135             :      mymap_t *                            // Returns join
     136             :      mymap_idx_insert( mymap_t * join,    // Current local join to element map
     137             :                        ulong     ele_idx, // Index of element to insert
     138             :                        myele_t * pool );  // Current local join to element storage
     139             : 
     140             :      // mymap_idx_remove removes the mapping of a key to an element.
     141             :      // Assumes there are no concurrent operations on the map and that
     142             :      // *key will not be modified during the remove.  The map retains no
     143             :      // interest in key.  On success, the map will no longer have an
     144             :      // interest in the returned element.  On failure, the returned
     145             :      // index lifetime will be that of the sentinel.
     146             : 
     147             :      ulong                                     // Index of removed element on success, sentinel on failure
     148             :      mymap_idx_remove( mymap_t *     join,     // Current local join to element map
     149             :                        ulong const * key,      // Points to the key to remove in the caller's address space
     150             :                        ulong         sentinel, // Value to return if key not in map
     151             :                        myele_t *     pool );   // Current local join to element storage
     152             : 
     153             :      // mymap_idx_query finds the element corresponding to key.  Assumes
     154             :      // there are no concurrent operations on the map and that *key will
     155             :      // not be modified during the query.  The map retains no interest
     156             :      // in key.  On success, the returned index lifetime will be at
     157             :      // least as long as the corresponding element is still in the map.
     158             :      // On failure, the returned index lifetime will be that of the
     159             :      // sentinel.
     160             : 
     161             :      ulong                                    // Index of found element on success, sentinel on failure
     162             :      mymap_idx_query( mymap_t *     join,     // Current local join to the element map
     163             :                       ulong const * key,      // Points to the key to find in the caller's address space
     164             :                       ulong         sentinel, // Value to return on failure
     165             :                       myele_t *     pool );   // Current local join to element storage
     166             : 
     167             :      // mymap_idx_query_const is the same as mymap_idx_query but
     168             :      // supports concurrent queries so long as there no concurrently
     169             :      // running insert/remove/query operations.  The value fields of the
     170             :      // element returned by this function can be changed by the
     171             :      // application but it is up to the application to manage
     172             :      // concurrency between different users modifying the same element.
     173             : 
     174             :      ulong                                            // Index of found element on success, sentinel on failure
     175             :      mymap_idx_query_const( mymap_t const * join,     // Current local join to element map
     176             :                             ulong const *   key,      // Points to the key to find in the caller's address space
     177             :                             ulong           sentinel, // Value to return on failure
     178             :                             myele_t const * pool );   // Current local join to element storage
     179             : 
     180             :      // The mymap_ele_{insert,remove,query,query_const} variants are the
     181             :      // same as the above but use pointers in the caller's address
     182             :      // instead of pool element storage indices.
     183             : 
     184             :      mymap_t *                           // Returns join
     185             :      mymap_ele_insert( mymap_t * join,   // Current local join to element map
     186             :                        myele_t * ele,    // Element to insert (assumed to be in pool)
     187             :                        myele_t * pool ); // Current local join to element storage
     188             : 
     189             :      myele_t *                                 // Removed element on success (will be from pool), sentinel on failure
     190             :      mymap_ele_remove( mymap_t *     join,     // Current local join to element map
     191             :                        ulong const * key,      // Points to the key to remove in the caller's address space
     192             :                        myele_t *     sentinel, // Value to return if key not in map
     193             :                        myele_t *     pool );   // Current local join to element storage
     194             : 
     195             :      myele_t *                                // Found element on success (will be from pool), sentinel on failure
     196             :      mymap_ele_query( mymap_t *     join,     // Current local join to element map
     197             :                       ulong const * key,      // Points to the key to find in the caller's address space
     198             :                       myele_t *     sentinel, // Value to return if key not in map
     199             :                       myele_t *     pool );   // Current local join to element storage
     200             : 
     201             :      myele_t const *                                  // Found element on success (will be from pool), sentinel on failure
     202             :      mymap_ele_query_const( mymap_t const * join,     // Current local join to element map
     203             :                             ulong const *   key,      // Points to the key to find in the caller's address space
     204             :                             myele_t const * sentinel, // Value to return if key not in map
     205             :                             myele_t const * pool );   // Current local join to element storage
     206             : 
     207             :      // mymap_iter_* support fast iteration over all the elements in a
     208             :      // map.  The iteration will be in a random order but the order will
     209             :      // be identical if repeated with no insert/remove/query operations
     210             :      // done in between.  Assumes no concurrent insert/remove/query
     211             :      // operations (query_const is fine).  Example usage:
     212             :      //
     213             :      //   for( mymap_iter_t iter = mymap_iter_init( join, pool );
     214             :      //        !mymap_iter_done( iter, join, pool );
     215             :      //        iter = mymap_iter_next( iter, join, pool ) ) {
     216             :      //     ulong ele_idx = mymap_iter_idx( iter, join, pool );
     217             :      //
     218             :      //     ... process element here
     219             :      //
     220             :      //     ... IMPORTANT!  It is _not_ _safe_ to insert, remove
     221             :      //     ... or query here (query_const is fine).
     222             :      //   }
     223             : 
     224             :      struct mymap_iter_private { ... internal use only ... };
     225             :      typedef struct mymap_iter_private mymap_iter_t;
     226             : 
     227             :      mymap_iter_t    mymap_iter_init     (                    mymap_t const * join, myele_t const * pool );
     228             :      int             mymap_iter_done     ( mymap_iter_t iter, mymap_t const * join, myele_t const * pool );
     229             :      mymap_iter_t    mymap_iter_next     ( mymap_iter_t iter, mymap_t const * join, myele_t const * pool ); // assumes not done
     230             :      ulong           mymap_iter_idx      ( mymap_iter_t iter, mymap_t const * join, myele_t const * pool ); // assumes not done
     231             :      myele_t *       mymap_iter_ele      ( mymap_iter_t iter, mymap_t const * join, myele_t *       pool ); // assumes not done
     232             :      myele_t const * mymap_iter_ele_const( mymap_iter_t iter, mymap_t const * join, myele_t const * pool ); // assumes not done
     233             : 
     234             :      // mymap_verify returns 0 if the mymap is not obviously corrupt or
     235             :      // -1 (i.e. ERR_INVAL) otherwise (logs details).
     236             : 
     237             :      int
     238             :      mymap_verify( mymap_t const * join,    // Current local join to a mymap.
     239             :                    ulong           ele_max, // Element storage size, in [0,mymap_ele_max()]
     240             :                    myele_t const * pool );  // Current local join to element storage, indexed [0,ele_max)
     241             : 
     242             :    You can do this as often as you like in a compilation unit to get
     243             :    different types of maps.  Variants exist for making header prototypes
     244             :    only and/or implementations if doing a library with multiple
     245             :    compilation units.  Further, options exist to use index compression,
     246             :    different hashing functions, comparison functions, etc as detailed
     247             :    below.
     248             : 
     249             :    If MAP_MULTI is defined to be 1, the map will support multiple
     250             :    entries for the same key. In this case, the existing API works as
     251             :    is, but new methods are provided:
     252             : 
     253             :      ulong                                           // Index of found element on success, sentinel on failure
     254             :      mymap_idx_next_const( ulong           prev,     // Previous result of mymap_idx_query_const
     255             :                            ulong           sentinel, // Value to return on failure
     256             :                            myele_t const * pool );   // Current local join to element storage
     257             : 
     258             :      myele_t const *                                 // Found element on success (will be from pool), sentinel on failure
     259             :      mymap_ele_next_const( myele_t const * prev,     // Previous result of mymap_ele_query_const
     260             :                            myele_t const * sentinel, // Value to return if key not in map
     261             :                            myele_t const * pool );   // Current local join to element storage
     262             : 
     263             :    These take a previous result from query_const (or next_const) and
     264             :    return the next element with the same key in the chain.
     265             : 
     266             :   IF MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL is defined to be 1, the map will
     267             :   support removing elements with a known index from the chain in O(1)
     268             :   time without needing to search for them in the chain.  This requires
     269             :   threading a reverse pointer through the elements in the chain.
     270             : 
     271             :     void
     272             :     mymap_idx_remove_fast( mymap_t * join,    // Current uslocal join to element map
     273             :                            ulong     ele_idx, // Index of element to remove in the element storage
     274             :                            myele_t * pool );  // Current local join to element storage
     275             : 
     276             : */
     277             : 
     278             : #include "fd_map.h"
     279             : 
     280             : /* MAP_NAME gives the API prefix to use for map */
     281             : 
     282             : #ifndef MAP_NAME
     283             : #error "Define MAP_NAME"
     284             : #endif
     285             : 
     286             : /* MAP_ELE_T is the map element type. */
     287             : 
     288             : #ifndef MAP_ELE_T
     289             : #error "Define MAP_ELE_T"
     290             : #endif
     291             : 
     292             : /* MAP_KEY_T is the map key type */
     293             : 
     294             : #ifndef MAP_KEY_T
     295             : #define MAP_KEY_T ulong
     296             : #endif
     297             : 
     298             : /* MAP_KEY is the MAP_ELE_T key field */
     299             : 
     300             : #ifndef MAP_KEY
     301     3776418 : #define MAP_KEY key
     302             : #endif
     303             : 
     304             : /* MAP_IDX_T is the type used for the next field in the MAP_ELE_T.
     305             :    Should be a primitive unsigned integer type.  Defaults to ulong.  A
     306             :    map can't use element stores with more elements than the maximum
     307             :    value that can be represented by a MAP_IDX_T.  (E.g. if ushort, the
     308             :    maximum size element store usable by the map is 65535 elements.) */
     309             : 
     310             : #ifndef MAP_IDX_T
     311     8767686 : #define MAP_IDX_T ulong
     312             : #endif
     313             : 
     314             : /* MAP_NEXT is the MAP_ELE_T next field */
     315             : 
     316             : #ifndef MAP_NEXT
     317   229211673 : #define MAP_NEXT next
     318             : #endif
     319             : 
     320             : /* If we're using the optimized random access removal, then we also need
     321             :    a MAP_ELE_T prev field. */
     322             : #if MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
     323             : #  ifndef MAP_PREV
     324     2647326 : #    define MAP_PREV prev
     325             : #  endif
     326             : #endif
     327             : 
     328             : /* MAP_KEY_EQ returns 0/1 if *k0 is the same/different as *k1 */
     329             : 
     330             : #ifndef MAP_KEY_EQ
     331  5656732506 : #define MAP_KEY_EQ(k0,k1) ((*(k0))==(*(k1)))
     332             : #endif
     333             : 
     334             : /* MAP_KEY_HASH maps *key into ulong uniform pseudo randomly. */
     335             : 
     336             : #ifndef MAP_KEY_HASH
     337     6566346 : #define MAP_KEY_HASH(key,seed) fd_ulong_hash( (*(key)) ^ (seed) )
     338             : #endif
     339             : 
     340             : /* MAP_MAGIC is the magic number to use for the structure to aid in
     341             :    persistent and or IPC usage. */
     342             : 
     343             : #ifndef MAP_MAGIC
     344        7884 : #define MAP_MAGIC (0xf17eda2c37c3a900UL) /* firedancer cmap version 0 */
     345             : #endif
     346             : 
     347             : /* 0 - local use only
     348             :    1 - library header declaration
     349             :    2 - library implementation */
     350             : 
     351             : #ifndef MAP_IMPL_STYLE
     352             : #define MAP_IMPL_STYLE 0
     353             : #endif
     354             : 
     355             : /* 0 - do NOT support multiple values with the same key
     356             :    1 - support multiple values with the same key */
     357             : 
     358             : #ifndef MAP_MULTI
     359             : #define MAP_MULTI 0
     360             : #endif
     361             : 
     362             : /* MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL controls whether the map supports
     363             :    removal of an element in a chain without iterating the whole chain
     364             :    from the beginning. */
     365             : 
     366             : #ifndef MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
     367             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 0
     368             : #endif
     369             : 
     370             : /* MAP_INSERT_FENCE prevents the compiler from reordering the two
     371             :    operations: setting the next pointer of the new chain head and
     372             :    updating the chain head. */
     373             : 
     374             : #ifndef MAP_INSERT_FENCE
     375             : #define MAP_INSERT_FENCE 0
     376             : #endif
     377             : 
     378             : /* MAP_COUNT specifies whether to keep track of map element count. */
     379             : 
     380             : #ifndef MAP_COUNT
     381             : #define MAP_COUNT 0
     382             : #endif
     383             : 
     384             : /* Implementation *****************************************************/
     385             : 
     386             : /* Constructors and verification log details on failure (rest only needs
     387             :    fd_bits.h, consider making logging a compile time option). */
     388             : 
     389             : #include "../log/fd_log.h"
     390             : 
     391 95671884453 : #define MAP_(n) FD_EXPAND_THEN_CONCAT3(MAP_NAME,_,n)
     392             : 
     393             : #if MAP_IMPL_STYLE==0 || MAP_IMPL_STYLE==1 /* need structures and inlines */
     394             : 
     395             : struct MAP_(private) {
     396             : 
     397             :   /* join points here */
     398             : 
     399             :   /* FIXME: consider having an ele_cnt for number of elements in the
     400             :      underlying storage? (probably not) consider having a memo of the
     401             :      chain in which an element is stored and/or using doubly linked
     402             :      chains?  We could do a faster remove by index then. */
     403             : 
     404             :   ulong magic;     /* == MAP_MAGIC */
     405             :   ulong seed;      /* Hash seed, arbitrary */
     406             :   ulong chain_cnt; /* Number of chains, positive integer power-of-two */
     407             : # if MAP_COUNT
     408             :   ulong ele_cnt;   /* Number of elements */
     409             : # endif
     410             : 
     411             :   /* MAP_IDX_T chain[ chain_cnt ] here */
     412             : 
     413             : };
     414             : 
     415             : typedef struct MAP_(private) MAP_(private_t);
     416             : 
     417             : typedef MAP_(private_t) MAP_(t);
     418             : 
     419             : typedef struct fd_map_chain_iter MAP_(iter_t);
     420             : 
     421             : FD_PROTOTYPES_BEGIN
     422             : 
     423             : /* map_private returns the location of the map private for a current
     424             :    local join.  Assumes join is a current local join.  map_private_const
     425             :    is a const correct version. */
     426             : 
     427  1225842822 : FD_FN_CONST static inline MAP_(private_t) *       MAP_(private)      ( MAP_(t) *       join ) { return join; }
     428  2696890165 : FD_FN_CONST static inline MAP_(private_t) const * MAP_(private_const)( MAP_(t) const * join ) { return join; }
     429             : 
     430             : /* map_private_chain returns the location in the caller's address space
     431             :    of the map chains.  Assumes map is valid.  map_private_chain_const is
     432             :    a const correct version. */
     433             : 
     434             : FD_FN_CONST static inline MAP_IDX_T *
     435  1225717318 : MAP_(private_chain)( MAP_(private_t) * map ) {
     436  1225717318 :   return (MAP_IDX_T *)(map+1);
     437  1225717318 : }
     438             : 
     439             : FD_FN_CONST static inline MAP_IDX_T const *
     440  2696890135 : MAP_(private_chain_const)( MAP_(private_t) const * map ) {
     441  2696890135 :   return (MAP_IDX_T const *)(map+1);
     442  2696890135 : }
     443             : 
     444             : /* map_private_chain_idx returns the index of the chain (in
     445             :    [0,chain_cnt) that will contain the element corresponding to key (if
     446             :    present at all) for a map with chain_cnt elements and seed.  Assumes
     447             :    chain_cnt is an integer power-of-two.  Assumes key points to a key is
     448             :    in the caller's address space that will not be changed concurrently.
     449             :    Retains no interest in key on return. */
     450             : 
     451             : FD_FN_PURE static inline ulong
     452             : MAP_(private_chain_idx)( MAP_KEY_T const * key,
     453             :                          ulong             seed,
     454  5041361428 :                          ulong             chain_cnt ) {
     455  5041361428 :   return (MAP_KEY_HASH( (key), (seed) )) & (chain_cnt-1UL);
     456  5041361428 : }
     457             : 
     458             : /* map_private_{box,unbox} compress / decompress 64-bit in-register
     459             :    indices to/from their in-memory representations. */
     460             : 
     461   396250745 : FD_FN_CONST static inline MAP_IDX_T MAP_(private_box)  ( ulong     idx  ) { return (MAP_IDX_T)idx;  }
     462 34747681384 : FD_FN_CONST static inline ulong     MAP_(private_unbox)( MAP_IDX_T cidx ) { return (ulong)    cidx; }
     463             : 
     464             : /* map_private_idx_null returns the element storage index that
     465             :    represents NULL. */
     466             : 
     467 14123464988 : FD_FN_CONST static inline ulong MAP_(private_idx_null)( void ) { return (ulong)(MAP_IDX_T)(~0UL); }
     468             : 
     469             : /* map_private_idx_is_null returns 1 if idx is the NULL map index
     470             :    and 0 otherwise. */
     471             : 
     472 37015885095 : FD_FN_CONST static inline int MAP_(private_idx_is_null)( ulong idx ) { return idx==(ulong)(MAP_IDX_T)(~0UL); }
     473             : 
     474    12620277 : FD_FN_CONST static inline ulong MAP_(ele_max)( void ) { return (ulong)(MAP_IDX_T)(~0UL); }
     475             : 
     476             : FD_FN_CONST static inline ulong
     477    12642465 : MAP_(chain_max)( void ) {
     478    12642465 :   return fd_ulong_pow2_dn( (ULONG_MAX + 1UL - alignof(MAP_(t)) - sizeof(MAP_(t))) / sizeof(MAP_IDX_T) );
     479    12642465 : }
     480             : 
     481             : FD_FN_CONST static inline ulong
     482     6008400 : MAP_(chain_cnt_est)( ulong ele_max_est ) {
     483             : 
     484             :   /* Clamp to be in [1,ele_max] (as ele_max_est 0 is degenerate and as
     485             :      the map is guaranteed to hold at most ele_max keys). */
     486             : 
     487     6008400 :   ele_max_est = fd_ulong_min( fd_ulong_max( ele_max_est, 1UL ), MAP_(ele_max)() );
     488             : 
     489             :   /* Compute the number of chains as the power of 2 that makes the
     490             :      average chain length between ~1 and ~2 when ele_max_est are stored
     491             :      in the map and then clamp to the chain max. */
     492             : 
     493     6008400 :   ulong chain_min = (ele_max_est>>1) + (ele_max_est&1UL); /* chain_min = ceil(ele_max_est/2), in [1,2^63], computed w/o overflow */
     494     6008400 :   ulong chain_cnt = fd_ulong_pow2_up( chain_min );        /* Power of 2 in [1,2^63] */
     495             : 
     496     6008400 :   return fd_ulong_min( chain_cnt, MAP_(chain_max)() );
     497     6008400 : }
     498             : 
     499          12 : FD_FN_PURE static inline ulong MAP_(chain_cnt)( MAP_(t) const * join ) { return MAP_(private_const)( join )->chain_cnt; }
     500           6 : FD_FN_PURE static inline ulong MAP_(seed)     ( MAP_(t) const * join ) { return MAP_(private_const)( join )->seed;      }
     501             : #if MAP_COUNT
     502          12 : FD_FN_PURE static inline ulong MAP_(ele_cnt)  ( MAP_(t) const * join ) { return MAP_(private_const)( join )->ele_cnt;   }
     503             : #endif
     504             : 
     505             : FD_FN_PURE static inline int
     506             : MAP_(key_eq)( MAP_KEY_T const * k0,
     507  5801527672 :               MAP_KEY_T const * k1 ) {
     508  5801527672 :   return !!(MAP_KEY_EQ( (k0), (k1) ));
     509  5801527672 : }
     510             : 
     511             : FD_FN_PURE static inline ulong
     512             : MAP_(key_hash)( MAP_KEY_T const * key,
     513     6001038 :                 ulong             seed ) {
     514     6001038 :   return (MAP_KEY_HASH( (key), (seed) ));
     515     6001038 : }
     516             : 
     517             : FD_FN_PURE static inline MAP_(iter_t)
     518             : MAP_(iter_init)( MAP_(t) const *   join,
     519     3111909 :                  MAP_ELE_T const * pool ) {
     520     3111909 :   (void)pool;
     521             : 
     522     3111909 :   MAP_(private_t) const * map   = MAP_(private_const)( join );
     523     3111909 :   MAP_IDX_T const *       chain = MAP_(private_chain_const)( map );
     524             : 
     525             :   /* Find first element.  If the map is empty, chain_rem will be 0 and
     526             :      ele_idx will be idx_null. */
     527             : 
     528     3111909 :   ulong chain_rem = map->chain_cnt; /* At least 1 */
     529     3111909 :   ulong ele_idx;
     530    31868443 :   do {
     531    31868443 :     ele_idx = MAP_(private_unbox)( chain[ chain_rem-1UL ] );
     532    31868443 :     if( !MAP_(private_idx_is_null)( ele_idx ) ) break;
     533    31868443 :   } while( --chain_rem );
     534             : 
     535     3111909 :   MAP_(iter_t) iter;
     536     3111909 :   iter.chain_rem = chain_rem;
     537     3111909 :   iter.ele_idx   = ele_idx;
     538     3111909 :   return iter;
     539     3111909 : }
     540             : 
     541             : FD_FN_CONST static inline int
     542             : MAP_(iter_done)( MAP_(iter_t)      iter,
     543             :                  MAP_(t) const *   join,
     544   796321626 :                  MAP_ELE_T const * pool ) {
     545   796321626 :   (void)join; (void)pool;
     546   796321626 :   return !iter.chain_rem;
     547   796321626 : }
     548             : 
     549             : __attribute__((warn_unused_result)) FD_FN_PURE static inline MAP_(iter_t)
     550             : MAP_(iter_next)( MAP_(iter_t)      iter,
     551             :                  MAP_(t) const *   join,
     552   793207452 :                  MAP_ELE_T const * pool ) {
     553             : # if FD_TMPL_USE_HANDHOLDING
     554             :   if( FD_UNLIKELY( MAP_(iter_done)( iter, join, pool ) ) ) FD_LOG_CRIT(( "assumes not done" ));
     555             : # endif
     556   793207452 :   ulong chain_rem = iter.chain_rem;
     557   793207452 :   ulong ele_idx   = iter.ele_idx;
     558             : 
     559             :   /* At this point, we are just finished iterating over element ele_idx
     560             :      on chain chain_rem-1 and we already iterated over all elements in
     561             :      chains (chain_rem,chain_cnt] and all elements in chain chain_rem-1
     562             :      before this element.  As such, ele_idx is in [0,ele_cnt) and
     563             :      chain_rem is in (0,chain_cnt].  Get the next element in the chain
     564             :      chain_rem-1. */
     565             : 
     566   793207452 :   ele_idx = MAP_(private_unbox)( pool[ ele_idx ].MAP_NEXT );
     567   793207452 :   if( MAP_(private_idx_is_null)( ele_idx ) ) {
     568             : 
     569             :     /* There were no more elements in chain chain_rem-1.  Find the next
     570             :        chain to start processing.  If all unprocessed chains are empty,
     571             :        then we are done. */
     572             : 
     573   384350476 :     MAP_IDX_T const * chain = MAP_(private_chain_const)( MAP_(private_const)( join ) );
     574   796026194 :     while( --chain_rem ) {
     575   792921998 :       ele_idx = MAP_(private_unbox)( chain[ chain_rem-1UL ] );
     576   792921998 :       if( !MAP_(private_idx_is_null)( ele_idx ) ) break;
     577   792921998 :     }
     578             : 
     579   384350476 :   }
     580             : 
     581   793207452 :   iter.chain_rem = chain_rem;
     582   793207452 :   iter.ele_idx   = ele_idx;
     583   793207452 :   return iter;
     584   793207452 : }
     585             : 
     586             : FD_FN_CONST static inline ulong
     587             : MAP_(iter_idx)( MAP_(iter_t)    iter,
     588             :                 MAP_(t) const * join,
     589   787970514 :                 MAP_ELE_T *     pool ) {
     590             : # if FD_TMPL_USE_HANDHOLDING
     591             :   if( FD_UNLIKELY( MAP_(iter_done)( iter, join, pool ) ) ) FD_LOG_CRIT(( "assumes not done" ));
     592             : # endif
     593   787970514 :   (void)join; (void)pool;
     594   787970514 :   return iter.ele_idx;
     595   787970514 : }
     596             : 
     597             : FD_FN_CONST static inline MAP_ELE_T *
     598             : MAP_(iter_ele)( MAP_(iter_t)    iter,
     599             :                 MAP_(t) const * join,
     600   793204464 :                 MAP_ELE_T *     pool ) {
     601             : # if FD_TMPL_USE_HANDHOLDING
     602             :   if( FD_UNLIKELY( MAP_(iter_done)( iter, join, pool ) ) ) FD_LOG_CRIT(( "assumes not done" ));
     603             : # endif
     604   793204464 :   (void)join;
     605   793204464 :   return pool + iter.ele_idx;
     606   793204464 : }
     607             : 
     608             : FD_FN_CONST static inline MAP_ELE_T const *
     609             : MAP_(iter_ele_const) ( MAP_(iter_t)      iter,
     610             :                        MAP_(t) const *   join,
     611   787969401 :                        MAP_ELE_T const * pool ) {
     612             : # if FD_TMPL_USE_HANDHOLDING
     613             :   if( FD_UNLIKELY( MAP_(iter_done)( iter, join, pool ) ) ) FD_LOG_CRIT(( "assumes not done" ));
     614             : # endif
     615   787969401 :   (void)join;
     616   787969401 :   return pool + iter.ele_idx;
     617   787969401 : }
     618             : 
     619             : FD_PROTOTYPES_END
     620             : 
     621             : #endif
     622             : 
     623             : #if MAP_IMPL_STYLE==1 /* need prototypes */
     624             : 
     625             : FD_PROTOTYPES_BEGIN
     626             : 
     627             : FD_FN_CONST ulong MAP_(align)    ( void );
     628             : FD_FN_CONST ulong MAP_(footprint)( ulong chain_cnt );
     629             : void *            MAP_(new)      ( void * shmem, ulong chain_cnt, ulong seed );
     630             : MAP_(t) *         MAP_(join)     ( void * shmap );
     631             : void *            MAP_(leave)    ( MAP_(t) * join );
     632             : void *            MAP_(delete)   ( void * shmap );
     633             : 
     634             : void              MAP_(reset)    ( MAP_(t) * join );
     635             : 
     636             : MAP_(t) *
     637             : MAP_(idx_insert)( MAP_(t) *   join,
     638             :                   ulong       ele_idx,
     639             :                   MAP_ELE_T * pool );
     640             : 
     641             : ulong
     642             : MAP_(idx_remove)( MAP_(t) *         join,
     643             :                   MAP_KEY_T const * key,
     644             :                   ulong             sentinel,
     645             :                   MAP_ELE_T *       pool );
     646             : 
     647             : #if MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
     648             : void
     649             : MAP_(idx_remove_fast)( MAP_(t) *   join,
     650             :                        ulong       ele_idx,
     651             :                        MAP_ELE_T * pool );
     652             : 
     653             : MAP_ELE_T *
     654             : MAP_(ele_remove_fast)( MAP_(t) *   join,
     655             :                        MAP_ELE_T * ele,
     656             :                        MAP_ELE_T * pool  );
     657             : #endif
     658             : 
     659             : ulong
     660             : MAP_(idx_query)( MAP_(t) *         join,
     661             :                  MAP_KEY_T const * key,
     662             :                  ulong             sentinel,
     663             :                  MAP_ELE_T *       pool );
     664             : 
     665             : FD_FN_PURE ulong
     666             : MAP_(idx_query_const)( MAP_(t) const *   join,
     667             :                        MAP_KEY_T const * key,
     668             :                        ulong             sentinel,
     669             :                        MAP_ELE_T const * pool );
     670             : 
     671             : #if MAP_MULTI!=0
     672             : 
     673             : FD_FN_PURE ulong
     674             : MAP_(idx_next_const)( ulong             prev,     // Previous result of mymap_idx_query_const
     675             :                       ulong             sentinel, // Value to return on failure
     676             :                       MAP_ELE_T const * pool );   // Current local join to element storage
     677             : 
     678             : #endif /* MAP_MULTI!=0 */
     679             : 
     680             : int
     681             : MAP_(verify)( MAP_(t) const *   join,
     682             :               ulong             ele_max,
     683             :               MAP_ELE_T const * pool );
     684             : 
     685             : FD_PROTOTYPES_END
     686             : 
     687             : #else /* need implementations */
     688             : 
     689             : #if MAP_IMPL_STYLE==0 /* local only */
     690             : #define MAP_IMPL_STATIC FD_FN_UNUSED static
     691             : #else
     692             : #define MAP_IMPL_STATIC
     693             : #endif
     694             : 
     695       43032 : FD_FN_CONST MAP_IMPL_STATIC ulong MAP_(align)( void ) { return alignof(MAP_(t)); }
     696             : 
     697             : FD_FN_CONST MAP_IMPL_STATIC ulong
     698       22200 : MAP_(footprint)( ulong chain_cnt ) {
     699       22200 :   if( !(fd_ulong_is_pow2( chain_cnt ) & (chain_cnt<=MAP_(chain_max)())) ) return 0UL;
     700       22164 :   return fd_ulong_align_up( sizeof(MAP_(t)) + chain_cnt*sizeof(MAP_IDX_T), alignof(MAP_(t)) ); /* no overflow */
     701       22200 : }
     702             : 
     703             : MAP_IMPL_STATIC void *
     704             : MAP_(new)( void * shmap,
     705             :            ulong  chain_cnt,
     706        8049 :            ulong  seed ) {
     707             : 
     708        8049 :   if( FD_UNLIKELY( !shmap ) ) {
     709           6 :     FD_LOG_WARNING(( "NULL shmap" ));
     710           6 :     return NULL;
     711           6 :   }
     712             : 
     713        8043 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmap, MAP_(align)() ) ) ) {
     714           6 :     FD_LOG_WARNING(( "misaligned shmap" ));
     715           6 :     return NULL;
     716           6 :   }
     717             : 
     718        8037 :   ulong footprint = MAP_(footprint)( chain_cnt );
     719        8037 :   if( FD_UNLIKELY( !footprint ) ) {
     720          18 :     FD_LOG_WARNING(( "bad footprint" ));
     721          18 :     return NULL;
     722          18 :   }
     723             : 
     724             :   /* seed is arbitrary */
     725             : 
     726             :   /* Init the metadata */
     727             : 
     728        8019 :   MAP_(private_t) * map = (MAP_(private_t) *)shmap;
     729             : 
     730        8019 :   map->seed      = seed;
     731        8019 :   map->chain_cnt = chain_cnt;
     732             : # if MAP_COUNT
     733             :   map->ele_cnt   = 0UL;
     734             : # endif
     735             : 
     736             :   /* Set all the chains to NULL */
     737             : 
     738        8019 :   MAP_IDX_T * chain = MAP_(private_chain)( map );
     739    50461563 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) chain[ chain_idx ] = MAP_(private_box)( MAP_(private_idx_null)() );
     740             : 
     741        8019 :   FD_COMPILER_MFENCE();
     742        8019 :   FD_VOLATILE( map->magic ) = MAP_MAGIC;
     743        8019 :   FD_COMPILER_MFENCE();
     744             : 
     745        8019 :   return shmap;
     746        8037 : }
     747             : 
     748             : MAP_IMPL_STATIC MAP_(t) *
     749        8082 : MAP_(join)( void * shmap ) {
     750        8082 :   MAP_(private_t) * map = (MAP_(private_t) *)shmap;
     751             : 
     752        8082 :   if( FD_UNLIKELY( !map ) ) {
     753           6 :     FD_LOG_WARNING(( "NULL shmap" ));
     754           6 :     return NULL;
     755           6 :   }
     756             : 
     757        8076 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)map, MAP_(align)() ) ) ) {
     758           6 :     FD_LOG_WARNING(( "misaligned shmap" ));
     759           6 :     return NULL;
     760           6 :   }
     761             : 
     762        8070 :   if( FD_UNLIKELY( map->magic!=MAP_MAGIC ) ) {
     763           6 :     FD_LOG_WARNING(( "bad magic" ));
     764           6 :     return NULL;
     765           6 :   }
     766             : 
     767        8064 :   return (MAP_(t) *)map;
     768        8070 : }
     769             : 
     770             : MAP_IMPL_STATIC void *
     771         285 : MAP_(leave)( MAP_(t) * join ) {
     772             : 
     773         285 :   if( FD_UNLIKELY( !join ) ) {
     774           6 :     FD_LOG_WARNING(( "NULL join" ));
     775           6 :     return NULL;
     776           6 :   }
     777             : 
     778         279 :   return (void *)join;
     779         285 : }
     780             : 
     781             : MAP_IMPL_STATIC void *
     782         294 : MAP_(delete)( void * shmap ) {
     783         294 :   MAP_(private_t) * map = (MAP_(private_t) *)shmap;
     784             : 
     785         294 :   if( FD_UNLIKELY( !map ) ) {
     786           6 :     FD_LOG_WARNING(( "NULL shmap" ));
     787           6 :     return NULL;
     788           6 :   }
     789             : 
     790         288 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)map, MAP_(align)() ) ) ) {
     791           6 :     FD_LOG_WARNING(( "misaligned shmap" ));
     792           6 :     return NULL;
     793           6 :   }
     794             : 
     795         282 :   if( FD_UNLIKELY( map->magic!=MAP_MAGIC ) ) {
     796           6 :     FD_LOG_WARNING(( "bad magic" ));
     797           6 :     return NULL;
     798           6 :   }
     799             : 
     800         276 :   FD_COMPILER_MFENCE();
     801         276 :   FD_VOLATILE( map->magic ) = 0UL;
     802         276 :   FD_COMPILER_MFENCE();
     803             : 
     804         276 :   return shmap;
     805         282 : }
     806             : 
     807             : MAP_IMPL_STATIC void
     808       69615 : MAP_(reset)( MAP_(t) * join ) {
     809       69615 :   MAP_(private_t) * map = MAP_(private)( join );
     810             : 
     811       69615 :   MAP_IDX_T * chain = MAP_(private_chain)( map );
     812    47878239 :   for( ulong chain_idx=0UL; chain_idx<map->chain_cnt; chain_idx++ ) chain[ chain_idx ] = MAP_(private_box)( MAP_(private_idx_null)() );
     813             : 
     814             : # if MAP_COUNT
     815             :   map->ele_cnt = 0UL;
     816             : # endif
     817       69615 : }
     818             : 
     819             : MAP_IMPL_STATIC ulong
     820             : MAP_(idx_query)( MAP_(t) *         join,
     821             :                  MAP_KEY_T const * key,
     822             :                  ulong             sentinel,
     823  1209908739 :                  MAP_ELE_T *       pool ) {
     824  1209908739 :   MAP_(private_t) * map = MAP_(private)( join );
     825             : 
     826             :   /* Find the key */
     827             : 
     828  1209908739 :   MAP_IDX_T * head = MAP_(private_chain)( map ) + MAP_(private_chain_idx)( key, map->seed, map->chain_cnt );
     829  1209908739 :   MAP_IDX_T * cur  = head;
     830  2125840223 :   for(;;) {
     831  2125840223 :     ulong ele_idx = MAP_(private_unbox)( *cur );
     832  2125840223 :     if( FD_UNLIKELY( MAP_(private_idx_is_null)( ele_idx ) ) ) break; /* optimize for found */
     833  1327171169 :     if( FD_LIKELY( MAP_(key_eq)( key, &pool[ ele_idx ].MAP_KEY ) ) ) { /* optimize for found */
     834             :       /* Found, move it to the front of the chain */
     835   411239685 :       if( FD_UNLIKELY( cur!=head ) ) { /* Assume already at head from previous query */
     836             : #if MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
     837       26480 :         if( FD_UNLIKELY( !MAP_(private_idx_is_null)( pool[ ele_idx ].MAP_NEXT ) ) ) pool[ pool[ ele_idx ].MAP_NEXT ].MAP_PREV = pool[ ele_idx ].MAP_PREV;
     838       26480 :         pool[ ele_idx ].MAP_PREV = MAP_(private_box)( MAP_(private_idx_null)() );
     839       26480 :         pool[ *head   ].MAP_PREV = MAP_(private_box)( ele_idx );
     840             : #endif
     841   289662185 :         *cur = pool[ ele_idx ].MAP_NEXT;
     842   289662185 :         pool[ ele_idx ].MAP_NEXT = *head;
     843   289662185 :         *head = MAP_(private_box)( ele_idx );
     844   289662185 :       }
     845   411239685 :       return ele_idx;
     846   411239685 :     }
     847   915931484 :     cur = &pool[ ele_idx ].MAP_NEXT; /* Retain the pointer to next so we can rewrite it later. */
     848   915931484 :   }
     849             : 
     850             :   /* Not found */
     851             : 
     852   798669054 :   return sentinel;
     853  1209908739 : }
     854             : 
     855             : MAP_IMPL_STATIC MAP_(t) *
     856             : MAP_(idx_insert)( MAP_(t) *   join,
     857             :                   ulong       ele_idx,
     858     5848707 :                   MAP_ELE_T * pool ) {
     859             : # if FD_TMPL_USE_HANDHOLDING && !MAP_MULTI
     860             :   if( FD_UNLIKELY( !MAP_(private_idx_is_null)( MAP_(idx_query)( join, &pool[ ele_idx ].MAP_KEY, MAP_(private_idx_null)(), pool ) ) ) ) {
     861             :     FD_LOG_CRIT(( "ele_idx already in map" ));
     862             :   }
     863             : # endif
     864     5848707 :   MAP_(private_t) * map = MAP_(private)( join );
     865             : 
     866     5848707 :   MAP_IDX_T * head = MAP_(private_chain)( map ) + MAP_(private_chain_idx)( &pool[ ele_idx ].MAP_KEY, map->seed, map->chain_cnt );
     867             : 
     868             : # if MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
     869     2210790 :   if( FD_UNLIKELY( !MAP_(private_idx_is_null)( *head ) ) ) pool[ *head ].MAP_PREV = MAP_(private_box)( ele_idx );
     870     2210790 :   pool[ ele_idx ].MAP_PREV = MAP_(private_box)( MAP_(private_idx_null)() );
     871             : # endif
     872     5848707 :   pool[ ele_idx ].MAP_NEXT = *head;
     873             : # if MAP_INSERT_FENCE
     874          84 :   FD_COMPILER_MFENCE();
     875             : # endif
     876     5848707 :   *head = MAP_(private_box)( ele_idx );
     877             : # if MAP_COUNT
     878             :   map->ele_cnt++;
     879             : # endif
     880             : 
     881     5848707 :   return join;
     882     5848707 : }
     883             : 
     884             : MAP_IMPL_STATIC ulong
     885             : MAP_(idx_remove)( MAP_(t) *         join,
     886             :                   MAP_KEY_T const * key,
     887             :                   ulong             sentinel,
     888     8249130 :                   MAP_ELE_T *       pool ) {
     889     8249130 :   MAP_(private_t) * map = MAP_(private)( join );
     890             : 
     891             :   /* Find the key */
     892             : 
     893     8249130 :   MAP_IDX_T * cur = MAP_(private_chain)( map ) + MAP_(private_chain_idx)( key, map->seed, map->chain_cnt );
     894    12383508 :   for(;;) {
     895    12383508 :     ulong ele_idx = MAP_(private_unbox)( *cur );
     896    12383508 :     if( FD_UNLIKELY( MAP_(private_idx_is_null)( ele_idx ) ) ) break; /* optimize for found (it is remove after all) */
     897     7707054 :     if( FD_LIKELY( MAP_(key_eq)( key, &pool[ ele_idx ].MAP_KEY ) ) ) { /* " */
     898     3572676 :       *cur = pool[ ele_idx ].MAP_NEXT;
     899             : #if MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
     900          57 :     if( FD_UNLIKELY( !MAP_(private_idx_is_null)( pool[ ele_idx ].MAP_NEXT ) ) ) pool[ pool[ ele_idx ].MAP_NEXT ].MAP_PREV = pool[ ele_idx ].MAP_PREV;
     901             : #endif
     902             : #if MAP_COUNT
     903             :       map->ele_cnt--;
     904             : #endif
     905     3572676 :       return ele_idx;
     906     3572676 :     }
     907     4134378 :     cur = &pool[ ele_idx ].MAP_NEXT; /* Retain the pointer to next so we can rewrite it later. */
     908     4134378 :   }
     909             : 
     910             :   /* Not found */
     911             : 
     912     4676454 :   return sentinel;
     913     8249130 : }
     914             : 
     915             : #if MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
     916             : MAP_IMPL_STATIC void
     917             : MAP_(idx_remove_fast)( MAP_(t) *   join,
     918             :                        ulong       ele_idx,
     919     1766631 :                        MAP_ELE_T * pool ) {
     920     1766631 :   MAP_(private_t) * map = MAP_(private)( join );
     921             : 
     922     1766631 :   MAP_ELE_T * ele = pool+ele_idx;
     923             : 
     924     1766631 :   if( FD_UNLIKELY( !MAP_(private_idx_is_null)( ele->MAP_NEXT ) ) )          pool[ ele->MAP_NEXT ].MAP_PREV = ele->MAP_PREV;
     925             : 
     926     1766631 :   if( FD_UNLIKELY( !MAP_(private_idx_is_null)( ele->MAP_PREV ) ) )          pool[ ele->MAP_PREV ].MAP_NEXT = ele->MAP_NEXT;
     927     1633102 :   else { MAP_(private_chain)( map )[ MAP_(private_chain_idx)( &ele->MAP_KEY, map->seed, map->chain_cnt ) ] = ele->MAP_NEXT; }
     928             : 
     929             : #if MAP_COUNT
     930             :   map->ele_cnt--;
     931             : #endif
     932     1766631 : }
     933             : 
     934             : MAP_IMPL_STATIC MAP_ELE_T *
     935             : MAP_(ele_remove_fast)( MAP_(t) *   join,
     936             :                        MAP_ELE_T * ele,
     937       36969 :                        MAP_ELE_T * pool  ) {
     938       36969 :   MAP_(idx_remove_fast)( join, (ulong)(ele-pool), pool );
     939       36969 :   return ele;
     940       36969 : }
     941             : #endif
     942             : 
     943             : FD_FN_PURE MAP_IMPL_STATIC ulong
     944             : MAP_(idx_query_const)( MAP_(t) const *   join,
     945             :                        MAP_KEY_T const * key,
     946             :                        ulong             sentinel,
     947  2302815891 :                        MAP_ELE_T const * pool ) {
     948  2302815891 :   MAP_(private_t) const * map = MAP_(private_const)( join );
     949             : 
     950             :   /* Find the key */
     951             : 
     952  2302815891 :   MAP_IDX_T const * cur = MAP_(private_chain_const)( map ) + MAP_(private_chain_idx)( key, map->seed, map->chain_cnt );
     953  3516334174 :   for(;;) {
     954  3516334174 :     ulong ele_idx = MAP_(private_unbox)( *cur );
     955  3516334174 :     if( FD_UNLIKELY( MAP_(private_idx_is_null)( ele_idx ) ) ) break; /* optimize for found */
     956  3514400671 :     if( FD_LIKELY( MAP_(key_eq)( key, &pool[ ele_idx ].MAP_KEY ) ) ) return ele_idx; /* optimize for found */
     957  1213518283 :     cur = &pool[ ele_idx ].MAP_NEXT;
     958  1213518283 :   }
     959             : 
     960             :   /* Not found */
     961             : 
     962     1933503 :   return sentinel;
     963  2302815891 : }
     964             : 
     965             : #if MAP_MULTI!=0
     966             : 
     967             : FD_FN_PURE MAP_IMPL_STATIC ulong
     968             : MAP_(idx_next_const)( ulong             prev,     // Previous result of mymap_idx_query_const
     969             :                       ulong             sentinel, // Value to return on failure
     970   688807248 :                       MAP_ELE_T const * pool ) {  // Current local join to element storage
     971             :   /* Go to next element in chain */
     972             : 
     973   688807248 :   MAP_ELE_T const * prev_ele = &pool[ prev ];
     974   688807248 :   MAP_IDX_T const * cur = &prev_ele->MAP_NEXT;
     975   928249054 :   for(;;) {
     976   928249054 :     ulong ele_idx = MAP_(private_unbox)( *cur );
     977   928249054 :     if( FD_UNLIKELY( MAP_(private_idx_is_null)( ele_idx ) ) ) break; /* optimize for found */
     978   928248775 :     if( FD_LIKELY( MAP_(key_eq)( &prev_ele->MAP_KEY, &pool[ ele_idx ].MAP_KEY ) ) ) return ele_idx; /* optimize for found */
     979   239441806 :     cur = &pool[ ele_idx ].MAP_NEXT;
     980   239441806 :   }
     981             : 
     982             :   /* Not found */
     983             : 
     984         279 :   return sentinel;
     985   688807248 : }
     986             : 
     987             : #endif /* MAP_MULTI!=0 */
     988             : 
     989             : MAP_IMPL_STATIC int
     990             : MAP_(verify)( MAP_(t) const *   join,
     991             :               ulong             ele_max,
     992     6611877 :               MAP_ELE_T const * pool ) {
     993             : 
     994  6200407431 : # define MAP_TEST(c) do {                                                        \
     995  6200407431 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return -1; } \
     996  6200407431 :   } while(0)
     997             : 
     998             :   /* Validate input arguments */
     999             : 
    1000     6611877 :   MAP_TEST( join );
    1001     6611871 :   MAP_TEST( ele_max<=MAP_(ele_max)() );
    1002     6611865 :   MAP_TEST( (!!pool) | (!ele_max) );
    1003             : 
    1004             :   /* Validate metadata */
    1005             : 
    1006     6611859 :   MAP_(private_t) const * map = MAP_(private_const)( join );
    1007             : 
    1008     6611859 :   MAP_TEST( map->magic==MAP_MAGIC );
    1009             : 
    1010     6611859 :   ulong seed = map->seed;
    1011             :   /* seed is arbitrary */
    1012             : 
    1013     6611859 :   ulong chain_cnt = map->chain_cnt;
    1014     6611859 :   MAP_TEST( fd_ulong_is_pow2( chain_cnt ) );
    1015     6611859 :   MAP_TEST( chain_cnt<=MAP_(chain_max)()  );
    1016             : 
    1017             :   /* Visit each map entry, doing simple chain integrity checks */
    1018             : 
    1019     6611859 :   MAP_IDX_T const * chain = MAP_(private_chain_const)( map );
    1020             : 
    1021     6611859 :   ulong ele_cnt = 0UL; (void)ele_cnt;
    1022     6611859 :   ulong rem = ele_max; /* We can visit at most ele_max elements */
    1023 11767144287 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ ) {
    1024 11760532428 :     ulong prev_ele = MAP_(private_idx_null)();
    1025 11760532428 :     (void)prev_ele;
    1026 11760532428 :     for( ulong ele_idx = MAP_(private_unbox)( chain[ chain_idx ] );
    1027 13273438266 :          !MAP_(private_idx_is_null)( ele_idx );
    1028 11760532428 :          ele_idx = MAP_(private_unbox)( pool[ ele_idx ].MAP_NEXT ) ) {
    1029  1512905838 :       MAP_TEST( rem ); rem--;                                                                      /* Check for cycles */
    1030  1512905838 :       MAP_TEST( ele_idx<ele_max );                                                                 /* Check valid element index */
    1031  1512905838 :       MAP_TEST( MAP_(private_chain_idx)( &pool[ ele_idx ].MAP_KEY, seed, chain_cnt )==chain_idx ); /* Check in correct chain */
    1032             : #if MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
    1033   109112877 :       MAP_TEST( pool[ ele_idx ].MAP_PREV==prev_ele );
    1034   109112877 :       prev_ele = ele_idx;
    1035   109112877 : #endif
    1036  1403792961 :       ele_cnt++;
    1037  1403792961 :     }
    1038 11760532428 :   }
    1039             : 
    1040             : # if MAP_COUNT
    1041          12 :   MAP_TEST( MAP_(ele_cnt)( join )==ele_cnt );
    1042          12 : # endif
    1043             : 
    1044             :   /* At this point, we know that there are no cycles in the map chains,
    1045             :      all indices are inbounds and elements are in the correct chains for
    1046             :      probes.  It is possible for there to be keys that have been
    1047             :      inserted more than once though.  We visit all the nodes a second
    1048             :      time and make sure each probe resolves to itself to prove the key
    1049             :      of every element in the map is unique.  (We could do this faster if
    1050             :      we could tag the elements but this verify is written to not modify
    1051             :      any memory.) */
    1052             : 
    1053 11767144287 :   for( ulong chain_idx=0UL; chain_idx<chain_cnt; chain_idx++ )
    1054 11760532428 :     for( ulong ele_idx = MAP_(private_unbox)( chain[ chain_idx ] );
    1055 13273438266 :          !MAP_(private_idx_is_null)( ele_idx );
    1056 11760532428 :          ele_idx = MAP_(private_unbox)( pool[ ele_idx ].MAP_NEXT ) ) {
    1057             : #if MAP_MULTI==0
    1058   896161131 :       MAP_TEST( MAP_(idx_query_const)( join, &pool[ ele_idx ].MAP_KEY, ULONG_MAX, pool )==ele_idx );
    1059             : #else
    1060             :       ulong idx2;
    1061   616744707 :       for (idx2 = MAP_(idx_query_const)( join, &pool[ ele_idx ].MAP_KEY, ULONG_MAX, pool );
    1062  1046107332 :            idx2 != ULONG_MAX && idx2 != ele_idx;
    1063   429362625 :            idx2 = MAP_(idx_next_const)( idx2, ULONG_MAX, pool )) ;
    1064   616744707 :       MAP_TEST( idx2 == ele_idx );
    1065             : #endif
    1066  1512905838 :     }
    1067             : 
    1068     6611859 : # undef MAP_TEST
    1069             : 
    1070     6611859 :   return 0;
    1071     6611859 : }
    1072             : 
    1073             : #undef MAP_IMPL_STATIC
    1074             : 
    1075             : #endif
    1076             : 
    1077             : #if MAP_IMPL_STYLE==0 || MAP_IMPL_STYLE==1 /* need inlines */
    1078             : 
    1079             : FD_PROTOTYPES_BEGIN
    1080             : 
    1081             : static inline MAP_(t) *
    1082             : MAP_(ele_insert)( MAP_(t) *   join,
    1083             :                   MAP_ELE_T * ele,
    1084     3681663 :                   MAP_ELE_T * pool ) {
    1085     3681663 :   return MAP_(idx_insert)( join, (ulong)(ele-pool), pool );
    1086     3681663 : }
    1087             : 
    1088             : static inline MAP_ELE_T *
    1089             : MAP_(ele_remove)( MAP_(t) *         join,
    1090             :                   MAP_KEY_T const * key,
    1091             :                   MAP_ELE_T *       sentinel,
    1092     7767903 :                   MAP_ELE_T *       pool ) {
    1093     7767903 :   ulong ele_idx = MAP_(idx_remove)( join, key, MAP_(private_idx_null)(), pool );
    1094     7767903 :   return fd_ptr_if( !MAP_(private_idx_is_null)( ele_idx ), (MAP_ELE_T       *)( (ulong)pool + (ele_idx * sizeof(MAP_ELE_T)) ), sentinel );
    1095     7767903 : }
    1096             : 
    1097             : FD_FN_PURE static inline MAP_ELE_T *
    1098             : MAP_(ele_query)( MAP_(t) *         join,
    1099             :                  MAP_KEY_T const * key,
    1100             :                  MAP_ELE_T *       sentinel,
    1101  1205708700 :                  MAP_ELE_T *       pool ) {
    1102  1205708700 :   ulong ele_idx = MAP_(idx_query)( join, key, MAP_(private_idx_null)(), pool );
    1103  1205708700 :   return fd_ptr_if( !MAP_(private_idx_is_null)( ele_idx ), (MAP_ELE_T       *)( (ulong)pool + (ele_idx * sizeof(MAP_ELE_T)) ), sentinel );
    1104  1205708700 : }
    1105             : 
    1106             : FD_FN_PURE static inline MAP_ELE_T const *
    1107             : MAP_(ele_query_const)( MAP_(t) const *   join,
    1108             :                        MAP_KEY_T const * key,
    1109             :                        MAP_ELE_T const * sentinel,
    1110   789512076 :                        MAP_ELE_T const * pool ) {
    1111   789512076 :   ulong ele_idx = MAP_(idx_query_const)( join, key, MAP_(private_idx_null)(), pool );
    1112   789512076 :   return fd_ptr_if( !MAP_(private_idx_is_null)( ele_idx ), (MAP_ELE_T const *)( (ulong)pool + (ele_idx * sizeof(MAP_ELE_T)) ), sentinel );
    1113   789512076 : }
    1114             : 
    1115             : #if MAP_MULTI!=0
    1116             : 
    1117             : FD_FN_PURE static inline MAP_ELE_T const *        // Found element on success (will be from pool), sentinel on failure
    1118             : MAP_(ele_next_const)( MAP_ELE_T const * prev,     // Previous result of mymap_ele_query_const
    1119             :                       MAP_ELE_T const * sentinel, // Value to return if key not in map
    1120   259444443 :                       MAP_ELE_T const * pool ) {  // Current local join to element storage
    1121   259444443 :          ulong ele_idx = MAP_(idx_next_const)( (ulong)(prev-pool), MAP_(private_idx_null)(), pool );
    1122   259444443 :   return fd_ptr_if( !MAP_(private_idx_is_null)( ele_idx ), pool + ele_idx, sentinel );
    1123   259444443 : }
    1124             : 
    1125             : #endif /* MAP_MULTI!=0 */
    1126             : 
    1127             : FD_PROTOTYPES_END
    1128             : 
    1129             : #endif
    1130             : 
    1131             : #undef MAP_
    1132             : 
    1133             : #undef MAP_IMPL_STYLE
    1134             : #undef MAP_MAGIC
    1135             : #undef MAP_KEY_HASH
    1136             : #undef MAP_KEY_EQ
    1137             : #undef MAP_NEXT
    1138             : #undef MAP_PREV
    1139             : #undef MAP_IDX_T
    1140             : #undef MAP_KEY
    1141             : #undef MAP_KEY_T
    1142             : #undef MAP_ELE_T
    1143             : #undef MAP_NAME
    1144             : #undef MAP_MULTI
    1145             : #undef MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL
    1146             : #undef MAP_INSERT_FENCE
    1147             : #undef MAP_COUNT

Generated by: LCOV version 1.14