LCOV - code coverage report
Current view: top level - disco/gui - fd_gui_shred.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 312 314 99.4 %
Date: 2026-11-12 04:25:03 Functions: 23 23 100.0 %

          Line data    Source code
       1             : #include "fd_gui_shred.h"
       2             : #include "fd_gui.h"
       3             : 
       4       56352 : #define FD_GUI_SHRED_LITERAL     (0U)
       5     3388479 : #define FD_GUI_SHRED_DELTA       (1U)
       6    28335405 : #define FD_GUI_SHRED_TS_NEG      (2U)
       7    28321179 : #define FD_GUI_SHRED_IDX_NEG     (4U)
       8    35102232 : #define FD_GUI_SHRED_EVENT_SHIFT (3U)
       9             : 
      10             : struct __attribute__((packed)) fd_gui_shred_delta {
      11             :   uint  insert_delta;
      12             :   uint  timestamp_delta : 24;
      13             :   uchar idx_delta;
      14             : };
      15             : typedef struct fd_gui_shred_delta fd_gui_shred_delta_t;
      16             : 
      17             : FD_STATIC_ASSERT( sizeof(fd_gui_shred_delta_t)==8UL, shred_delta_sz );
      18             : 
      19             : static ulong
      20      299154 : shred_window( long ns ) {
      21      299154 :   return ns<=0L ? 0UL : (ulong)(ns/FD_GUI_HIST_RES_1S_NS);
      22      299154 : }
      23             : 
      24             : static inline fd_gui_shred_event_t
      25     6300726 : fec_shred_event( fd_gui_fec_event_t const * src ) {
      26     6300726 :   return (fd_gui_shred_event_t){ .insert_time_ns=src->insert_time_ns, .event_time_ns=src->event_time_ns,
      27     6300726 :                                  .slot=src->key.slot, .idx=src->key.idx, .event=src->key.event };
      28     6300726 : }
      29             : 
      30             : static int
      31             : batch_append( fd_gui_shred_builder_t *      builder,
      32     3451788 :               fd_gui_shred_event_t const * event ) {
      33     3451788 :   fd_gui_shred_batch_t * batch = &builder->batch;
      34     3451788 :   fd_gui_shred_event_t const * prev = &builder->prev;
      35     3451788 :   ulong ts_delta = event->event_time_ns>=prev->event_time_ns
      36     3451788 :       ? (ulong)event->event_time_ns-(ulong)prev->event_time_ns
      37     3451788 :       : (ulong)prev->event_time_ns-(ulong)event->event_time_ns;
      38     3451788 :   ulong idx_delta = event->idx>=prev->idx ? (ulong)(event->idx-prev->idx) : (ulong)(prev->idx-event->idx);
      39     3451788 :   int delta = batch->event_cnt && event->slot==prev->slot && ts_delta<=FD_GUI_SHRED_EVENT_TS_MAX && idx_delta<=UCHAR_MAX &&
      40     3451788 :               event->event<=(UCHAR_MAX>>FD_GUI_SHRED_EVENT_SHIFT);
      41     3451788 :   ulong sz = 1UL + (delta ? sizeof(fd_gui_shred_delta_t) : sizeof(fd_gui_shred_event_t));
      42     3451788 :   if( FD_UNLIKELY( sz>sizeof(batch->data)-batch->data_sz ) ) return 0;
      43             : 
      44     3444831 :   uchar * dst = batch->data+batch->data_sz;
      45     3444831 :   if( FD_LIKELY( delta ) ) {
      46     3388479 :     *dst++ = (uchar)(FD_GUI_SHRED_DELTA |
      47     3388479 :                     (event->event_time_ns<prev->event_time_ns ? FD_GUI_SHRED_TS_NEG  : 0U) |
      48     3388479 :                     (event->idx<prev->idx ? FD_GUI_SHRED_IDX_NEG : 0U) |
      49     3388479 :                     ((uint)event->event<<FD_GUI_SHRED_EVENT_SHIFT));
      50     3388479 :     fd_gui_shred_delta_t d = {
      51     3388479 :       .insert_delta    = (uint)(event->insert_time_ns-prev->insert_time_ns),
      52     3388479 :       .timestamp_delta = (uint)(ts_delta & FD_GUI_SHRED_EVENT_TS_MAX),
      53     3388479 :       .idx_delta       = (uchar)idx_delta
      54     3388479 :     };
      55     3388479 :     fd_memcpy( dst, &d, sizeof(d) );
      56     3388479 :   } else {
      57       56352 :     *dst++ = FD_GUI_SHRED_LITERAL;
      58       56352 :     fd_memcpy( dst, event, sizeof(*event) );
      59       56352 :   }
      60     3444831 :   batch->data_sz += (uint)sz;
      61     3444831 :   batch->event_cnt++;
      62     3444831 :   builder->prev = *event;
      63     3444831 :   return 1;
      64     3451788 : }
      65             : 
      66             : ulong
      67             : fd_gui_fec_archive( fd_gui_t * gui,
      68        6168 :                     ulong      budget ) {
      69        6168 :   if( !gui->db || !gui->hist ) return 0UL;
      70        6168 :   ulong total = 0UL;
      71       18474 :   while( budget ) {
      72       12327 :     fd_gui_store_kv_scan_t scan;
      73       12327 :     fd_gui_store_kv_scan_begin( gui->db, &scan, FD_GUI_HIST_FEC_EVENTS );
      74       12327 :     fd_gui_shred_builder_t builder = {0};
      75       12327 :     fd_gui_fec_event_batch_t events = { .min_ns=LONG_MAX, .max_ns=LONG_MIN };
      76       12327 :     ulong count = 0UL;
      77     3158082 :     while( count<budget && fd_gui_store_kv_scan_next( &scan ) ) {
      78     3151929 :       fd_gui_fec_event_t const * src = scan.rec;
      79     3151929 :       if( src->key.slot>=fd_gui_event_closed_slot_wmark( gui ) ) break;
      80             :       /* Bound the batch's admission span so event-time queries can find
      81             :          it (see FD_GUI_FEC_BATCH_SPAN_NS).  This also keeps the codec's
      82             :          32-bit admission deltas in range. */
      83     3151905 :       if( count && src->insert_time_ns-events.insert_time_ns>FD_GUI_FEC_BATCH_SPAN_NS ) break;
      84     3151899 :       fd_gui_shred_event_t event = fec_shred_event( src );
      85     3151899 :       if( !count ) events.insert_time_ns = builder.batch.insert_time_ns = src->insert_time_ns;
      86     3151899 :       if( !batch_append( &builder, &event ) ) break;
      87     3145755 :       events.min_ns = fd_long_min( events.min_ns, src->event_time_ns );
      88     3145755 :       events.max_ns = fd_long_max( events.max_ns, src->event_time_ns );
      89     3145755 :       count++;
      90     3145755 :     }
      91       12327 :     if( !count ) break; /* An open prefix pins all later admissions. */
      92       12306 :     events.batch = builder.batch;
      93             :     /* Keep staging intact on archive admission failure. */
      94       12306 :     if( fd_gui_hist_ts_append( gui, FD_GUI_HIST_FEC_EVENT_ARCHIVE, &events ) ) break;
      95       12306 :     FD_TEST( fd_gui_store_kv_reclaim_prefix( gui->db, FD_GUI_HIST_FEC_EVENTS, count, NULL, NULL )==count );
      96       12306 :     budget -= count;
      97       12306 :     total  += count;
      98       12306 :   }
      99        6168 :   return total;
     100        6168 : }
     101             : 
     102             : static void
     103    64027824 : batch_next( fd_gui_shred_event_iter_t * iter ) {
     104    64027824 :   uchar const * src = iter->_batch->data+iter->_data_off;
     105    64027824 :   uchar tag = *src++;
     106    64027824 :   if( FD_LIKELY( tag & FD_GUI_SHRED_DELTA ) ) {
     107    28318578 :     fd_gui_shred_delta_t d;
     108    28318578 :     fd_memcpy( &d, src, sizeof(d) );
     109    28318578 :     iter->event.insert_time_ns += (long)d.insert_delta;
     110    28318578 :     iter->event.event_time_ns += (tag & FD_GUI_SHRED_TS_NEG) ? -(long)d.timestamp_delta : (long)d.timestamp_delta;
     111    28318578 :     iter->event.idx = (ushort)((tag & FD_GUI_SHRED_IDX_NEG) ? iter->event.idx-d.idx_delta : iter->event.idx+d.idx_delta);
     112    28318578 :     iter->event.event = (uchar)(tag>>FD_GUI_SHRED_EVENT_SHIFT);
     113    28318578 :     iter->_data_off += 1UL+sizeof(d);
     114    35709246 :   } else {
     115    35709246 :     fd_memcpy( &iter->event, src, sizeof(iter->event) );
     116    35709246 :     iter->_data_off += 1UL+sizeof(iter->event);
     117    35709246 :   }
     118    64027824 :   iter->_batch_idx++;
     119    64027824 : }
     120             : 
     121             : static int
     122             : fec_admitted_before( void const * rec,
     123         291 :                      void *       ctx ) {
     124         291 :   return ((fd_gui_fec_event_t const *)rec)->insert_time_ns<*(long const *)ctx;
     125         291 : }
     126             : 
     127             : static fd_gui_shred_event_iter_t *
     128             : shred_iter_begin( fd_gui_t *                  gui,
     129             :                   fd_gui_shred_event_iter_t * iter,
     130             :                   int                         event_time,
     131             :                   int                         fec,
     132             :                   long                        after_ns,
     133        6429 :                   long                        before_ns ) {
     134        6429 :   fd_memset( iter, 0, sizeof(*iter) );
     135        6429 :   iter->_gui         = gui;
     136        6429 :   iter->_after_ns    = after_ns;
     137        6429 :   iter->_before_ns   = before_ns;
     138        6429 :   iter->_event_time  = event_time;
     139        6429 :   iter->_fec_archive = fec;
     140             : 
     141             :   /* Event-time shred queries widen the scan by +-1 second. The
     142             :      assumption is that event-ordering approximately equals
     143             :      insert-ordering, and in rare cases where there is more than a second
     144             :      of skew in the system we can tolerate missing data.  Stored FEC event
     145             :      times are within FD_GUI_FEC_EVENT_LAG_MAX_NS of admission and
     146             :      batches span at most FD_GUI_FEC_BATCH_SPAN_NS of admission. */
     147        6429 :   if( FD_LIKELY( gui->db && gui->hist && after_ns<=before_ns ) ) {
     148        6429 :     long lo_margin = fec ? FD_GUI_FEC_EVENT_LAG_MAX_NS+FD_GUI_FEC_BATCH_SPAN_NS : event_time ? FD_GUI_HIST_RES_1S_NS : 0L;
     149        6429 :     long hi_margin = fec ? FD_GUI_FEC_EVENT_LAG_MAX_NS                          : event_time ? FD_GUI_HIST_RES_1S_NS : 0L;
     150        6429 :     long lo_ns = fd_long_max( 0L, fd_long_sat_sub( after_ns, lo_margin ) );
     151        6429 :     long hi_ns = fd_long_min( LONG_MAX-1L, fd_long_sat_add( before_ns, hi_margin ) );
     152        6429 :     int  dbi   = fec ? FD_GUI_HIST_FEC_EVENT_ARCHIVE : FD_GUI_HIST_SHRED_EVENTS;
     153        6429 :     iter->_hist_active = !fd_gui_hist_range_begin( gui, &iter->_hist_iter, dbi, lo_ns, hi_ns, NULL, NULL );
     154        6429 :     if( fec ) {
     155             :       /* Staged admission times are nondecreasing, so only admissions in
     156             :          [after_ns-LAG_MAX,before_ns+LAG_MAX) can match. */
     157          60 :       long insert_lo = fd_long_sat_sub( after_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
     158          60 :       iter->_fec_insert_hi = fd_long_sat_add( before_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
     159          60 :       fd_gui_store_kv_scan_begin( gui->db, &iter->_fec_iter, FD_GUI_HIST_FEC_EVENTS );
     160          60 :       fd_gui_store_kv_scan_seek( &iter->_fec_iter, fec_admitted_before, &insert_lo );
     161        6369 :     } else {
     162        6369 :       iter->_builder_pending = 1;
     163        6369 :     }
     164        6429 :   }
     165        6429 :   return iter;
     166        6429 : }
     167             : 
     168             : fd_gui_shred_event_iter_t *
     169             : fd_gui_shred_event_hist_iter_begin( fd_gui_t *                  gui,
     170             :                                     fd_gui_shred_event_iter_t * iter,
     171             :                                     long                        after_ns,
     172          12 :                                     long                        before_ns ) {
     173          12 :   return shred_iter_begin( gui, iter, 1, 0, after_ns, before_ns );
     174          12 : }
     175             : 
     176             : fd_gui_shred_event_iter_t *
     177             : fd_gui_shred_event_iter_begin( fd_gui_t *                  gui,
     178             :                                fd_gui_shred_event_iter_t * iter,
     179             :                                long                        after_ns,
     180        6357 :                                long                        before_ns ) {
     181        6357 :   return shred_iter_begin( gui, iter, 0, 0, fd_long_max( after_ns, 0L ), fd_long_min( before_ns, LONG_MAX-1L ) );
     182        6357 : }
     183             : 
     184             : int
     185    23186601 : fd_gui_shred_event_iter_next( fd_gui_shred_event_iter_t * iter ) {
     186    67495800 :   while( iter->_batch || iter->_hist_active || iter->_builder_pending ) {
     187    64341450 :     if( iter->_batch && iter->_batch_idx<iter->_batch->event_cnt ) {
     188    64027824 :       batch_next( iter );
     189    64027824 :       long ts = iter->_event_time ? iter->event.event_time_ns : iter->event.insert_time_ns;
     190    64027824 :       if( ts>=iter->_after_ns && (iter->_event_time ? ts<iter->_before_ns : ts<=iter->_before_ns) ) return 1;
     191    43995573 :       continue;
     192    64027824 :     }
     193      313626 :     iter->_batch = NULL;
     194      313626 :     iter->_batch_idx = 0UL;
     195      313626 :     iter->_data_off  = 0UL;
     196      313626 :     if( iter->_hist_active && fd_gui_hist_range_next( &iter->_hist_iter ) ) {
     197      302631 :       if( iter->_fec_archive ) {
     198       36966 :         fd_gui_fec_event_batch_t const * b = iter->_hist_iter.rec;
     199       36966 :         if( b->max_ns<iter->_after_ns || b->min_ns>=iter->_before_ns ) continue;
     200       12345 :         iter->_batch = &b->batch;
     201      265665 :       } else iter->_batch = iter->_hist_iter.rec;
     202      278010 :       continue;
     203      302631 :     }
     204       10995 :     if( iter->_hist_active ) fd_gui_hist_range_end( &iter->_hist_iter );
     205       10995 :     iter->_hist_active = 0;
     206       10995 :     if( iter->_builder_pending ) {
     207        5472 :       iter->_builder_pending = 0;
     208        5472 :       iter->_batch = &iter->_gui->shreds.builder.batch;
     209        5472 :     }
     210       10995 :   }
     211             : 
     212             :   /* Staged FEC events, only set up by fd_gui_fec_event_iter_begin. */
     213     3157422 :   while( fd_gui_store_kv_scan_next( &iter->_fec_iter ) ) {
     214     3151929 :     fd_gui_fec_event_t const * src = iter->_fec_iter.rec;
     215     3151929 :     if( src->insert_time_ns>=iter->_fec_insert_hi ) {
     216          30 :       iter->_fec_iter.db = NULL;
     217          30 :       break;
     218          30 :     }
     219     3151899 :     if( src->event_time_ns<iter->_after_ns || src->event_time_ns>=iter->_before_ns ) continue;
     220     3148827 :     iter->event = fec_shred_event( src );
     221     3148827 :     return 1;
     222     3151899 :   }
     223        5523 :   return 0;
     224     3154350 : }
     225             : 
     226             : void
     227        6429 : fd_gui_shred_event_iter_end( fd_gui_shred_event_iter_t * iter ) {
     228        6429 :   if( iter->_hist_active ) fd_gui_hist_range_end( &iter->_hist_iter );
     229        6429 :   iter->_hist_active = 0;
     230        6429 :   iter->_builder_pending = 0;
     231        6429 :   iter->_batch = NULL;
     232        6429 :   iter->_fec_iter.db = NULL;
     233        6429 : }
     234             : 
     235             : static void
     236         903 : batch_flush( fd_gui_t * gui ) {
     237         903 :   fd_gui_shred_batch_t * batch = &gui->shreds.builder.batch;
     238         903 :   if( !batch->event_cnt ) return;
     239         903 :   if( FD_UNLIKELY( fd_gui_hist_ts_append( gui, FD_GUI_HIST_SHRED_EVENTS, batch ) ) ) {
     240           0 :     gui->shreds.dropped_event_cnt += batch->event_cnt;
     241           0 :   }
     242         903 :   batch->event_cnt = 0U;
     243         903 :   batch->data_sz   = 0U;
     244         903 : }
     245             : 
     246             : void
     247             : fd_gui_shred_event_append( fd_gui_t * gui,
     248             :                            ulong      slot,
     249             :                            ulong      idx,
     250             :                            uchar      event,
     251             :                            long       timestamp,
     252      316980 :                            long       now ) {
     253      316980 :   fd_gui_shred_builder_t * builder = &gui->shreds.builder;
     254      316980 :   int marker = event==FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE;
     255      316980 :   if( FD_UNLIKELY( !gui->db || !gui->hist || slot>UINT_MAX || idx>USHORT_MAX ||
     256      316980 :                   (marker ? idx!=USHORT_MAX : idx==USHORT_MAX) ||
     257      316980 :                   (event>FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE && event!=FD_GUI_SLOT_SHRED_SHRED_PUBLISHED) ||
     258      316980 :                   (!marker && (timestamp<0L || timestamp==LONG_MAX || slot<fd_gui_event_closed_slot_wmark( gui ))) ||
     259      316980 :                   now<0L || now==LONG_MAX || now<builder->prev.insert_time_ns ) ) {
     260       17904 :     gui->shreds.dropped_event_cnt++;
     261       17904 :     return;
     262       17904 :   }
     263      299076 :   if( event==FD_GUI_SLOT_SHRED_SHRED_RECEIVED_TURBINE || event==FD_GUI_SLOT_SHRED_SHRED_RECEIVED_REPAIR ||
     264      299076 :       event==FD_GUI_SLOT_SHRED_SHRED_PUBLISHED ) {
     265             :     /* Index the first arrival per slot for the timeline; the raw events
     266             :        cannot be looked up by slot.  Producer timestamps need not arrive
     267             :        in order. */
     268      245793 :     fd_gui_epoch_t * epoch = fd_gui_get_epoch_by_slot( gui, slot );
     269      245793 :     if( epoch && slot>=epoch->start_slot && slot-epoch->start_slot<fd_ulong_min( epoch->slot_cnt, MAX_SLOTS_PER_EPOCH ) ) {
     270         219 :       ulong i     = slot-epoch->start_slot;
     271         219 :       uchar state = epoch->timeline_slot_state[ i ];
     272         219 :       epoch->timeline_slot_first_shred_ns[ i ] = (state & FD_GUI_TIMELINE_SLOT_STATE_STARTED)
     273         219 :         ? fd_long_min( epoch->timeline_slot_first_shred_ns[ i ], timestamp ) : timestamp;
     274         219 :       epoch->timeline_slot_state[ i ] = (uchar)(state | FD_GUI_TIMELINE_SLOT_STATE_STARTED);
     275         219 :     }
     276      245793 :   }
     277      299076 :   fd_gui_shred_batch_t * batch = &builder->batch;
     278      299076 :   long window_ns = (long)shred_window( now )*FD_GUI_HIST_RES_1S_NS;
     279      299076 :   if( batch->event_cnt && batch->insert_time_ns!=window_ns ) batch_flush( gui );
     280      299076 :   batch->insert_time_ns = window_ns;
     281      299076 :   fd_gui_shred_event_t rec = { .insert_time_ns=now, .event_time_ns=timestamp, .slot=(uint)slot, .idx=(ushort)idx, .event=event };
     282      299076 :   if( FD_UNLIKELY( !batch_append( builder, &rec ) ) ) {
     283         813 :     batch_flush( gui );
     284         813 :     FD_TEST( batch_append( builder, &rec ) );
     285         813 :   }
     286      299076 : }
     287             : 
     288             : int
     289             : fd_gui_shred_flush( fd_gui_t * gui,
     290          39 :                     long       now ) {
     291          39 :   if( FD_UNLIKELY( !gui->db || !gui->hist ) ) return 0;
     292          39 :   fd_gui_shred_batch_t const * batch = &gui->shreds.builder.batch;
     293          39 :   if( !batch->event_cnt || shred_window( batch->insert_time_ns )>=shred_window( now ) ) return 0;
     294          36 :   batch_flush( gui );
     295          36 :   return 1;
     296          39 : }
     297             : 
     298             : ulong
     299     6605286 : fd_gui_event_closed_slot_wmark( fd_gui_t const * gui ) {
     300     6605286 :   return gui->shreds.closed_slot_wmark;
     301     6605286 : }
     302             : 
     303             : /* fec_slot_hi_wmark returns the FEC staging high watermark: slots at or
     304             :    above it are not staged. */
     305             : 
     306             : static inline ulong
     307     3154587 : fec_slot_hi_wmark( fd_gui_t const * gui ) {
     308     3154587 :   return fd_ulong_sat_add( gui->shreds.closed_slot_wmark, FD_GUI_FEC_SLOT_WMARK_CNT );
     309     3154587 : }
     310             : 
     311             : void
     312             : fd_gui_fec_event_iter_begin( fd_gui_t *                  gui,
     313             :                              fd_gui_shred_event_iter_t * iter,
     314             :                              long                        after_ns,
     315          60 :                              long                        before_ns ) {
     316          60 :   shred_iter_begin( gui, iter, 1, 1, after_ns, before_ns );
     317          60 : }
     318             : 
     319             : /* fec_event_skewed tests whether timestamp is more than
     320             :    FD_GUI_FEC_EVENT_LAG_MAX_NS from the admission time insert_ns.
     321             :    Skewed events are not stored, so event-time queries can find every
     322             :    stored event from its admission time. */
     323             : 
     324             : static int
     325             : fec_event_skewed( long insert_ns,
     326     3148923 :                   long timestamp ) {
     327     3148923 :   return timestamp<fd_long_sat_sub( insert_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS ) ||
     328     3148923 :          timestamp>fd_long_sat_add( insert_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
     329     3148923 : }
     330             : 
     331             : void
     332             : fd_gui_fec_event_append( fd_gui_t * gui,
     333             :                          ulong      slot,
     334             :                          ulong      idx,
     335             :                          uchar      event,
     336             :                          long       timestamp,
     337     3154614 :                          long       now ) {
     338     3154614 :   int marker = event==FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE;
     339     3154614 :   if( FD_UNLIKELY( !gui->db || !gui->hist || slot>UINT_MAX || idx>USHORT_MAX ||
     340     3154614 :                   (marker ? idx!=USHORT_MAX : idx==USHORT_MAX) || timestamp<0L || timestamp==LONG_MAX ||
     341     3154614 :                   now<0L || now==LONG_MAX ||
     342     3154614 :                   (event>FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE && event!=FD_GUI_SLOT_SHRED_SHRED_PUBLISHED) ||
     343     3154614 :                   (!marker && slot<fd_gui_event_closed_slot_wmark( gui )) || slot>=fec_slot_hi_wmark( gui ) ||
     344     3154614 :                   (marker && gui->shreds.fec_marker_sequence==ULONG_MAX) ) ) {
     345        5685 :     gui->shreds.dropped_fec_event_cnt++;
     346        5685 :     return;
     347        5685 :   }
     348             :   /* Staging admission order must be monotone in admission time, since
     349             :      it becomes the archive order. */
     350     3148929 :   now = fd_long_max( now, gui->shreds.fec_insert_time_ns );
     351     3148929 :   fd_gui_fec_event_key_t key = { .slot=(uint)slot, .idx=(ushort)idx, .event=event };
     352     3148929 :   fd_gui_fec_event_t * dst = marker ? NULL : fd_gui_hist_kv_get( gui, FD_GUI_HIST_FEC_EVENTS, &key );
     353     3148929 :   if( dst && timestamp>=dst->event_time_ns ) return;
     354     3148923 :   if( FD_UNLIKELY( fec_event_skewed( dst ? dst->insert_time_ns : now, timestamp ) ) ) {
     355           9 :     gui->shreds.clamped_fec_event_cnt++;
     356           9 :     return;
     357           9 :   }
     358     3148914 :   if( dst ) {
     359           6 :     dst->event_time_ns = timestamp;
     360           6 :     return;
     361           6 :   }
     362     3148908 :   if( marker ) key.marker_sequence = ++gui->shreds.fec_marker_sequence;
     363     3148908 :   dst = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_FEC_EVENTS, &key );
     364     3148908 :   if( FD_UNLIKELY( !dst ) ) {
     365           3 :     gui->shreds.dropped_fec_event_cnt++;
     366           3 :     return;
     367           3 :   }
     368     3148905 :   *dst = (fd_gui_fec_event_t){ .key=key, .insert_time_ns=now, .event_time_ns=timestamp };
     369     3148905 :   gui->shreds.fec_insert_time_ns = now;
     370     3148905 : }
     371             : 
     372             : void
     373             : fd_gui_shred_event_slot_complete( fd_gui_t * gui,
     374             :                                   ulong      slot,
     375             :                                   long       timestamp,
     376          66 :                                   long       now ) {
     377          66 :   if( FD_UNLIKELY( slot>UINT_MAX ) ) return;
     378          66 :   fd_gui_shred_event_append( gui, slot, USHORT_MAX, FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE, timestamp, now );
     379          66 :   fd_gui_fec_event_append( gui, slot, USHORT_MAX, FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE, timestamp, now );
     380          66 : }
     381             : 
     382             : void
     383             : fd_gui_event_closed_slot_wmark_advance( fd_gui_t * gui,
     384          39 :                                         ulong      wmark ) {
     385          39 :   if( FD_UNLIKELY( wmark==ULONG_MAX || wmark<=gui->shreds.closed_slot_wmark ) ) return;
     386          36 :   gui->shreds.closed_slot_wmark = wmark;
     387          36 : }
     388             : 
     389             : int
     390             : fd_gui_event_bounds( fd_gui_t * gui,
     391             :                      int        dbi,
     392             :                      long *     lo,
     393          27 :                      long *     hi ) {
     394          27 :   if( dbi==FD_GUI_HIST_FEC_EVENTS ) {
     395             :     /* Bound by admission time, widened by the lag bound and batch span
     396             :        rather than scanning event times. */
     397           9 :     long first = LONG_MAX;
     398           9 :     long last  = LONG_MIN;
     399           9 :     long a_first, a_last;
     400           9 :     if( gui->db && fd_gui_store_ts_live_timestamp_bounds( gui->db, FD_GUI_HIST_FEC_EVENT_ARCHIVE, &a_first, &a_last ) ) {
     401           6 :       first = a_first;
     402           6 :       last  = fd_long_sat_add( a_last, FD_GUI_FEC_BATCH_SPAN_NS );
     403           6 :     }
     404           9 :     if( gui->db ) {
     405           9 :       fd_gui_store_kv_scan_t it;
     406           9 :       fd_gui_store_kv_scan_begin( gui->db, &it, FD_GUI_HIST_FEC_EVENTS );
     407           9 :       if( fd_gui_store_kv_scan_next( &it ) ) {
     408           6 :         first = fd_long_min( first, ((fd_gui_fec_event_t const *)it.rec)->insert_time_ns );
     409           6 :         last  = fd_long_max( last, gui->shreds.fec_insert_time_ns );
     410           6 :       }
     411           9 :     }
     412           9 :     int have = first!=LONG_MAX;
     413           9 :     *lo = have ? fd_long_max( 0L, fd_long_sat_sub( first, FD_GUI_FEC_EVENT_LAG_MAX_NS ) ) : LONG_MAX;
     414           9 :     *hi = have ? fd_long_min( LONG_MAX-1L, fd_long_sat_add( last, FD_GUI_FEC_EVENT_LAG_MAX_NS+1L ) ) : LONG_MIN;
     415           9 :     return have;
     416           9 :   }
     417          18 :   long first;
     418          18 :   long last;
     419          18 :   int have = gui->db && fd_gui_store_ts_live_timestamp_bounds( gui->db, (ulong)dbi, &first, &last );
     420          18 :   if( dbi==FD_GUI_HIST_SHRED_EVENTS && gui->shreds.builder.batch.event_cnt ) {
     421           9 :     long active = gui->shreds.builder.batch.insert_time_ns;
     422           9 :     first = have ? fd_long_min( first, active ) : active;
     423           9 :     last  = have ? fd_long_max( last, active ) : active;
     424           9 :     have  = 1;
     425           9 :   }
     426             : 
     427          18 :   if( have && dbi==FD_GUI_HIST_SHRED_EVENTS ) last = fd_long_sat_add( last, FD_GUI_HIST_RES_1S_NS-1L );
     428          18 :   *lo = have ? fd_long_max( 0L, fd_long_sat_sub( first, FD_GUI_HIST_RES_1S_NS ) ) : LONG_MAX;
     429          18 :   *hi = have ? fd_long_min( LONG_MAX-1L, fd_long_sat_add( last, FD_GUI_HIST_RES_1S_NS+1L ) ) : LONG_MIN;
     430          18 :   return have;
     431          27 : }
     432             : 
     433             : int
     434             : fd_gui_shred_window_is_empty( fd_gui_t * gui,
     435             :                               long       after_ns,
     436         918 :                               long       before_ns ) {
     437         918 :   fd_gui_shred_event_iter_t it[ 1 ];
     438         918 :   fd_gui_shred_event_iter_begin( gui, it, after_ns, before_ns );
     439         918 :   int empty = !fd_gui_shred_event_iter_next( it );
     440         918 :   fd_gui_shred_event_iter_end( it );
     441         918 :   return empty;
     442         918 : }

Generated by: LCOV version 1.14