LCOV - code coverage report
Current view: top level - util/tmpl - fd_map_dynamic.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 122 126 96.8 %
Date: 2024-11-13 11:58:15 Functions: 120 575 20.9 %

          Line data    Source code
       1             : /* Declare ultra high performance dynamic key-val maps of bounded
       2             :    run time size.  Typical usage:
       3             : 
       4             :      struct mymap {
       5             :        ulong key;  // Technically "MAP_KEY_T  MAP_KEY;"  (default is ulong key)
       6             :        uint  hash; // Technically "MAP_HASH_T MAP_HASH;" (default is uint  hash), ==mymap_hash(key)
       7             :        ... key and hash can be located arbitrarily in struct
       8             :        ... hash is not required if MAP_MEMOIZE is zero
       9             :        ... the rest of the struct is POD state/values associated with key
      10             :        ... the mapping of a key to a map slot is arbitrary and might
      11             :        ... change over the lifetime of the key
      12             :      };
      13             : 
      14             :      typedef struct mymap mymap_t;
      15             : 
      16             :      #define MAP_NAME mymap
      17             :      #define MAP_T    mymap_t
      18             :      #include "util/tmpl/fd_map_dynamic.c"
      19             : 
      20             :   will declare the following static inline APIs as a header only style
      21             :   library in the compilation unit:
      22             : 
      23             :     // align/footprint - Return the alignment/footprint required for a
      24             :     // memory region to be used as mymap sufficient to hold up to
      25             :     // (2^lg_slot_cnt)-1 keys.  Assumes non-negative lg_slot_cnt.
      26             :     //
      27             :     // new - Format a memory region pointed to by shmem into a mymap.
      28             :     // Assumes shmem points to a region with the required alignment and
      29             :     // footprint not in use by anything else.  Caller is not joined on
      30             :     // return.  Returns shmem.
      31             :     //
      32             :     // join - Join a mymap.  Assumes shmap points at a region formatted
      33             :     // as a mymap.  Returns a handle of the callers join (will be
      34             :     // pointer to an array indexed [0,2^lg_slot_cnt) of mymap_t slots).
      35             :     // THIS IS NOT JUST A SIMPLE CAST OF SHMAP.
      36             :     //
      37             :     // leave - Leave a mymap.  Assumes mymap points to a current join.
      38             :     // Returns a pointer to the shared memory region the join.  THIS IS
      39             :     // NOT JUST A SIMPLE CAST OF MAP.
      40             :     //
      41             :     // delete - Unformat a memory region used as a mymap.  Assumes
      42             :     // shmymap points to a formatted region with no current joins.
      43             :     // Returns a pointer to the unformatted memory region.
      44             : 
      45             :     ulong     mymap_align    ( void                             );
      46             :     ulong     mymap_footprint( int lg_slot_cnt                  );
      47             :     void *    mymap_new      ( void *    shmem, int lg_slot_cnt );
      48             :     mymap_t * mymap_join     ( void *    shmap                  ); // Indexed [0,2^lg_slot_cnt)
      49             :     void *    mymap_leave    ( mymap_t * map                    );
      50             :     void *    mymap_delete   ( void *    shmap                  );
      51             : 
      52             :     // Return the current/maximum number of keys that can be inserted
      53             :     // into a mymap.  The mymap will become increasingly inefficient the
      54             :     // more keys there are.  Recommend not using more than around half
      55             :     // this value.
      56             : 
      57             :     ulong mymap_key_cnt( mymap_t const * map ); // In [0,key_max]
      58             :     ulong mymap_key_max( mymap_t const * map ); // == 2^lg_slot_cnt - 1
      59             : 
      60             :     // Return the log2 number of slots / number of slots in a mymap.
      61             :     // This is to facilitate iterating / listing all contents of a mymap
      62             :     // (this process is not algorithmically ideal for a sparse mymap).
      63             :     // E.g.  mymap[slot_idx].key for slot_idx in [0,mymap_slot_cnt()]
      64             :     // when key!=0 is the set of all current key-vals in the mymap.
      65             : 
      66             :     int   mymap_lg_slot_cnt( mymap_t const * map ); // Non-negative
      67             :     ulong mymap_slot_cnt   ( mymap_t const * map ); // == 2^lg_slot_cnt
      68             : 
      69             :     // Returns the index of the slot (allows communicating locations of
      70             :     // map entries between users of mymap in different address spaces).
      71             :     // Might imply a division (probably via magic multiply) if
      72             :     // sizeof(mymap_t) is not a power of two (as such, power-of-2
      73             :     // sizeof(mymap_t) have good Feng Shui).  Assumes that mymap is a
      74             :     // current join and slot points to a slot in that join.
      75             : 
      76             :     ulong mymap_slot_idx( mymap_t const * map, mymap_t const * slot );
      77             : 
      78             :     // Returns the "null" key, which is the canonical key that will
      79             :     // never be inserted (typically zero).
      80             : 
      81             :     ulong mymap_key_null( void ); // == MAP_KEY_NULL
      82             : 
      83             :     // Return the 1/0 if key is a key that will never/might be inserted.
      84             : 
      85             :     int mymap_key_inval( ulong key );
      86             : 
      87             :     // Return the 1/0 if k0 and k1 are keys that are the same
      88             : 
      89             :     int mymap_key_equal( ulong k0, ulong k1 );
      90             : 
      91             :     // Return the hash of key used by the map.  Should ideally be a
      92             :     // random mapping from MAP_KEY_T -> MAP_HASH_T.
      93             : 
      94             :     uint mymap_key_hash( ulong key );
      95             : 
      96             :     // Insert key into the map, fast O(1).  Returns a pointer to the map
      97             :     // entry with key on success and NULL on failure (i.e. key is
      98             :     // already in the map or there are too many keys in the map to
      99             :     // insert this key).  The returned pointer lifetime is until _any_
     100             :     // map remove or map leave.  The caller should not change the values
     101             :     // in map_key or map_hash but is free to modify other fields in the
     102             :     // entry on return.  Assumes map is a current join and key is value
     103             :     // that can be inserted.
     104             : 
     105             :     mymap_t * mymap_insert( mymap_t * map, ulong key );
     106             : 
     107             :     // Remove entry from map, fast O(1).  Assumes map is a current join
     108             :     // and that entry points to a full entry currently in the map.
     109             :     // Removal performance very slightly more optimal if sizeof(mymap_t)
     110             :     // is a power of two.
     111             : 
     112             :     void mymap_remove( mymap_t * map, mymap_t * entry );
     113             : 
     114             :     // Remove all entries from the map. O(map size).
     115             : 
     116             :     void mymap_clear( mymap_t * map );
     117             : 
     118             :     // Query map for key, fast O(1).  Returns a pointer to the map slot
     119             :     // holding key or null if key not in map.  The returned pointer
     120             :     // lifetime is until the next map remove or map leave.  The caller
     121             :     // should not change key or hash but is free to modify other fields
     122             :     // in the entry.  Assumes map is a current join and that key is
     123             :     // non-zero.
     124             : 
     125             :     mymap_t * mymap_query( mymap_t * map, ulong key, mymap_t * null );
     126             : 
     127             :   You can do this as often as you like in a compilation unit to get
     128             :   different types of maps.  Since it is all static inline, it is fine
     129             :   to do this in a header too.  Further, options exist to use different
     130             :   hashing functions, variable length keys, etc as detailed below.
     131             : 
     132             :   For use with non-POD C++ structs, map_new assumes that a slot can be
     133             :   initialized to empty by assigning its key to MAP_KEY_NULL.  Map insert
     134             :   will use MAP_KEY_MOVE to move the user provided key into the slot key
     135             :   value.  Map remove will MAP_MOVE slots as necessary to preserve their
     136             :   probe sequences and assumes the user has already cleaned up the entry
     137             :   to remove enough so that all that needs to be done to eliminate the
     138             :   entry from the map is assign the entry's key to MAP_KEY_NULL.
     139             : 
     140             :     mymap_t * slot = mymap_insert( map, cxx_key );
     141             :     if( FD_UNLIKELY( !slot ) ) ... handle failure (cxx_key was not moved)
     142             :     else {
     143             :       ... mymap_insert did a MAP_KEY_MOVE of cxx_key into slot->key
     144             :       ... clean cxx_key's shell here as necessary here
     145             :       ... initialize other slot fields as necessary
     146             :     }
     147             : 
     148             :   and on removal:
     149             : 
     150             :     ... clean up other slot fields as necessary
     151             :     ... clean up slot->key as necessary
     152             :     mymap_remove( map, slot );
     153             :     ... the mapping of keys to map slots might have been changed by the
     154             :     ... mymap_remove.  Any motion of slots was done via:
     155             :     ... MAP_MOVE( dst_slot, src_slot ) */
     156             : 
     157             : #include "../bits/fd_bits.h"
     158             : #include <stddef.h>
     159             : 
     160             : #ifndef offsetof
     161             : #  define offsetof(TYPE,MEMB) ((ulong)((TYPE*)0)->MEMB)
     162             : #endif
     163             : 
     164             : #ifndef MAP_NAME
     165             : #error "Define MAP_NAME"
     166             : #endif
     167             : 
     168             : /* A MAP_T should be something something reasonable to shallow copy with
     169             :    the fields described above. */
     170             : 
     171             : #ifndef MAP_T
     172             : #error "Define MAP_T struct"
     173             : #endif
     174             : 
     175             : /* MAP_HASH_T should be an unsigned integral type. */
     176             : 
     177             : #ifndef MAP_HASH_T
     178   735349207 : #define MAP_HASH_T uint
     179             : #endif
     180             : 
     181             : /* MAP_HASH is the MAP_T hash field name.  Defaults to hash. */
     182             : 
     183             : #ifndef MAP_HASH
     184    49526535 : #define MAP_HASH hash
     185             : #endif
     186             : 
     187             : /* MAP_KEY_T should be something reasonable to pass to a static inline
     188             :    by value, assign to MAP_KEY_NULL, compare for equality and copy.
     189             :    E.g. a uint, ulong, __m128i, etc. */
     190             : 
     191             : #ifndef MAP_KEY_T
     192   671218188 : #define MAP_KEY_T ulong
     193             : #else
     194             : #if !defined(MAP_KEY_NULL) || !defined(MAP_KEY_INVAL) || !defined(MAP_KEY_EQUAL) || !defined(MAP_KEY_EQUAL_IS_SLOW) || !defined(MAP_KEY_HASH)
     195             : #error "Define MAP_KEY_NULL, MAP_KEY_INVAL, MAP_KEY_EQUAL, MAP_KEY_EQUAL_IS_SLOW, and MAP_KEY_HASH if using a custom MAP_KEY_T"
     196             : #endif
     197             : #endif
     198             : 
     199             : /* MAP_KEY is the MAP_T key field name.  Defaults to key. */
     200             : 
     201             : #ifndef MAP_KEY
     202  5500009683 : #define MAP_KEY key
     203             : #endif
     204             : 
     205             : /* MAP_KEY_NULL is a key that will never be inserted. */
     206             : 
     207             : #ifndef MAP_KEY_NULL
     208     4068426 : #define MAP_KEY_NULL 0UL
     209             : #endif
     210             : 
     211             : /* MAP_KEY_INVAL returns 1 if k0 is key that will never be inserted
     212             :    and zero otherwise.  Note that MAP_KEY_INVAL( MAP_KEY_NULL ) should
     213             :    be true.  This should be generally fast. */
     214             : 
     215             : #ifndef MAP_KEY_INVAL
     216   433828994 : #define MAP_KEY_INVAL(k) !(k)
     217             : #endif
     218             : 
     219             : /* MAP_KEY_EQUAL returns 0/1 if k0 is the same/different */
     220             : 
     221             : #ifndef MAP_KEY_EQUAL
     222   616381986 : #define MAP_KEY_EQUAL(k0,k1) (k0)==(k1)
     223             : #endif
     224             : 
     225             : /* If MAP_KEY_EQUAL_IS_SLOW is slow (e.g. variable length string
     226             :    compare, large buffer compares, etc), set MAP_KEY_EQUAL_IS_SLOW to
     227             :    non-zero.  Then, if MAP_MEMOIZE (below) is set, precomputed key hashes
     228             :    will be used accelerate key insert and key query. */
     229             : 
     230             : #ifndef MAP_KEY_EQUAL_IS_SLOW
     231             : #define MAP_KEY_EQUAL_IS_SLOW 0
     232             : #endif
     233             : 
     234             : /* MAP_KEY_HASH takes a key and maps it into MAP_HASH_T uniform pseudo
     235             :    randomly. */
     236             : 
     237             : #ifndef MAP_KEY_HASH
     238   281603769 : #define MAP_KEY_HASH(key) ((MAP_HASH_T)fd_ulong_hash( key ))
     239             : #endif
     240             : 
     241             : /* MAP_KEY_MOVE moves the contents from src to dst.  Non-POD key types
     242             :    need to customize this accordingly (and handle the case of
     243             :    ks==MAP_KEY_NULL).  Defaults to shallow copy. */
     244             : 
     245             : #ifndef MAP_KEY_MOVE
     246   319770248 : #define MAP_KEY_MOVE(kd,ks) (kd)=(ks)
     247             : #endif
     248             : 
     249             : /* MAP_MOVE moves the contents of a MAP_T from src to dst.  Non-POD key
     250             :    types need to customize this accordingly.  Defaults to shallow copy. */
     251             : 
     252             : #ifndef MAP_MOVE
     253     4616882 : #define MAP_MOVE(d,s) (d)=(s)
     254             : #endif
     255             : 
     256             : /* If MAP_MEMOIZE is defined to non-zero, the MAP_T requires a
     257             :    "map_hash" field that will hold the value of the MAP_KEY_HASH of the
     258             :    MAP_T's MAP_KEY when the map slot is not empty (undefined otherwise).
     259             :    This is useful for accelerating user operations that might need a
     260             :    hash of the key and for accelerating remove operations.  It is also
     261             :    potentially useful as a way to accelerate slow key comparison
     262             :    operations (see MAP_KEY_EQUAL_IS_SLOW). */
     263             : 
     264             : #ifndef MAP_MEMOIZE
     265             : #define MAP_MEMOIZE 1
     266             : #endif
     267             : 
     268             : /* MAP_QUERY_OPT allows the user to specify how the map query function
     269             :    should be optimized.
     270             :      0 -> optimize for low fill ratio
     271             :      1 -> optimize for low fill ratio and extremely rare query failure
     272             :      2 -> optimize for low fill ratio and extremely rare query success */
     273             : 
     274             : #ifndef MAP_QUERY_OPT
     275             : #define MAP_QUERY_OPT 0
     276             : #endif
     277             : 
     278             : /* Implementation *****************************************************/
     279             : 
     280  9740126935 : #define MAP_(n) FD_EXPAND_THEN_CONCAT3(MAP_NAME,_,n)
     281             : 
     282             : struct MAP_(private) {
     283             :   ulong key_cnt;     /* == number of keys currently in map */
     284             :   ulong slot_mask;   /* == key_max == 2^lg_slot_cnt - 1 */
     285             :   int   lg_slot_cnt; /* non-negative */
     286             :   MAP_T slot[1];     /* Actually 2^lg_slot_cnt in size */
     287             : };
     288             : 
     289             : typedef struct MAP_(private) MAP_(private_t);
     290             : 
     291             : FD_PROTOTYPES_BEGIN
     292             : 
     293             : /* Private APIs *******************************************************/
     294             : 
     295             : /* private_from_slot return a pointer to the map_private given a pointer
     296             :    to the map's slot.  private_from_map_const also provided for
     297             :    const-correctness purposes. */
     298             : 
     299             : FD_FN_CONST static inline MAP_(private_t) *
     300   808240877 : MAP_(private_from_slot)( MAP_T * slot ) {
     301   808240877 :   ulong slot_ofs = offsetof( MAP_(private_t), slot );
     302   808240877 :   return (MAP_(private_t) *)( (ulong)slot - (ulong)slot_ofs );
     303   808240877 : }
     304             : 
     305             : FD_FN_CONST static inline MAP_(private_t) const *
     306      627813 : MAP_(private_from_slot_const)( MAP_T const * slot ) {
     307      627813 :   ulong slot_ofs = offsetof( MAP_(private_t), slot );
     308      627813 :   return (MAP_(private_t) const *)( (ulong)slot - (ulong)slot_ofs );
     309      627813 : }
     310             : 
     311             : /* Get the linear probing starting slot for a key and the slot to probe
     312             :    after a given slot */
     313             : 
     314   735349207 : FD_FN_CONST static inline ulong MAP_(private_start)( MAP_HASH_T hash, ulong slot_mask ) { return (ulong)(hash & (MAP_HASH_T)slot_mask); }
     315  3431320385 : FD_FN_CONST static inline ulong MAP_(private_next) ( ulong      slot, ulong slot_mask ) { return (++slot) & slot_mask; }
     316             : 
     317             : /* Public APIS ********************************************************/
     318             : 
     319           0 : FD_FN_CONST static inline ulong MAP_(align)( void ) { return alignof(MAP_(private_t)); }
     320             : 
     321             : FD_FN_CONST static inline ulong
     322      777294 : MAP_(footprint)( int lg_slot_cnt ) {
     323      777294 :   ulong slot_cnt = 1UL << lg_slot_cnt;
     324      777294 :   return fd_ulong_align_up( fd_ulong_align_up( 24UL, alignof(MAP_T) ) + sizeof(MAP_T)*slot_cnt, alignof(MAP_(private_t)) );
     325      777294 : }
     326             : 
     327             : static inline void *
     328             : MAP_(new)( void *  shmem,
     329      634743 :            int     lg_slot_cnt ) {
     330      634743 :   ulong slot_cnt  = 1UL<<lg_slot_cnt;
     331      634743 :   ulong slot_mask = slot_cnt - 1UL;
     332      634743 :   MAP_(private_t) * map = (MAP_(private_t) *)shmem;
     333             : 
     334      634743 :   map->key_cnt     = 0UL;
     335      634743 :   map->slot_mask   = slot_mask;
     336      634743 :   map->lg_slot_cnt = lg_slot_cnt;
     337             : 
     338      634743 :   MAP_T * slot = map->slot; FD_COMPILER_FORGET( slot );
     339             : 
     340  1935944049 :   for( ulong slot_idx=0UL; slot_idx<slot_cnt; slot_idx++ )
     341  1935309306 :     slot[ slot_idx ].MAP_KEY = (MAP_KEY_NULL);
     342             : 
     343      634743 :   return map;
     344      634743 : }
     345             : 
     346             : static inline MAP_T *
     347      634743 : MAP_(join)( void * shmap ) {
     348      634743 :   MAP_(private_t) * map = (MAP_(private_t) *)shmap;
     349      634743 :   MAP_T * slot = map->slot; FD_COMPILER_FORGET( slot );
     350      634743 :   return slot;
     351      634743 : }
     352             : 
     353      316182 : static inline void * MAP_(leave) ( MAP_T * slot  ) { return (void *)MAP_(private_from_slot)( slot ); }
     354      316182 : static inline void * MAP_(delete)( void *  shmap ) { return shmap; }
     355             : 
     356      627804 : FD_FN_PURE static inline ulong MAP_(key_cnt)    ( MAP_T const * slot ) { return MAP_(private_from_slot_const)( slot )->key_cnt;       }
     357           3 : FD_FN_PURE static inline ulong MAP_(key_max)    ( MAP_T const * slot ) { return MAP_(private_from_slot_const)( slot )->slot_mask;     }
     358           3 : FD_FN_PURE static inline int   MAP_(lg_slot_cnt)( MAP_T const * slot ) { return MAP_(private_from_slot_const)( slot )->lg_slot_cnt;   }
     359           3 : FD_FN_PURE static inline ulong MAP_(slot_cnt)   ( MAP_T const * slot ) { return MAP_(private_from_slot_const)( slot )->slot_mask+1UL; }
     360             : 
     361    84717637 : FD_FN_CONST static inline ulong MAP_(slot_idx)( MAP_T const * map, MAP_T const * entry ) { return (ulong)(entry - map); }
     362             : 
     363           0 : FD_FN_CONST static inline MAP_KEY_T MAP_(key_null)( void ) { return (MAP_KEY_NULL); }
     364             : 
     365             : /* These are FD_FN_PURE instead of FD_FN_CONST in case a non-POD
     366             :    MAP_KEY_T.  FIXME: CONSIDER LETTING THE COMPILER SORT THIS OUT? */
     367             : 
     368  4051879544 : FD_FN_PURE static inline int MAP_(key_inval)( MAP_KEY_T k0               ) { return (MAP_KEY_INVAL(k0)); }
     369  3777841509 : FD_FN_PURE static inline int MAP_(key_equal)( MAP_KEY_T k0, MAP_KEY_T k1 ) { return (MAP_KEY_EQUAL(k0,k1)); }
     370             : 
     371   728131281 : FD_FN_PURE static inline MAP_HASH_T MAP_(key_hash)( MAP_KEY_T key ) { return (MAP_KEY_HASH(key)); }
     372             : 
     373             : FD_FN_UNUSED static MAP_T * /* Work around -Winline */
     374             : MAP_(insert)( MAP_T *   map,
     375   319923548 :               MAP_KEY_T key ) {
     376   319923548 :   MAP_(private_t) * hdr = MAP_(private_from_slot)( map );
     377             : 
     378   319923548 :   ulong key_cnt   = hdr->key_cnt;
     379   319923548 :   ulong slot_mask = hdr->slot_mask; /* == key_max (FIXME: MAKE KEY_MAX DISTINCT FROM SLOT_MASK?) */
     380   319923548 :   if( FD_UNLIKELY( key_cnt >= slot_mask ) ) return NULL;
     381             : 
     382   319923248 :   MAP_HASH_T hash = MAP_(key_hash)( key );
     383   319923248 :   ulong      slot = MAP_(private_start)( hash, slot_mask );
     384   319923248 :   MAP_T * m;
     385  2191302491 :   for(;;) {
     386  2191302491 :     m = map + slot;
     387  2191302491 :     MAP_KEY_T map_key = m->MAP_KEY;
     388  2191302491 :     if( FD_LIKELY( MAP_(key_inval)( map_key ) ) ) break; /* Optimize for not found */
     389             : #   if MAP_MEMOIZE && MAP_KEY_EQUAL_IS_SLOW              /* ... and then for searching */
     390           0 :     if( FD_UNLIKELY( m->MAP_HASH==hash && MAP_(key_equal)( map_key, key ) ) ) return NULL;
     391             : #   else
     392  1871532243 :     if( FD_UNLIKELY( MAP_(key_equal)( map_key, key ) ) ) return NULL;
     393  1871379243 : #   endif
     394  1871379243 :     slot = MAP_(private_next)( slot, slot_mask );
     395  1871379243 :   }
     396   319770248 :   MAP_KEY_MOVE( m->MAP_KEY, key );
     397             : # if MAP_MEMOIZE
     398    42308609 :   m->MAP_HASH = hash;
     399             : # endif
     400   319770248 :   hdr->key_cnt = key_cnt + 1UL;
     401   319770248 :   return m;
     402   319923248 : }
     403             : 
     404             : static inline void
     405             : MAP_(remove)( MAP_T * map,
     406    84716101 :               MAP_T * entry ) {
     407    84716101 :   MAP_(private_t) * hdr = MAP_(private_from_slot)( map );
     408             : 
     409             :   /* FIXME: CONSIDER VALIDATING KEY_CNT AND/OR ENTRY ISN'T VALID */
     410    84716101 :   hdr->key_cnt--;
     411             : 
     412    84716101 :   ulong slot_mask = hdr->slot_mask;
     413    84716101 :   ulong slot      = MAP_(slot_idx)( map, entry );
     414    89332989 :   for(;;) {
     415             : 
     416             :     /* Make a hole at slot */
     417             : 
     418    89332989 :     map[slot].MAP_KEY = (MAP_KEY_NULL);
     419    89332989 :     ulong hole = slot;
     420             : 
     421             :     /* The creation of a hole at slot might have disrupted the probe
     422             :        sequence involving the keys in any contiguously occupied map
     423             :        entry after slot. */
     424             : 
     425    96859660 :     for(;;) {
     426    96859660 :       slot = MAP_(private_next)( slot, slot_mask );
     427             : 
     428             :       /* At this point, map entries (hole,slot) (cyclic) are occupied
     429             :          and the probe sequence for these has been confirmed to be
     430             :          intact.  If slot is empty, then all probe sequences are intact. */
     431             : 
     432    96859660 :       MAP_KEY_T key = map[slot].MAP_KEY;
     433    96859660 :       if( MAP_(key_inval)(key) ) return;
     434             : 
     435             :       /* slot is occupied.  If a probe looking for the key at slot does
     436             :          not start its scan in (hole,slot] (cyclic), its scan will fail
     437             :          erroneously due to the hole just made.  In this case, we move
     438             :          slot to hole to restore the probe sequence for the key at slot
     439             :          and then make a new hole at slot.  As the new hole could break
     440             :          the other probe sequences, we start over on the new hole. */
     441             : 
     442             : #     if MAP_MEMOIZE
     443     7217926 :       MAP_HASH_T hash = map[slot].MAP_HASH;
     444             : #     else
     445     4925633 :       MAP_HASH_T hash = MAP_(key_hash)( key );
     446             : #     endif
     447    12143559 :       ulong start = MAP_(private_start)( hash, slot_mask );
     448    12143559 :       if( !(((hole<start) & (start<=slot)) | ((hole>slot) & ((hole<start) | (start<=slot)))) ) break;
     449     4925633 :     }
     450             : 
     451     4616888 :     MAP_MOVE( map[hole], map[slot] );
     452     4616888 :   }
     453             :   /* never get here */
     454    84716101 : }
     455             : 
     456             : static inline void
     457        2646 : MAP_(clear)( MAP_T * map ) {
     458        2646 :   MAP_(private_t) * hdr = MAP_(private_from_slot)( map );
     459        2646 :   hdr->key_cnt = 0UL;
     460        2646 :   ulong slot_cnt  = 1UL<<hdr->lg_slot_cnt;
     461        2646 :   MAP_T * slot = hdr->slot;
     462    44835414 :   for( ulong slot_idx=0UL; slot_idx<slot_cnt; slot_idx++ )
     463    44832768 :     slot[ slot_idx ].MAP_KEY = (MAP_KEY_NULL);
     464        2646 : }
     465             : 
     466             : FD_FN_PURE FD_FN_UNUSED static MAP_T * /* Work around -Winline */
     467             : MAP_(query)( MAP_T *   map,
     468             :              MAP_KEY_T key,
     469   403282400 :              MAP_T *   null ) {
     470   403282400 :   ulong      slot_mask = MAP_(private_from_slot)( map )->slot_mask;
     471   403282400 :   MAP_HASH_T hash      = MAP_(key_hash)( key );
     472   403282400 :   ulong      slot      = MAP_(private_start)( hash, slot_mask );
     473   403282400 :   MAP_T * m;
     474  1866363882 :   for(;;) {
     475  1866363882 :     m = map + slot;
     476  1866363882 :     MAP_KEY_T map_key = m->MAP_KEY;
     477             : 
     478             : #   if MAP_MEMOIZE && MAP_KEY_EQUAL_IS_SLOW
     479           0 : #   define MAP_IMPL_QUERY_FOUND (hash==m->MAP_HASH && MAP_(key_equal)( map_key, key ))
     480             : #   else
     481  1724484915 : #   define MAP_IMPL_QUERY_FOUND MAP_(key_equal)( map_key, key )
     482             : #   endif
     483             : 
     484             : #   if MAP_QUERY_OPT==0
     485  1724484915 :     int found = MAP_IMPL_QUERY_FOUND;
     486  1724484915 :     int empty = MAP_(key_inval)( map_key );
     487             :     int done  = found | empty;
     488  1724484915 :     if( empty ) m = null; /* cmov */
     489  1724484915 :     FD_COMPILER_FORGET( done );
     490  1724484915 :     if( FD_LIKELY( done ) ) break;
     491             : #   elif MAP_QUERY_OPT==1
     492   141878967 :     if( FD_LIKELY( MAP_IMPL_QUERY_FOUND       ) ) break;
     493    39227432 :     if( FD_LIKELY( MAP_(key_inval)( map_key ) ) ) return null;
     494             : #   else
     495             :     if( FD_LIKELY( MAP_(key_inval)( map_key ) ) ) return null;
     496             :     if( FD_LIKELY( MAP_IMPL_QUERY_FOUND       ) ) break;
     497             : #   endif
     498             : 
     499     5444749 : #   undef MAP_IMPL_QUERY_FOUND
     500             : 
     501  1463081482 :     slot = MAP_(private_next)( slot, slot_mask );
     502  1463081482 :   }
     503   102651535 :   return m;
     504   403282400 : }
     505             : 
     506             : FD_PROTOTYPES_END
     507             : 
     508             : #undef MAP_SLOT_MASK
     509             : #undef MAP_SLOT_CNT
     510             : #undef MAP_
     511             : 
     512             : /* End implementation *************************************************/
     513             : 
     514             : #undef MAP_QUERY_OPT
     515             : #undef MAP_MEMOIZE
     516             : #undef MAP_MOVE
     517             : #undef MAP_KEY_MOVE
     518             : #undef MAP_KEY_HASH
     519             : #undef MAP_KEY_EQUAL_IS_SLOW
     520             : #undef MAP_KEY_EQUAL
     521             : #undef MAP_KEY_INVAL
     522             : #undef MAP_KEY_NULL
     523             : #undef MAP_KEY
     524             : #undef MAP_KEY_T
     525             : #undef MAP_HASH
     526             : #undef MAP_HASH_T
     527             : #undef MAP_T
     528             : #undef MAP_NAME

Generated by: LCOV version 1.14