LCOV - code coverage report
Current view: top level - disco/gui - fd_gui_shred.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 314 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 23 0.0 %

          Line data    Source code
       1             : #include "fd_gui_shred.h"
       2             : #include "fd_gui.h"
       3             : 
       4           0 : #define FD_GUI_SHRED_LITERAL     (0U)
       5           0 : #define FD_GUI_SHRED_DELTA       (1U)
       6           0 : #define FD_GUI_SHRED_TS_NEG      (2U)
       7           0 : #define FD_GUI_SHRED_IDX_NEG     (4U)
       8           0 : #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           0 : shred_window( long ns ) {
      21           0 :   return ns<=0L ? 0UL : (ulong)(ns/FD_GUI_HIST_RES_1S_NS);
      22           0 : }
      23             : 
      24             : static inline fd_gui_shred_event_t
      25           0 : fec_shred_event( fd_gui_fec_event_t const * src ) {
      26           0 :   return (fd_gui_shred_event_t){ .insert_time_ns=src->insert_time_ns, .event_time_ns=src->event_time_ns,
      27           0 :                                  .slot=src->key.slot, .idx=src->key.idx, .event=src->key.event };
      28           0 : }
      29             : 
      30             : static int
      31             : batch_append( fd_gui_shred_builder_t *      builder,
      32           0 :               fd_gui_shred_event_t const * event ) {
      33           0 :   fd_gui_shred_batch_t * batch = &builder->batch;
      34           0 :   fd_gui_shred_event_t const * prev = &builder->prev;
      35           0 :   ulong ts_delta = event->event_time_ns>=prev->event_time_ns
      36           0 :       ? (ulong)event->event_time_ns-(ulong)prev->event_time_ns
      37           0 :       : (ulong)prev->event_time_ns-(ulong)event->event_time_ns;
      38           0 :   ulong idx_delta = event->idx>=prev->idx ? (ulong)(event->idx-prev->idx) : (ulong)(prev->idx-event->idx);
      39           0 :   int delta = batch->event_cnt && event->slot==prev->slot && ts_delta<=FD_GUI_SHRED_EVENT_TS_MAX && idx_delta<=UCHAR_MAX &&
      40           0 :               event->event<=(UCHAR_MAX>>FD_GUI_SHRED_EVENT_SHIFT);
      41           0 :   ulong sz = 1UL + (delta ? sizeof(fd_gui_shred_delta_t) : sizeof(fd_gui_shred_event_t));
      42           0 :   if( FD_UNLIKELY( sz>sizeof(batch->data)-batch->data_sz ) ) return 0;
      43             : 
      44           0 :   uchar * dst = batch->data+batch->data_sz;
      45           0 :   if( FD_LIKELY( delta ) ) {
      46           0 :     *dst++ = (uchar)(FD_GUI_SHRED_DELTA |
      47           0 :                     (event->event_time_ns<prev->event_time_ns ? FD_GUI_SHRED_TS_NEG  : 0U) |
      48           0 :                     (event->idx<prev->idx ? FD_GUI_SHRED_IDX_NEG : 0U) |
      49           0 :                     ((uint)event->event<<FD_GUI_SHRED_EVENT_SHIFT));
      50           0 :     fd_gui_shred_delta_t d = {
      51           0 :       .insert_delta    = (uint)(event->insert_time_ns-prev->insert_time_ns),
      52           0 :       .timestamp_delta = (uint)(ts_delta & FD_GUI_SHRED_EVENT_TS_MAX),
      53           0 :       .idx_delta       = (uchar)idx_delta
      54           0 :     };
      55           0 :     fd_memcpy( dst, &d, sizeof(d) );
      56           0 :   } else {
      57           0 :     *dst++ = FD_GUI_SHRED_LITERAL;
      58           0 :     fd_memcpy( dst, event, sizeof(*event) );
      59           0 :   }
      60           0 :   batch->data_sz += (uint)sz;
      61           0 :   batch->event_cnt++;
      62           0 :   builder->prev = *event;
      63           0 :   return 1;
      64           0 : }
      65             : 
      66             : ulong
      67             : fd_gui_fec_archive( fd_gui_t * gui,
      68           0 :                     ulong      budget ) {
      69           0 :   if( !gui->db || !gui->hist ) return 0UL;
      70           0 :   ulong total = 0UL;
      71           0 :   while( budget ) {
      72           0 :     fd_gui_store_kv_scan_t scan;
      73           0 :     fd_gui_store_kv_scan_begin( gui->db, &scan, FD_GUI_HIST_FEC_EVENTS );
      74           0 :     fd_gui_shred_builder_t builder = {0};
      75           0 :     fd_gui_fec_event_batch_t events = { .min_ns=LONG_MAX, .max_ns=LONG_MIN };
      76           0 :     ulong count = 0UL;
      77           0 :     while( count<budget && fd_gui_store_kv_scan_next( &scan ) ) {
      78           0 :       fd_gui_fec_event_t const * src = scan.rec;
      79           0 :       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           0 :       if( count && src->insert_time_ns-events.insert_time_ns>FD_GUI_FEC_BATCH_SPAN_NS ) break;
      84           0 :       fd_gui_shred_event_t event = fec_shred_event( src );
      85           0 :       if( !count ) events.insert_time_ns = builder.batch.insert_time_ns = src->insert_time_ns;
      86           0 :       if( !batch_append( &builder, &event ) ) break;
      87           0 :       events.min_ns = fd_long_min( events.min_ns, src->event_time_ns );
      88           0 :       events.max_ns = fd_long_max( events.max_ns, src->event_time_ns );
      89           0 :       count++;
      90           0 :     }
      91           0 :     if( !count ) break; /* An open prefix pins all later admissions. */
      92           0 :     events.batch = builder.batch;
      93             :     /* Keep staging intact on archive admission failure. */
      94           0 :     if( fd_gui_hist_ts_append( gui, FD_GUI_HIST_FEC_EVENT_ARCHIVE, &events ) ) break;
      95           0 :     FD_TEST( fd_gui_store_kv_reclaim_prefix( gui->db, FD_GUI_HIST_FEC_EVENTS, count, NULL, NULL )==count );
      96           0 :     budget -= count;
      97           0 :     total  += count;
      98           0 :   }
      99           0 :   return total;
     100           0 : }
     101             : 
     102             : static void
     103           0 : batch_next( fd_gui_shred_event_iter_t * iter ) {
     104           0 :   uchar const * src = iter->_batch->data+iter->_data_off;
     105           0 :   uchar tag = *src++;
     106           0 :   if( FD_LIKELY( tag & FD_GUI_SHRED_DELTA ) ) {
     107           0 :     fd_gui_shred_delta_t d;
     108           0 :     fd_memcpy( &d, src, sizeof(d) );
     109           0 :     iter->event.insert_time_ns += (long)d.insert_delta;
     110           0 :     iter->event.event_time_ns += (tag & FD_GUI_SHRED_TS_NEG) ? -(long)d.timestamp_delta : (long)d.timestamp_delta;
     111           0 :     iter->event.idx = (ushort)((tag & FD_GUI_SHRED_IDX_NEG) ? iter->event.idx-d.idx_delta : iter->event.idx+d.idx_delta);
     112           0 :     iter->event.event = (uchar)(tag>>FD_GUI_SHRED_EVENT_SHIFT);
     113           0 :     iter->_data_off += 1UL+sizeof(d);
     114           0 :   } else {
     115           0 :     fd_memcpy( &iter->event, src, sizeof(iter->event) );
     116           0 :     iter->_data_off += 1UL+sizeof(iter->event);
     117           0 :   }
     118           0 :   iter->_batch_idx++;
     119           0 : }
     120             : 
     121             : static int
     122             : fec_admitted_before( void const * rec,
     123           0 :                      void *       ctx ) {
     124           0 :   return ((fd_gui_fec_event_t const *)rec)->insert_time_ns<*(long const *)ctx;
     125           0 : }
     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           0 :                   long                        before_ns ) {
     134           0 :   fd_memset( iter, 0, sizeof(*iter) );
     135           0 :   iter->_gui         = gui;
     136           0 :   iter->_after_ns    = after_ns;
     137           0 :   iter->_before_ns   = before_ns;
     138           0 :   iter->_event_time  = event_time;
     139           0 :   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           0 :   if( FD_LIKELY( gui->db && gui->hist && after_ns<=before_ns ) ) {
     148           0 :     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           0 :     long hi_margin = fec ? FD_GUI_FEC_EVENT_LAG_MAX_NS                          : event_time ? FD_GUI_HIST_RES_1S_NS : 0L;
     150           0 :     long lo_ns = fd_long_max( 0L, fd_long_sat_sub( after_ns, lo_margin ) );
     151           0 :     long hi_ns = fd_long_min( LONG_MAX-1L, fd_long_sat_add( before_ns, hi_margin ) );
     152           0 :     int  dbi   = fec ? FD_GUI_HIST_FEC_EVENT_ARCHIVE : FD_GUI_HIST_SHRED_EVENTS;
     153           0 :     iter->_hist_active = !fd_gui_hist_range_begin( gui, &iter->_hist_iter, dbi, lo_ns, hi_ns, NULL, NULL );
     154           0 :     if( fec ) {
     155             :       /* Staged admission times are nondecreasing, so only admissions in
     156             :          [after_ns-LAG_MAX,before_ns+LAG_MAX) can match. */
     157           0 :       long insert_lo = fd_long_sat_sub( after_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
     158           0 :       iter->_fec_insert_hi = fd_long_sat_add( before_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
     159           0 :       fd_gui_store_kv_scan_begin( gui->db, &iter->_fec_iter, FD_GUI_HIST_FEC_EVENTS );
     160           0 :       fd_gui_store_kv_scan_seek( &iter->_fec_iter, fec_admitted_before, &insert_lo );
     161           0 :     } else {
     162           0 :       iter->_builder_pending = 1;
     163           0 :     }
     164           0 :   }
     165           0 :   return iter;
     166           0 : }
     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           0 :                                     long                        before_ns ) {
     173           0 :   return shred_iter_begin( gui, iter, 1, 0, after_ns, before_ns );
     174           0 : }
     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           0 :                                long                        before_ns ) {
     181           0 :   return shred_iter_begin( gui, iter, 0, 0, fd_long_max( after_ns, 0L ), fd_long_min( before_ns, LONG_MAX-1L ) );
     182           0 : }
     183             : 
     184             : int
     185           0 : fd_gui_shred_event_iter_next( fd_gui_shred_event_iter_t * iter ) {
     186           0 :   while( iter->_batch || iter->_hist_active || iter->_builder_pending ) {
     187           0 :     if( iter->_batch && iter->_batch_idx<iter->_batch->event_cnt ) {
     188           0 :       batch_next( iter );
     189           0 :       long ts = iter->_event_time ? iter->event.event_time_ns : iter->event.insert_time_ns;
     190           0 :       if( ts>=iter->_after_ns && (iter->_event_time ? ts<iter->_before_ns : ts<=iter->_before_ns) ) return 1;
     191           0 :       continue;
     192           0 :     }
     193           0 :     iter->_batch = NULL;
     194           0 :     iter->_batch_idx = 0UL;
     195           0 :     iter->_data_off  = 0UL;
     196           0 :     if( iter->_hist_active && fd_gui_hist_range_next( &iter->_hist_iter ) ) {
     197           0 :       if( iter->_fec_archive ) {
     198           0 :         fd_gui_fec_event_batch_t const * b = iter->_hist_iter.rec;
     199           0 :         if( b->max_ns<iter->_after_ns || b->min_ns>=iter->_before_ns ) continue;
     200           0 :         iter->_batch = &b->batch;
     201           0 :       } else iter->_batch = iter->_hist_iter.rec;
     202           0 :       continue;
     203           0 :     }
     204           0 :     if( iter->_hist_active ) fd_gui_hist_range_end( &iter->_hist_iter );
     205           0 :     iter->_hist_active = 0;
     206           0 :     if( iter->_builder_pending ) {
     207           0 :       iter->_builder_pending = 0;
     208           0 :       iter->_batch = &iter->_gui->shreds.builder.batch;
     209           0 :     }
     210           0 :   }
     211             : 
     212             :   /* Staged FEC events, only set up by fd_gui_fec_event_iter_begin. */
     213           0 :   while( fd_gui_store_kv_scan_next( &iter->_fec_iter ) ) {
     214           0 :     fd_gui_fec_event_t const * src = iter->_fec_iter.rec;
     215           0 :     if( src->insert_time_ns>=iter->_fec_insert_hi ) {
     216           0 :       iter->_fec_iter.db = NULL;
     217           0 :       break;
     218           0 :     }
     219           0 :     if( src->event_time_ns<iter->_after_ns || src->event_time_ns>=iter->_before_ns ) continue;
     220           0 :     iter->event = fec_shred_event( src );
     221           0 :     return 1;
     222           0 :   }
     223           0 :   return 0;
     224           0 : }
     225             : 
     226             : void
     227           0 : fd_gui_shred_event_iter_end( fd_gui_shred_event_iter_t * iter ) {
     228           0 :   if( iter->_hist_active ) fd_gui_hist_range_end( &iter->_hist_iter );
     229           0 :   iter->_hist_active = 0;
     230           0 :   iter->_builder_pending = 0;
     231           0 :   iter->_batch = NULL;
     232           0 :   iter->_fec_iter.db = NULL;
     233           0 : }
     234             : 
     235             : static void
     236           0 : batch_flush( fd_gui_t * gui ) {
     237           0 :   fd_gui_shred_batch_t * batch = &gui->shreds.builder.batch;
     238           0 :   if( !batch->event_cnt ) return;
     239           0 :   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           0 :   batch->event_cnt = 0U;
     243           0 :   batch->data_sz   = 0U;
     244           0 : }
     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           0 :                            long       now ) {
     253           0 :   fd_gui_shred_builder_t * builder = &gui->shreds.builder;
     254           0 :   int marker = event==FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE;
     255           0 :   if( FD_UNLIKELY( !gui->db || !gui->hist || slot>UINT_MAX || idx>USHORT_MAX ||
     256           0 :                   (marker ? idx!=USHORT_MAX : idx==USHORT_MAX) ||
     257           0 :                   (event>FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE && event!=FD_GUI_SLOT_SHRED_SHRED_PUBLISHED) ||
     258           0 :                   (!marker && (timestamp<0L || timestamp==LONG_MAX || slot<fd_gui_event_closed_slot_wmark( gui ))) ||
     259           0 :                   now<0L || now==LONG_MAX || now<builder->prev.insert_time_ns ) ) {
     260           0 :     gui->shreds.dropped_event_cnt++;
     261           0 :     return;
     262           0 :   }
     263           0 :   if( event==FD_GUI_SLOT_SHRED_SHRED_RECEIVED_TURBINE || event==FD_GUI_SLOT_SHRED_SHRED_RECEIVED_REPAIR ||
     264           0 :       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           0 :     fd_gui_epoch_t * epoch = fd_gui_get_epoch_by_slot( gui, slot );
     269           0 :     if( epoch && slot>=epoch->start_slot && slot-epoch->start_slot<fd_ulong_min( epoch->slot_cnt, MAX_SLOTS_PER_EPOCH ) ) {
     270           0 :       ulong i     = slot-epoch->start_slot;
     271           0 :       uchar state = epoch->timeline_slot_state[ i ];
     272           0 :       epoch->timeline_slot_first_shred_ns[ i ] = (state & FD_GUI_TIMELINE_SLOT_STATE_STARTED)
     273           0 :         ? fd_long_min( epoch->timeline_slot_first_shred_ns[ i ], timestamp ) : timestamp;
     274           0 :       epoch->timeline_slot_state[ i ] = (uchar)(state | FD_GUI_TIMELINE_SLOT_STATE_STARTED);
     275           0 :     }
     276           0 :   }
     277           0 :   fd_gui_shred_batch_t * batch = &builder->batch;
     278           0 :   long window_ns = (long)shred_window( now )*FD_GUI_HIST_RES_1S_NS;
     279           0 :   if( batch->event_cnt && batch->insert_time_ns!=window_ns ) batch_flush( gui );
     280           0 :   batch->insert_time_ns = window_ns;
     281           0 :   fd_gui_shred_event_t rec = { .insert_time_ns=now, .event_time_ns=timestamp, .slot=(uint)slot, .idx=(ushort)idx, .event=event };
     282           0 :   if( FD_UNLIKELY( !batch_append( builder, &rec ) ) ) {
     283           0 :     batch_flush( gui );
     284           0 :     FD_TEST( batch_append( builder, &rec ) );
     285           0 :   }
     286           0 : }
     287             : 
     288             : int
     289             : fd_gui_shred_flush( fd_gui_t * gui,
     290           0 :                     long       now ) {
     291           0 :   if( FD_UNLIKELY( !gui->db || !gui->hist ) ) return 0;
     292           0 :   fd_gui_shred_batch_t const * batch = &gui->shreds.builder.batch;
     293           0 :   if( !batch->event_cnt || shred_window( batch->insert_time_ns )>=shred_window( now ) ) return 0;
     294           0 :   batch_flush( gui );
     295           0 :   return 1;
     296           0 : }
     297             : 
     298             : ulong
     299           0 : fd_gui_event_closed_slot_wmark( fd_gui_t const * gui ) {
     300           0 :   return gui->shreds.closed_slot_wmark;
     301           0 : }
     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           0 : fec_slot_hi_wmark( fd_gui_t const * gui ) {
     308           0 :   return fd_ulong_sat_add( gui->shreds.closed_slot_wmark, FD_GUI_FEC_SLOT_WMARK_CNT );
     309           0 : }
     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           0 :                              long                        before_ns ) {
     316           0 :   shred_iter_begin( gui, iter, 1, 1, after_ns, before_ns );
     317           0 : }
     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           0 :                   long timestamp ) {
     327           0 :   return timestamp<fd_long_sat_sub( insert_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS ) ||
     328           0 :          timestamp>fd_long_sat_add( insert_ns, FD_GUI_FEC_EVENT_LAG_MAX_NS );
     329           0 : }
     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           0 :                          long       now ) {
     338           0 :   int marker = event==FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE;
     339           0 :   if( FD_UNLIKELY( !gui->db || !gui->hist || slot>UINT_MAX || idx>USHORT_MAX ||
     340           0 :                   (marker ? idx!=USHORT_MAX : idx==USHORT_MAX) || timestamp<0L || timestamp==LONG_MAX ||
     341           0 :                   now<0L || now==LONG_MAX ||
     342           0 :                   (event>FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE && event!=FD_GUI_SLOT_SHRED_SHRED_PUBLISHED) ||
     343           0 :                   (!marker && slot<fd_gui_event_closed_slot_wmark( gui )) || slot>=fec_slot_hi_wmark( gui ) ||
     344           0 :                   (marker && gui->shreds.fec_marker_sequence==ULONG_MAX) ) ) {
     345           0 :     gui->shreds.dropped_fec_event_cnt++;
     346           0 :     return;
     347           0 :   }
     348             :   /* Staging admission order must be monotone in admission time, since
     349             :      it becomes the archive order. */
     350           0 :   now = fd_long_max( now, gui->shreds.fec_insert_time_ns );
     351           0 :   fd_gui_fec_event_key_t key = { .slot=(uint)slot, .idx=(ushort)idx, .event=event };
     352           0 :   fd_gui_fec_event_t * dst = marker ? NULL : fd_gui_hist_kv_get( gui, FD_GUI_HIST_FEC_EVENTS, &key );
     353           0 :   if( dst && timestamp>=dst->event_time_ns ) return;
     354           0 :   if( FD_UNLIKELY( fec_event_skewed( dst ? dst->insert_time_ns : now, timestamp ) ) ) {
     355           0 :     gui->shreds.clamped_fec_event_cnt++;
     356           0 :     return;
     357           0 :   }
     358           0 :   if( dst ) {
     359           0 :     dst->event_time_ns = timestamp;
     360           0 :     return;
     361           0 :   }
     362           0 :   if( marker ) key.marker_sequence = ++gui->shreds.fec_marker_sequence;
     363           0 :   dst = fd_gui_hist_kv_get_or_create( gui, FD_GUI_HIST_FEC_EVENTS, &key );
     364           0 :   if( FD_UNLIKELY( !dst ) ) {
     365           0 :     gui->shreds.dropped_fec_event_cnt++;
     366           0 :     return;
     367           0 :   }
     368           0 :   *dst = (fd_gui_fec_event_t){ .key=key, .insert_time_ns=now, .event_time_ns=timestamp };
     369           0 :   gui->shreds.fec_insert_time_ns = now;
     370           0 : }
     371             : 
     372             : void
     373             : fd_gui_shred_event_slot_complete( fd_gui_t * gui,
     374             :                                   ulong      slot,
     375             :                                   long       timestamp,
     376           0 :                                   long       now ) {
     377           0 :   if( FD_UNLIKELY( slot>UINT_MAX ) ) return;
     378           0 :   fd_gui_shred_event_append( gui, slot, USHORT_MAX, FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE, timestamp, now );
     379           0 :   fd_gui_fec_event_append( gui, slot, USHORT_MAX, FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE, timestamp, now );
     380           0 : }
     381             : 
     382             : void
     383             : fd_gui_event_closed_slot_wmark_advance( fd_gui_t * gui,
     384           0 :                                         ulong      wmark ) {
     385           0 :   if( FD_UNLIKELY( wmark==ULONG_MAX || wmark<=gui->shreds.closed_slot_wmark ) ) return;
     386           0 :   gui->shreds.closed_slot_wmark = wmark;
     387           0 : }
     388             : 
     389             : int
     390             : fd_gui_event_bounds( fd_gui_t * gui,
     391             :                      int        dbi,
     392             :                      long *     lo,
     393           0 :                      long *     hi ) {
     394           0 :   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           0 :     long first = LONG_MAX;
     398           0 :     long last  = LONG_MIN;
     399           0 :     long a_first, a_last;
     400           0 :     if( gui->db && fd_gui_store_ts_live_timestamp_bounds( gui->db, FD_GUI_HIST_FEC_EVENT_ARCHIVE, &a_first, &a_last ) ) {
     401           0 :       first = a_first;
     402           0 :       last  = fd_long_sat_add( a_last, FD_GUI_FEC_BATCH_SPAN_NS );
     403           0 :     }
     404           0 :     if( gui->db ) {
     405           0 :       fd_gui_store_kv_scan_t it;
     406           0 :       fd_gui_store_kv_scan_begin( gui->db, &it, FD_GUI_HIST_FEC_EVENTS );
     407           0 :       if( fd_gui_store_kv_scan_next( &it ) ) {
     408           0 :         first = fd_long_min( first, ((fd_gui_fec_event_t const *)it.rec)->insert_time_ns );
     409           0 :         last  = fd_long_max( last, gui->shreds.fec_insert_time_ns );
     410           0 :       }
     411           0 :     }
     412           0 :     int have = first!=LONG_MAX;
     413           0 :     *lo = have ? fd_long_max( 0L, fd_long_sat_sub( first, FD_GUI_FEC_EVENT_LAG_MAX_NS ) ) : LONG_MAX;
     414           0 :     *hi = have ? fd_long_min( LONG_MAX-1L, fd_long_sat_add( last, FD_GUI_FEC_EVENT_LAG_MAX_NS+1L ) ) : LONG_MIN;
     415           0 :     return have;
     416           0 :   }
     417           0 :   long first;
     418           0 :   long last;
     419           0 :   int have = gui->db && fd_gui_store_ts_live_timestamp_bounds( gui->db, (ulong)dbi, &first, &last );
     420           0 :   if( dbi==FD_GUI_HIST_SHRED_EVENTS && gui->shreds.builder.batch.event_cnt ) {
     421           0 :     long active = gui->shreds.builder.batch.insert_time_ns;
     422           0 :     first = have ? fd_long_min( first, active ) : active;
     423           0 :     last  = have ? fd_long_max( last, active ) : active;
     424           0 :     have  = 1;
     425           0 :   }
     426             : 
     427           0 :   if( have && dbi==FD_GUI_HIST_SHRED_EVENTS ) last = fd_long_sat_add( last, FD_GUI_HIST_RES_1S_NS-1L );
     428           0 :   *lo = have ? fd_long_max( 0L, fd_long_sat_sub( first, FD_GUI_HIST_RES_1S_NS ) ) : LONG_MAX;
     429           0 :   *hi = have ? fd_long_min( LONG_MAX-1L, fd_long_sat_add( last, FD_GUI_HIST_RES_1S_NS+1L ) ) : LONG_MIN;
     430           0 :   return have;
     431           0 : }
     432             : 
     433             : int
     434             : fd_gui_shred_window_is_empty( fd_gui_t * gui,
     435             :                               long       after_ns,
     436           0 :                               long       before_ns ) {
     437           0 :   fd_gui_shred_event_iter_t it[ 1 ];
     438           0 :   fd_gui_shred_event_iter_begin( gui, it, after_ns, before_ns );
     439           0 :   int empty = !fd_gui_shred_event_iter_next( it );
     440           0 :   fd_gui_shred_event_iter_end( it );
     441           0 :   return empty;
     442           0 : }

Generated by: LCOV version 1.14