LCOV - code coverage report
Current view: top level - util/tmpl - fd_heap.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 195 204 95.6 %
Date: 2024-11-13 11:58:15 Functions: 24 56 42.9 %

          Line data    Source code
       1             : /* Generate prototypes, inlines and implementations for ultra high
       2             :    performance zero copy heaps for use in situations where the heap
       3             :    elements are not stored sequentially in memory (textbook heap
       4             :    algorithms, including prq, assume the heap elements will be stored in
       5             :    an array such that the root, parent, left/right children and next
       6             :    heap element to use are all implied by the element cnt and array
       7             :    indices ... also worth noting that this API does assume that heap
       8             :    elements themselves are embedded in a linear addressable storage
       9             :    region such as a fd_pool for persistence and relocatability though).
      10             : 
      11             :    This API is designed for ultra tight coupling with pools, maps, other
      12             :    heaps, etc.  Likewise, a heap can be persisted beyond the lifetime of
      13             :    creating process, used concurrently in many common operations, used
      14             :    inter-process, relocated in memory, naively serialized/deserialized,
      15             :    moved between hosts, supports index compression for cache and memory
      16             :    bandwidth efficiency, etc.
      17             : 
      18             :    Typical usage:
      19             : 
      20             :      struct myele {
      21             : 
      22             :        ... Each field below can be located arbitrarily in the struct
      23             : 
      24             :        ulong left;  // Technically "HEAP_IDX_T HEAP_LEFT;" (default is ulong left), similarly for right.
      25             :        ulong right; // These are managed by the heap when a myele is in the heap.
      26             : 
      27             :        ... Note that other kinds of objects can use these fields for
      28             :        ... their metadata needs to keep element metadata / cache
      29             :        ... footprint overheads minimal.  The only restriction is that
      30             :        ... they cannot concurrently use the same field.
      31             : 
      32             :        ... Note that fields could be in an anonymous union and/or made
      33             :        ... into narrow bit fields if useful for additional layout,
      34             :        ... memory, bandwidth and cache efficiency.
      35             : 
      36             :        ... Arbitrary application fields mixed in here.  Power-of-2
      37             :        ... element sizes have good cache and indexing Feng Shui.
      38             : 
      39             :        char key[ KEY_MAX ]; // For demonstration purposes
      40             : 
      41             :      };
      42             : 
      43             :      typedef struct myele myele_t;
      44             : 
      45             :      #define HEAP_NAME      myheap
      46             :      #define HEAP_T         myele_t
      47             :      #define HEAP_LT(e0,e1) (strcmp( e0->key, e1->key )<0)
      48             :      #include "fd_heap.c"
      49             : 
      50             :    will declare the following APIs as a header-only style library in the
      51             :    compilation unit:
      52             : 
      53             :      int myheap_lt( myele_t const * e0, myele_t const * e1 ); // Provides HEAP_LT
      54             : 
      55             :      // myheap_idx_null returns the element index used to represent
      56             :      // NULL, infinite lifetime.  myheap_ele_null returns NULL, infinite
      57             :      // lifetime, for completeness, myheap_ele_null_const is a
      58             :      // const-correct version, also for completeness.
      59             : 
      60             :      ulong           myheap_idx_null      ( void );
      61             :      myele_t *       myheap_ele_null      ( void );
      62             :      myele_t const * myheap_ele_null_const( void );
      63             : 
      64             :      // myheap_{idx,ele}_is_null returns i==myheap_idx_null() / !e
      65             : 
      66             :      int myheap_idx_is_null( ulong           i );
      67             :      int myheap_ele_is_null( myele_t const * e );
      68             : 
      69             :      // myheap_idx returns e's index.  Assumes e is a pointer in the
      70             :      // caller's local address space to a pool element or is NULL.
      71             :      // Return will be in [0,ele_max) or myheap_idx_null().  Lifetime is
      72             :      // the element storage lifetime.  myheap_idx_fast is the same but
      73             :      // assumes e is not NULL.  pool is a pointer in the caller's
      74             :      // address space to the ele_max linearly addressable storage region
      75             :      // backing the heap.
      76             : 
      77             :      ulong myheap_idx     ( myele_t const * e, myele_t const * pool );
      78             :      ulong myheap_idx_fast( myele_t const * e, myele_t const * pool );
      79             : 
      80             :      // myheap_ele returns a pointer in the caller's address space to
      81             :      // element idx.  Assumes idx is in [0,ele_max) or is
      82             :      // myheap_idx_null().  Return pointer lifetime is ele's local
      83             :      // lifetime.  myheap_ele_fast is the same but assumes idx is not
      84             :      // myheap_idx_null().  myheap_ele[_fast]_const is a const correct
      85             :      // version.  pool is a pointer in the caller's address space to the
      86             :      // ele_max linearly addressable storage region backing the heap.
      87             : 
      88             :      myele_t * myheap_ele     ( ulong i, myele_t * pool );
      89             :      myele_t * myheap_ele_fast( ulong i, myele_t * pool );
      90             : 
      91             :      myele_t const * myheap_ele_const     ( ulong i, myele_t const * pool );
      92             :      myele_t const * myheap_ele_fast_const( ulong i, myele_t const * pool );
      93             : 
      94             :      // myheap_{align,footprint} returns the alignment and footprint
      95             :      // needed for a memory region to hold the state of a myheap of
      96             :      // elements from an ele_max element storage.  align will be an
      97             :      // integer power-of-two and footprint will be a multiple of align.
      98             :      // footprint will non-zero on a success and 0 on failure (silent)
      99             :      // (e.g. ele_max too large for the specified HEAP_IDX_T).  myheap_t
     100             :      // is stack declaration, data segment declaration, heap allocation
     101             :      // and stack allocation friendly.  Even though footprint is passed
     102             :      // ele_max, the footprint is a small O(1) spatial overhead.
     103             :      //
     104             :      // myheap_new formats a memory region with the appropriate
     105             :      // alignment and footprint whose first byte in the caller's address
     106             :      // space is pointed to by shmem as a myheap for elements from an
     107             :      // ele_max element storage.  Returns shmem on success and NULL on
     108             :      // failure (log details, e.g. ele_max is too large for the width of
     109             :      // the HEAP_IDX_T specified).  Caller is not joined on return.  The
     110             :      // heap will be empty.
     111             :      //
     112             :      // myheap_join joins a myheap.  Assumes shheap points at a memory
     113             :      // region formatted as a myheap in the caller's address space.
     114             :      // Returns a handle to the caller's local join on success and NULL
     115             :      // on failure (logs details).
     116             :      //
     117             :      // myheap_leave leaves a myheap.  Assumes join points to a current
     118             :      // local join.  Returns shheap used on join and NULL on failure
     119             :      // (logs details).
     120             :      //
     121             :      // myheap_delete unformats a memory region used as a myheap.
     122             :      // Assumes shheap points to a memory region in the caller's local
     123             :      // address space formatted as a myheap, that there are no joins to
     124             :      // the myheap and that any application side cleanups have been
     125             :      // done.  Returns shheap on success and NULL on failure (logs
     126             :      // details).
     127             : 
     128             :      ulong      myheap_align    ( void                             );
     129             :      ulong      myheap_footprint( ulong      ele_max               );
     130             :      void *     myheap_new      ( void *     shmem,  ulong ele_max );
     131             :      myheap_t * myheap_join     ( void *     shheap                );
     132             :      void *     myheap_leave    ( myheap_t * heap                  );
     133             :      void *     myheap_delete   ( void *     shheap                );
     134             : 
     135             :      // myheap_{ele_max,ele_cnt} gives the maximum number of elements
     136             :      // the heap can support / the current number of elements in the
     137             :      // heap.  Assumes heap is a current local join.  These might be
     138             :      // deprecated in the future.
     139             : 
     140             :      ulong myheap_ele_max( myheap_t const * heap );
     141             :      ulong myheap_ele_cnt( myheap_t const * heap );
     142             : 
     143             :      // myheap_idx_peek_min returns the index where the minimum element
     144             :      // is on the heap.  Returns [0,ele_max) on success and
     145             :      // myheap_idx_null() if the heap is empty.  Lifetime of the
     146             :      // returned idx is the lesser of until the min element is removed
     147             :      // or the underlying element storage lifetime.  myheap_ele_peek_min
     148             :      // is the same but returns the location in the caller's address
     149             :      // space of the found element on success and NULL on failure
     150             :      // (lifetime of the returned pointer is until the min element is
     151             :      // removed or ele's local lifetime).  myheap_ele_peek_min_const is
     152             :      // a const correct version.  These operations are a fast O(1).
     153             :      // pool is a pointer in the caller's address space to the ele_max
     154             :      // linearly addressable storage region backing the heap.
     155             : 
     156             :      ulong           myheap_idx_peek_min      ( myheap_t const * heap, char const * q, myele_t const * pool );
     157             :      myele_t *       myheap_ele_peek_min      ( myheap_t *       heap, char const * q, myele_t *       pool );
     158             :      myele_t const * myheap_ele_peek_min_const( myheap_t const * heap, char const * q, myele_t const * pool );
     159             : 
     160             :      // myheap_idx_insert inserts element n into the heap and returns
     161             :      // heap.  Assumes heap is a current local join, ele points in the
     162             :      // caller's address space to the ele_max element storage used for
     163             :      // heap elements, n is in [0,ele_max), n is currently not in the
     164             :      // heap, and n's queries are not in the heap (n's queries are the
     165             :      // set of queries that are covered by n).  pool is a pointer in the
     166             :      // caller's address space to the ele_max linearly addressable
     167             :      // storage region backing the heap.  Given these assumptions, this
     168             :      // cannot fail.
     169             :      //
     170             :      // n's query fields should already be populated (i.e.
     171             :      // MYHEAP_LT(ele+n,ele+i) should return valid results before this
     172             :      // is called).  On return, n and n's queries will be in the heap.
     173             :      // n's left and right should not be modified while n is in the
     174             :      // heap.  Further, the caller should not assume n's left and right
     175             :      // values are stable while n is in the heap.  The heap does not
     176             :      // care about any other fields and these can be modified by the
     177             :      // user as necessary.
     178             :      //
     179             :      // myheap_ele_insert is the same but n points in the caller's local
     180             :      // address space the element to insert / remove.
     181             :      //
     182             :      // These operations have HPC implementations and are O(lg N)
     183             :      // average with an ultra high probability of having a small
     184             :      // coefficient.
     185             : 
     186             :      myheap_t * myheap_idx_insert( myheap_t * heap, ulong     n, myele_t * pool );
     187             :      myheap_t * myheap_ele_insert( myheap_t * heap, myele_t * n, myele_t * pool );
     188             : 
     189             :      // myheap_idx_remove_min removes the min element from the heap and
     190             :      // returns heap.  Assumes heap is a current local join, ele points
     191             :      // in the caller's address space to the ele_max element storage
     192             :      // used for heap elements and the heap has at least one element.
     193             :      // pool is a pointer in the caller's address space to the ele_max
     194             :      // linearly addressable storage region backing the heap.  Given
     195             :      // these assumptions, this cannot fail.
     196             :      //
     197             :      // On return the min element and the min element's queries are no
     198             :      // longer in the heap.  Use peek before calling this to get the
     199             :      // location of the min element to be removed.  The fields of the
     200             :      // removed element can be freely modified on return.
     201             :      //
     202             :      // myheap_ele_remove_min is the same and just for naming
     203             :      // consistency.
     204             :      //
     205             :      // These operations have HPC implementations and are O(lg N)
     206             :      // average with an ultra high probability of having a small
     207             :      // coefficient (i.e. close to algorithmically optimal trees).
     208             : 
     209             :      myheap_t * myheap_idx_remove_min( myheap_t * heap, myele_t * pool );
     210             :      myheap_t * myheap_ele_remove_min( myheap_t * heap, myele_t * pool );
     211             : 
     212             :      // myheap_verify returns 0 if the myheap is not obviously corrupt
     213             :      // or a -1 (i.e. ERR_INVAL) if it is (logs details).  heap is a
     214             :      // current local join to a myheap.  pool is a pointer in the
     215             :      // caller's address space to the ele_max linearly addressable
     216             :      // storage region backing the heap.
     217             : 
     218             :      int myheap_verify( myheap_t const * heap, myele_t const * pool );
     219             : 
     220             :    You can do this as often as you like within a compilation unit to get
     221             :    different types of heaps.  Variants exist for making separate headers
     222             :    and implementations for doing libraries and handling multiple
     223             :    compilation units.  Additional options exist as detailed below. */
     224             : 
     225             : /* HEAP_NAME gives the API prefix to use */
     226             : 
     227             : #ifndef HEAP_NAME
     228             : #error "Define HEAP_NAME"
     229             : #endif
     230             : 
     231             : /* HEAP_T is the heap element type */
     232             : 
     233             : #ifndef HEAP_T
     234             : #error "Define HEAP_T"
     235             : #endif
     236             : 
     237             : /* HEAP_LT returns 1 if the element e0's query fields are strictly less
     238             :    element e1's query fields and 0 otherwise.  Should be a pure
     239             :    function. */
     240             : 
     241             : #ifndef HEAP_LT
     242             : #error "Define HEAP_LT"
     243             : #endif
     244             : 
     245             : /* HEAP_IDX_T is the type used for the HEAP_T fields.  Should be a
     246             :    primitive unsigned integer type.  Defaults to ulong.  A heap can't
     247             :    use element memory regions that contain more than the maximum value
     248             :    that can be represented by a HEAP_IDX_T. */
     249             : 
     250             : #ifndef HEAP_IDX_T
     251           0 : #define HEAP_IDX_T ulong
     252             : #endif
     253             : 
     254             : /* HEAP_{LEFT,RIGHT} is the name of the heap element left / right
     255             :    fields.  Defaults to left / right. */
     256             : 
     257             : #ifndef HEAP_LEFT
     258           0 : #define HEAP_LEFT left
     259             : #endif
     260             : 
     261             : #ifndef HEAP_RIGHT
     262           0 : #define HEAP_RIGHT right
     263             : #endif
     264             : 
     265             : /* HEAP_IMPL_STYLE controls what this template should emit.
     266             :    0 - local use only
     267             :    1 - library header
     268             :    2 - library implementation */
     269             : 
     270             : #ifndef HEAP_IMPL_STYLE
     271             : #define HEAP_IMPL_STYLE 0
     272             : #endif
     273             : 
     274             : /* Implementation *****************************************************/
     275             : 
     276             : #if HEAP_IMPL_STYLE==0
     277             : #define HEAP_STATIC static FD_FN_UNUSED
     278             : #else
     279             : #define HEAP_STATIC
     280             : #endif
     281             : 
     282  2132295078 : #define HEAP_IDX_NULL           ((ulong)(HEAP_IDX_T)(~0UL))
     283  2087942823 : #define HEAP_IDX_IS_NULL( idx ) ((idx)==HEAP_IDX_NULL)
     284             : 
     285    34803810 : #define HEAP_(n) FD_EXPAND_THEN_CONCAT3(HEAP_NAME,_,n)
     286             : 
     287             : /* Verification logs details on failure.  The rest only needs fd_bits.h
     288             :    (consider making logging a compile time option). */
     289             : 
     290             : #include "../log/fd_log.h"
     291             : 
     292             : #if HEAP_IMPL_STYLE!=2 /* need structures, prototypes and inlines */
     293             : 
     294             : /* structures */
     295             : 
     296             : /* TODO: consider eliminating ele_cnt and maybe ele_max fields (less overhead,
     297             :    faster bulk ops, concurrency options, simpler constructors, etc) */
     298             : 
     299             : struct HEAP_(private) {
     300             :   ulong      ele_max; /* Maximum number of elements in heap, in [0,HEAP_IDX_NULL] */
     301             :   ulong      ele_cnt; /* Current number of elements in heap, in [0,ele_max] */
     302             :   HEAP_IDX_T root;    /* Index of the root heap element, in [0,ele_max) or HEAP_IDX_NULL */
     303             : };
     304             : 
     305             : typedef struct HEAP_(private) HEAP_(t);
     306             : 
     307             : FD_PROTOTYPES_BEGIN
     308             : 
     309             : /* prototypes */
     310             : 
     311             : HEAP_STATIC FD_FN_CONST ulong      HEAP_(align)    ( void                             );
     312             : HEAP_STATIC FD_FN_CONST ulong      HEAP_(footprint)( ulong      ele_max               );
     313             : HEAP_STATIC /**/        void *     HEAP_(new)      ( void *     shmem,  ulong ele_max );
     314             : HEAP_STATIC /**/        HEAP_(t) * HEAP_(join)     ( void *     shheap                );
     315             : HEAP_STATIC /**/        void *     HEAP_(leave)    ( HEAP_(t) * heap                  );
     316             : HEAP_STATIC /**/        void *     HEAP_(delete)   ( void *     shheap                );
     317             : 
     318             : HEAP_STATIC HEAP_(t) * HEAP_(idx_insert)( HEAP_(t) * heap, ulong n, HEAP_T * pool );
     319             : 
     320             : HEAP_STATIC HEAP_(t) * HEAP_(idx_remove_min)( HEAP_(t) * heap, HEAP_T * pool );
     321             : 
     322             : HEAP_STATIC FD_FN_PURE int HEAP_(verify)( HEAP_(t) const * heap, HEAP_T const * pool );
     323             : 
     324             : /* inlines */
     325             : 
     326   991910559 : FD_FN_PURE static inline int HEAP_(lt) ( HEAP_T const * e0, HEAP_T const * e1 ) { return HEAP_LT( e0, e1 ); }
     327             : 
     328           0 : FD_FN_CONST static inline ulong          HEAP_(idx_null)      ( void ) { return HEAP_IDX_NULL; }
     329           0 : FD_FN_CONST static inline HEAP_T *       HEAP_(ele_null)      ( void ) { return NULL;          }
     330           0 : FD_FN_CONST static inline HEAP_T const * HEAP_(ele_null_const)( void ) { return NULL;          }
     331             : 
     332      217560 : FD_FN_CONST static inline int HEAP_(idx_is_null)( ulong          i ) { return HEAP_IDX_IS_NULL( i ); }
     333         768 : FD_FN_CONST static inline int HEAP_(ele_is_null)( HEAP_T const * e ) { return !e;                    }
     334             : 
     335             : FD_FN_CONST static inline ulong
     336             : HEAP_(idx)( HEAP_T const * e,
     337    29579712 :             HEAP_T const * pool ) {
     338    29579712 :   return fd_ulong_if( !!e, (ulong)(e-pool), HEAP_IDX_NULL );
     339    29579712 : }
     340             : 
     341             : FD_FN_CONST static inline HEAP_T *
     342             : HEAP_(ele)( ulong    i,
     343         768 :             HEAP_T * pool ) {
     344         768 :   return fd_ptr_if( !HEAP_IDX_IS_NULL( i ), pool + i, NULL );
     345         768 : }
     346             : 
     347             : FD_FN_CONST static inline HEAP_T const *
     348             : HEAP_(ele_const)( ulong          i,
     349         768 :                   HEAP_T const * pool ) {
     350         768 :   return fd_ptr_if( !HEAP_IDX_IS_NULL( i ), pool + i, NULL );
     351         768 : }
     352             : 
     353         765 : FD_FN_CONST static inline ulong          HEAP_(idx_fast)      ( HEAP_T const * e, HEAP_T const * pool ) { return (ulong)(e - pool); }
     354         765 : FD_FN_CONST static inline HEAP_T *       HEAP_(ele_fast)      ( ulong i,          HEAP_T *       pool ) { return pool + i; }
     355         765 : FD_FN_CONST static inline HEAP_T const * HEAP_(ele_fast_const)( ulong i,          HEAP_T const * pool ) { return pool + i; }
     356             : 
     357    15006264 : FD_FN_PURE static inline ulong HEAP_(ele_max)( HEAP_(t) const * heap ) { return heap->ele_max; }
     358    15006264 : FD_FN_PURE static inline ulong HEAP_(ele_cnt)( HEAP_(t) const * heap ) { return heap->ele_cnt; }
     359             : 
     360    22392384 : FD_FN_PURE static inline ulong HEAP_(idx_peek_min)( HEAP_(t) const * heap ) { return (ulong)heap->root; }
     361             : 
     362             : FD_FN_PURE static inline HEAP_T *
     363             : HEAP_(ele_peek_min)( HEAP_(t) const * heap,
     364    15006264 :                      HEAP_T *         pool ) {
     365    15006264 :   ulong i = (ulong)heap->root;
     366    15006264 :   return fd_ptr_if( !HEAP_IDX_IS_NULL( i ), pool + i, NULL );
     367    15006264 : }
     368             : 
     369             : FD_FN_PURE static inline HEAP_T const *
     370             : HEAP_(ele_peek_min_const)( HEAP_(t) const * heap,
     371    15006264 :                            HEAP_T const *   pool ) {
     372    15006264 :   ulong i = (ulong)heap->root;
     373    15006264 :   return fd_ptr_if( !HEAP_IDX_IS_NULL( i ), pool + i, NULL );
     374    15006264 : }
     375             : 
     376             : static inline HEAP_(t) *
     377             : HEAP_(ele_insert)( HEAP_(t) * heap,
     378             :                    HEAP_T *   e,
     379     3692667 :                    HEAP_T *   pool ) {
     380     3692667 :   return HEAP_(idx_insert)( heap, (ulong)(e - pool), pool );
     381     3692667 : }
     382             : 
     383             : static inline HEAP_(t) *
     384             : HEAP_(ele_remove_min)( HEAP_(t) * heap,
     385     3691989 :                        HEAP_T *   pool ) {
     386     3691989 :   return HEAP_(idx_remove_min)( heap, pool );
     387     3691989 : }
     388             : 
     389             : FD_PROTOTYPES_END
     390             : 
     391             : #endif
     392             : 
     393             : #if HEAP_IMPL_STYLE!=1 /* need implementations */
     394             : 
     395             : HEAP_STATIC FD_FN_CONST ulong
     396           0 : HEAP_(align)( void ) {
     397           0 :   return alignof(HEAP_(t));
     398           0 : }
     399             : 
     400             : HEAP_STATIC FD_FN_CONST ulong
     401           6 : HEAP_(footprint)( ulong ele_max ) {
     402           6 :   if( FD_UNLIKELY( ele_max>HEAP_IDX_NULL ) ) return 0UL;
     403           3 :   return sizeof(HEAP_(t));
     404           6 : }
     405             : 
     406             : HEAP_STATIC void *
     407             : HEAP_(new)( void * shmem,
     408          12 :             ulong  ele_max ) {
     409          12 :   if( !shmem ) {
     410           3 :     FD_LOG_WARNING(( "NULL shmem" ));
     411           3 :     return NULL;
     412           3 :   }
     413             : 
     414           9 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, HEAP_(align)() ) ) ) {
     415           3 :     FD_LOG_WARNING(( "misaligned shmem" ));
     416           3 :     return NULL;
     417           3 :   }
     418             : 
     419           6 :   if( FD_UNLIKELY( ele_max>HEAP_IDX_NULL ) ) {
     420           3 :     FD_LOG_WARNING(( "ele_max too large" ));
     421           3 :     return NULL;
     422           3 :   }
     423             : 
     424           3 :   HEAP_(t) * heap = (HEAP_(t) *)shmem;
     425             : 
     426           3 :   heap->ele_max = ele_max;
     427           3 :   heap->ele_cnt = 0UL;
     428           3 :   heap->root    = (HEAP_IDX_T)HEAP_IDX_NULL;
     429             : 
     430           3 :   return heap;
     431           6 : }
     432             : 
     433             : HEAP_STATIC HEAP_(t) *
     434           9 : HEAP_(join)( void * shheap ) {
     435           9 :   if( FD_UNLIKELY( !shheap ) ) {
     436           3 :     FD_LOG_WARNING(( "NULL shheap" ));
     437           3 :     return NULL;
     438           3 :   }
     439             : 
     440           6 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shheap, HEAP_(align)() ) ) ) {
     441           3 :     FD_LOG_WARNING(( "misaligned shheap" ));
     442           3 :     return NULL;
     443           3 :   }
     444             : 
     445           3 :   return (HEAP_(t) *)shheap;
     446           6 : }
     447             : 
     448             : HEAP_STATIC void *
     449           6 : HEAP_(leave)( HEAP_(t) * heap ) {
     450           6 :   if( FD_UNLIKELY( !heap ) ) {
     451           3 :     FD_LOG_WARNING(( "NULL heap" ));
     452           3 :     return NULL;
     453           3 :   }
     454             : 
     455           3 :   return (void *)heap;
     456           6 : }
     457             : 
     458             : HEAP_STATIC void *
     459           9 : HEAP_(delete)( void * shheap ) {
     460           9 :   if( FD_UNLIKELY( !shheap ) ) {
     461           3 :     FD_LOG_WARNING(( "NULL shheap" ));
     462           3 :     return NULL;
     463           3 :   }
     464             : 
     465           6 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shheap, HEAP_(align)() ) ) ) {
     466           3 :     FD_LOG_WARNING(( "misaligned shheap" ));
     467           3 :     return NULL;
     468           3 :   }
     469             : 
     470           3 :   return shheap;
     471           6 : }
     472             : 
     473             : HEAP_STATIC HEAP_(t) *
     474             : HEAP_(idx_insert)( HEAP_(t) * heap,
     475             :                    ulong      n,
     476     7386270 :                    HEAP_T *   pool ) {
     477             : 
     478     7386270 :   HEAP_IDX_T * _p_child = &heap->root;
     479             : 
     480     7386270 :   ulong i = (ulong)*_p_child;
     481             : 
     482     7386270 :   if( FD_UNLIKELY( HEAP_IDX_IS_NULL( i ) ) ) { /* Heap was empty, make n the root, opt for larger heaps */
     483      109533 :     pool[ n ].HEAP_LEFT  = (HEAP_IDX_T)HEAP_IDX_NULL;
     484      109533 :     pool[ n ].HEAP_RIGHT = (HEAP_IDX_T)HEAP_IDX_NULL;
     485      109533 :     *_p_child            = (HEAP_IDX_T)n;
     486      109533 :     heap->ele_cnt++;
     487      109533 :     return heap;
     488      109533 :   }
     489             : 
     490     7276737 :   ulong rbits_cnt = 0UL;
     491     7276737 :   ulong rbits     = 0UL;
     492             : 
     493    31611390 :   for(;;) {
     494    31611390 :     ulong l = (ulong)pool[ i ].HEAP_LEFT;
     495    31611390 :     ulong r = (ulong)pool[ i ].HEAP_RIGHT;
     496             : 
     497             :     /* At this point, i's ancestors are less than n.  If n is before i,
     498             :        displace i with n. */
     499             : 
     500    31611390 :     if( FD_UNLIKELY( HEAP_(lt)( pool + n, pool + i ) ) ) { /* Opt for larger heaps and IID rank inserts */
     501    25149279 :       pool[ n ].HEAP_LEFT  = (HEAP_IDX_T)l;
     502    25149279 :       pool[ n ].HEAP_RIGHT = (HEAP_IDX_T)r;
     503    25149279 :       *_p_child = (HEAP_IDX_T)n;
     504    25149279 :       ulong swap_tmp = i; i = n; n = swap_tmp;
     505    25149279 :     }
     506             : 
     507             :     /* At this point i < n.  If there is neither a left subheap nor a
     508             :        right subheap, make n i's left child.  If there is no left/right
     509             :        subheap, make n i's left/right child. */
     510             : 
     511    31611390 :     int is_null_left  = HEAP_IDX_IS_NULL( l );
     512    31611390 :     int is_null_right = HEAP_IDX_IS_NULL( r );
     513    31611390 :     if( FD_UNLIKELY( (is_null_left | is_null_right) ) ) { /* Opt for larger heaps */
     514     7276737 :       pool[ n ].HEAP_LEFT  = (HEAP_IDX_T)HEAP_IDX_NULL;
     515     7276737 :       pool[ n ].HEAP_RIGHT = (HEAP_IDX_T)HEAP_IDX_NULL;
     516     7276737 :       *fd_ptr_if( is_null_left, &pool[ i ].HEAP_LEFT, &pool[ i ].HEAP_RIGHT ) = (HEAP_IDX_T)n;
     517     7276737 :       break;
     518     7276737 :     }
     519             : 
     520             :     /* At this point, i has a left and right subheap.  We need to pick one
     521             :        to insert n into.  Ideally we'd pick the smaller one.  But since
     522             :        we don't know this (might be possible to be clever here using the
     523             :        cnt of items in the heap on the assumption that heap has been
     524             :        optimally constructed thus far though this would probably only
     525             :        work in pure heapsort like cases), we pseudo randomly pick one
     526             :        instead. */
     527             : 
     528    24334653 :     if( FD_UNLIKELY( !rbits_cnt ) ) {
     529     7037898 :       rbits     = fd_ulong_hash( (                      i       ^ fd_ulong_rotate_left( n, 16 )) ^
     530     7037898 :                                  (fd_ulong_rotate_left( l, 32 ) ^ fd_ulong_rotate_left( r, 48 )) );
     531     7037898 :       rbits_cnt = 64UL; /* TODO: consider using fraction to mix up further? */
     532     7037898 :     }
     533    24334653 :     int go_left = (int)(rbits & 1UL);
     534    24334653 :     rbits >>= 1;
     535    24334653 :     rbits_cnt--;
     536             : 
     537    24334653 :     _p_child = fd_ptr_if  ( go_left, &pool[ i ].HEAP_LEFT, &pool[ i ].HEAP_RIGHT );
     538    24334653 :     i        = fd_ulong_if( go_left, l,                    r                     );
     539    24334653 :   }
     540             : 
     541     7276737 :   heap->ele_cnt++;
     542     7276737 :   return heap;
     543     7386270 : }
     544             : 
     545             : HEAP_STATIC HEAP_(t) *
     546             : HEAP_(idx_remove_min)( HEAP_(t) * heap,
     547     7386120 :                        HEAP_T *   pool ) {
     548     7386120 :   ulong d = (ulong)heap->root;
     549             : 
     550     7386120 :   HEAP_IDX_T * _p_child = &heap->root;
     551     7386120 :   ulong        l        = (ulong)pool[ d ].HEAP_LEFT;
     552     7386120 :   ulong        r        = (ulong)pool[ d ].HEAP_RIGHT;
     553             : 
     554    34805271 :   for(;;) {
     555             : 
     556             :     /* At this point, we have a hole to fill at d.
     557             : 
     558             :        l is the hole's left subheap (if any)
     559             :        r is the hole's right subheap (if any)
     560             : 
     561             :        p_child points to the link from the d's parent to d (if d has a
     562             :        parent) and to the heap root link otherwise.
     563             : 
     564             :        If there is neither a left subheap nor a right subheap, we are
     565             :        done.  If there is a left/right subheap, we fill the hole with
     566             :        the right/left subheap and we are done. */
     567             : 
     568    34805271 :     int is_null_left  = HEAP_IDX_IS_NULL( l );
     569    34805271 :     int is_null_right = HEAP_IDX_IS_NULL( r );
     570    34805271 :     if( FD_UNLIKELY( is_null_left | is_null_right ) ) { /* Opt for larger heaps */
     571     7386120 :       *_p_child = (HEAP_IDX_T)fd_ulong_if( is_null_left, r, l );
     572     7386120 :       break;
     573     7386120 :     }
     574             : 
     575             :     /* d has two subheaps.  We fill the hole with the smaller root element
     576             :        (preserving the heap property).  This bubbles d down one layer
     577             :        toward the subheap with the smaller root.  We fill that hole the
     578             :        next iteration.  Note we don't need to update any links to/from d
     579             :        as we will be getting rid of all links to/from d. */
     580             : 
     581    27419151 :     int promote_left = HEAP_(lt)( pool + l, pool + r );
     582             : 
     583    27419151 :     ulong c     = fd_ulong_if( promote_left, l, r );
     584    27419151 :     ulong l_nxt = (ulong)pool[ c ].HEAP_LEFT;
     585    27419151 :     ulong r_nxt = (ulong)pool[ c ].HEAP_RIGHT;
     586             : 
     587    27419151 :     *_p_child = (HEAP_IDX_T)c;
     588             : 
     589    27419151 :     *fd_ptr_if( promote_left, &pool[ l ].HEAP_RIGHT, &pool[ r ].HEAP_LEFT ) = (HEAP_IDX_T)fd_ulong_if( promote_left, r, l );
     590             : 
     591    27419151 :     _p_child = fd_ptr_if( promote_left, &pool[ l ].HEAP_LEFT, &pool[ r ].HEAP_RIGHT );
     592    27419151 :     l        = l_nxt;
     593    27419151 :     r        = r_nxt;
     594    27419151 :   }
     595             : 
     596     7386120 :   heap->ele_cnt--;
     597     7386120 :   return heap;
     598     7386120 : }
     599             : 
     600             : HEAP_STATIC int
     601             : HEAP_(verify)( HEAP_(t) const * heap,
     602    30000003 :                HEAP_T const *   pool ) {
     603             : 
     604  3970217940 : # define HEAP_TEST( c ) do { if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: " #c )); return -1; } } while(0)
     605             : 
     606    30000003 :   HEAP_TEST( heap ); /* Validate local join */
     607             : 
     608    30000003 :   ulong ele_max = heap->ele_max;
     609    30000003 :   ulong ele_cnt = heap->ele_cnt;
     610    30000003 :   HEAP_TEST( ele_max<=HEAP_IDX_NULL ); /* Validate ele_max */
     611    30000003 :   HEAP_TEST( ele_cnt<=ele_max       ); /* Validate ele_cnt */
     612    30000003 :   if( ele_max ) HEAP_TEST( pool );     /* Validate ele storage */
     613             : 
     614    30000003 :   ulong stack[ 512 ];
     615    30000003 :   ulong stack_cnt = 0UL;
     616    30000003 :   ulong stack_max = 512UL; /* Should be impossibly large if heap is statistically well balanced */
     617             : 
     618    30000003 :   ulong visit_cnt = 0UL;
     619             : 
     620    30000003 :   ulong i = (ulong)heap->root;
     621    30000003 :   if( !HEAP_IDX_IS_NULL( i ) ) {      /* Schedule first visit */
     622    29565951 :     HEAP_TEST( i<ele_max );           /* Make sure inbounds */
     623    29565951 :     HEAP_TEST( stack_cnt<stack_max ); /* Make sure no stack overflow */
     624    29565951 :     stack[ stack_cnt++ ] = i;         /* Push i to stack */
     625    29565951 :   }
     626             : 
     627   992445972 :   while( stack_cnt ) { /* While still nodes to visit */
     628   962445969 :     HEAP_TEST( visit_cnt<ele_cnt ); /* Make sure no cycles */
     629             : 
     630   962445969 :     i = stack[ --stack_cnt ]; /* Pop the stack to get next visit (value was validated on push) */
     631             : 
     632             :     /* visit i and schedule visits to i's children */
     633             : 
     634   962445969 :     ulong r = (ulong)pool[ i ].HEAP_RIGHT;
     635   962445969 :     if( !HEAP_IDX_IS_NULL( r ) ) {
     636   392997171 :       HEAP_TEST( HEAP_(lt)( pool + i, pool + r ) ); /* Make sure heap property satisfied */
     637   392997171 :       HEAP_TEST( r<ele_max );                       /* Make sure inbounds */
     638   392997171 :       HEAP_TEST( stack_cnt<stack_max );             /* Make sure no stack overflow */
     639   392997171 :       stack[ stack_cnt++ ] = r;                     /* Push r to stack */
     640   392997171 :     }
     641             : 
     642   962445969 :     ulong l = (ulong)pool[ i ].HEAP_LEFT;
     643   962445969 :     if( !HEAP_IDX_IS_NULL( l ) ) {
     644   539882847 :       HEAP_TEST( HEAP_(lt)( pool + i, pool + l ) ); /* Make sure heap property satisfied */
     645   539882847 :       HEAP_TEST( l<ele_max );                       /* Make sure inbounds */
     646   539882847 :       HEAP_TEST( stack_cnt<stack_max );             /* Make sure no stack overflow */
     647   539882847 :       stack[ stack_cnt++ ] = l;                     /* Push l to stack */
     648   539882847 :     }
     649             : 
     650   962445969 :     visit_cnt++; /* update the number visited */
     651   962445969 :   }
     652             : 
     653    30000003 :   HEAP_TEST( visit_cnt==ele_cnt ); /* Make sure visit count matches */
     654    30000003 :   return 0;
     655    30000003 : }
     656             : 
     657             : #endif
     658             : 
     659             : #undef HEAP_IDX_IS_NULL
     660             : #undef HEAP_IDX_NULL
     661             : #undef HEAP_STATIC
     662             : 
     663             : #undef HEAP_IMPL_STYLE
     664             : #undef HEAP_RIGHT
     665             : #undef HEAP_LEFT
     666             : #undef HEAP_IDX_T
     667             : #undef HEAP_LT
     668             : #undef HEAP_T
     669             : #undef HEAP_NAME
     670             : 

Generated by: LCOV version 1.14