LCOV - code coverage report
Current view: top level - disco/gui - fd_gui_shred.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 164 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 13 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             : FD_STATIC_ASSERT( sizeof(fd_gui_shred_batch_t)==FD_GUI_SHRED_BATCH_SZ, shred_batch_sz );
      19             : 
      20             : static ulong
      21           0 : shred_window( long ns ) {
      22           0 :   return ns<=0L ? 0UL : (ulong)(ns/FD_GUI_HIST_RES_1S_NS);
      23           0 : }
      24             : 
      25             : static int
      26             : batch_append( fd_gui_shred_builder_t *      builder,
      27           0 :               fd_gui_shred_event_t const * event ) {
      28           0 :   fd_gui_shred_batch_t * batch = &builder->batch;
      29           0 :   fd_gui_shred_event_t const * prev = &builder->prev;
      30           0 :   ulong ts_delta = event->event_time_ns>=prev->event_time_ns
      31           0 :       ? (ulong)event->event_time_ns-(ulong)prev->event_time_ns
      32           0 :       : (ulong)prev->event_time_ns-(ulong)event->event_time_ns;
      33           0 :   ulong idx_delta = event->idx>=prev->idx ? (ulong)(event->idx-prev->idx) : (ulong)(prev->idx-event->idx);
      34           0 :   int delta = batch->event_cnt && event->slot==prev->slot && ts_delta<=FD_GUI_SHRED_EVENT_TS_MAX && idx_delta<=UCHAR_MAX &&
      35           0 :               event->event<=(UCHAR_MAX>>FD_GUI_SHRED_EVENT_SHIFT);
      36           0 :   ulong sz = 1UL + (delta ? sizeof(fd_gui_shred_delta_t) : sizeof(fd_gui_shred_event_t));
      37           0 :   if( FD_UNLIKELY( sz>sizeof(batch->data)-batch->data_sz ) ) return 0;
      38             : 
      39           0 :   uchar * dst = batch->data+batch->data_sz;
      40           0 :   if( FD_LIKELY( delta ) ) {
      41           0 :     *dst++ = (uchar)(FD_GUI_SHRED_DELTA |
      42           0 :                     (event->event_time_ns<prev->event_time_ns ? FD_GUI_SHRED_TS_NEG  : 0U) |
      43           0 :                     (event->idx<prev->idx ? FD_GUI_SHRED_IDX_NEG : 0U) |
      44           0 :                     ((uint)event->event<<FD_GUI_SHRED_EVENT_SHIFT));
      45           0 :     fd_gui_shred_delta_t d = {
      46           0 :       .insert_delta    = (uint)(event->insert_time_ns-prev->insert_time_ns),
      47           0 :       .timestamp_delta = (uint)(ts_delta & FD_GUI_SHRED_EVENT_TS_MAX),
      48           0 :       .idx_delta       = (uchar)idx_delta
      49           0 :     };
      50           0 :     fd_memcpy( dst, &d, sizeof(d) );
      51           0 :   } else {
      52           0 :     *dst++ = FD_GUI_SHRED_LITERAL;
      53           0 :     fd_memcpy( dst, event, sizeof(*event) );
      54           0 :   }
      55           0 :   batch->data_sz += (uint)sz;
      56           0 :   batch->event_cnt++;
      57           0 :   builder->prev = *event;
      58           0 :   return 1;
      59           0 : }
      60             : 
      61             : static void
      62           0 : batch_next( fd_gui_shred_event_iter_t * iter ) {
      63           0 :   uchar const * src = iter->_batch->data+iter->_data_off;
      64           0 :   uchar tag = *src++;
      65           0 :   if( FD_LIKELY( tag & FD_GUI_SHRED_DELTA ) ) {
      66           0 :     fd_gui_shred_delta_t d;
      67           0 :     fd_memcpy( &d, src, sizeof(d) );
      68           0 :     iter->event.insert_time_ns += (long)d.insert_delta;
      69           0 :     iter->event.event_time_ns += (tag & FD_GUI_SHRED_TS_NEG) ? -(long)d.timestamp_delta : (long)d.timestamp_delta;
      70           0 :     iter->event.idx = (ushort)((tag & FD_GUI_SHRED_IDX_NEG) ? iter->event.idx-d.idx_delta : iter->event.idx+d.idx_delta);
      71           0 :     iter->event.event = (uchar)(tag>>FD_GUI_SHRED_EVENT_SHIFT);
      72           0 :     iter->_data_off += 1UL+sizeof(d);
      73           0 :   } else {
      74           0 :     fd_memcpy( &iter->event, src, sizeof(iter->event) );
      75           0 :     iter->_data_off += 1UL+sizeof(iter->event);
      76           0 :   }
      77           0 :   iter->_batch_idx++;
      78           0 : }
      79             : 
      80             : static fd_gui_shred_event_iter_t *
      81             : shred_iter_begin( fd_gui_t *                  gui,
      82             :                   fd_gui_shred_event_iter_t * iter,
      83             :                   int                         event_time,
      84             :                   long                        after_ns,
      85           0 :                   long                        before_ns ) {
      86           0 :   fd_memset( iter, 0, sizeof(*iter) );
      87           0 :   iter->_gui        = gui;
      88           0 :   iter->_after_ns   = after_ns;
      89           0 :   iter->_before_ns  = before_ns;
      90           0 :   iter->_event_time = event_time;
      91             : 
      92             :   /* Event-time queries widen the scan by +-1 second. The assumption is that
      93             :      event-ordering approximately equals insert-ordering, and in rare
      94             :      cases where there is more than a second of skew in the system we
      95             :      can tolerate missing data.  */
      96           0 :   if( FD_LIKELY( gui->db && gui->hist && after_ns<=before_ns ) ) {
      97           0 :     long margin = event_time ? FD_GUI_HIST_RES_1S_NS : 0L;
      98           0 :     long lo_ns = fd_long_max( 0L, fd_long_sat_sub( after_ns, margin ) );
      99           0 :     long hi_ns = fd_long_min( LONG_MAX-1L, fd_long_sat_add( before_ns, margin ) );
     100           0 :     iter->_hist_active = !fd_gui_hist_range_begin( gui, &iter->_hist_iter, FD_GUI_HIST_SHRED_EVENTS, lo_ns, hi_ns, NULL, NULL );
     101           0 :     iter->_builder_pending = 1;
     102           0 :   }
     103           0 :   return iter;
     104           0 : }
     105             : 
     106             : fd_gui_shred_event_iter_t *
     107             : fd_gui_shred_event_hist_iter_begin( fd_gui_t *                  gui,
     108             :                                     fd_gui_shred_event_iter_t * iter,
     109             :                                     long                        after_ns,
     110           0 :                                     long                        before_ns ) {
     111           0 :   return shred_iter_begin( gui, iter, 1, after_ns, before_ns );
     112           0 : }
     113             : 
     114             : fd_gui_shred_event_iter_t *
     115             : fd_gui_shred_event_iter_begin( fd_gui_t *                  gui,
     116             :                                fd_gui_shred_event_iter_t * iter,
     117             :                                long                        after_ns,
     118           0 :                                long                        before_ns ) {
     119           0 :   return shred_iter_begin( gui, iter, 0, fd_long_max( after_ns, 0L ), fd_long_min( before_ns, LONG_MAX-1L ) );
     120           0 : }
     121             : 
     122             : int
     123           0 : fd_gui_shred_event_iter_next( fd_gui_shred_event_iter_t * iter ) {
     124           0 :   while( iter->_batch || iter->_hist_active || iter->_builder_pending ) {
     125           0 :     if( iter->_batch && iter->_batch_idx<iter->_batch->event_cnt ) {
     126           0 :       batch_next( iter );
     127           0 :       long ts = iter->_event_time ? iter->event.event_time_ns : iter->event.insert_time_ns;
     128           0 :       if( ts>=iter->_after_ns && (iter->_event_time ? ts<iter->_before_ns : ts<=iter->_before_ns) ) return 1;
     129           0 :       continue;
     130           0 :     }
     131           0 :     iter->_batch = NULL;
     132           0 :     iter->_batch_idx = 0UL;
     133           0 :     iter->_data_off  = 0UL;
     134           0 :     if( iter->_hist_active && fd_gui_hist_range_next( &iter->_hist_iter ) ) {
     135           0 :       iter->_batch = iter->_hist_iter.rec;
     136           0 :       continue;
     137           0 :     }
     138           0 :     if( iter->_hist_active ) fd_gui_hist_range_end( &iter->_hist_iter );
     139           0 :     iter->_hist_active = 0;
     140           0 :     if( iter->_builder_pending ) {
     141           0 :       iter->_builder_pending = 0;
     142           0 :       iter->_batch = &iter->_gui->shreds.builder.batch;
     143           0 :     }
     144           0 :   }
     145           0 :   return 0;
     146           0 : }
     147             : 
     148             : void
     149           0 : fd_gui_shred_event_iter_end( fd_gui_shred_event_iter_t * iter ) {
     150           0 :   if( iter->_hist_active ) fd_gui_hist_range_end( &iter->_hist_iter );
     151           0 :   iter->_hist_active = 0;
     152           0 :   iter->_builder_pending = 0;
     153           0 :   iter->_batch = NULL;
     154           0 : }
     155             : 
     156             : static void
     157           0 : batch_flush( fd_gui_t * gui ) {
     158           0 :   fd_gui_shred_batch_t * batch = &gui->shreds.builder.batch;
     159           0 :   if( !batch->event_cnt ) return;
     160           0 :   if( FD_UNLIKELY( fd_gui_hist_ts_append( gui, FD_GUI_HIST_SHRED_EVENTS, batch ) ) ) {
     161           0 :     gui->shreds.dropped_event_cnt += batch->event_cnt;
     162           0 :   }
     163           0 :   batch->event_cnt = 0U;
     164           0 :   batch->data_sz   = 0U;
     165           0 : }
     166             : 
     167             : void
     168             : fd_gui_shred_event_append( fd_gui_t * gui,
     169             :                            ulong      slot,
     170             :                            ulong      idx,
     171             :                            uchar      event,
     172             :                            long       timestamp,
     173           0 :                            long       now ) {
     174           0 :   fd_gui_shred_builder_t * builder = &gui->shreds.builder;
     175           0 :   if( FD_UNLIKELY( !gui->db || !gui->hist || slot>UINT_MAX || idx>USHORT_MAX ||
     176           0 :                   (idx==USHORT_MAX && event!=FD_GUI_SLOT_SHRED_SHRED_SLOT_COMPLETE) ||
     177           0 :                   now<builder->prev.insert_time_ns ) ) {
     178           0 :     gui->shreds.dropped_event_cnt++;
     179           0 :     return;
     180           0 :   }
     181           0 :   fd_gui_shred_batch_t * batch = &builder->batch;
     182           0 :   long window_ns = (long)shred_window( now )*FD_GUI_HIST_RES_1S_NS;
     183           0 :   if( batch->event_cnt && batch->insert_time_ns!=window_ns ) batch_flush( gui );
     184           0 :   batch->insert_time_ns = window_ns;
     185           0 :   fd_gui_shred_event_t rec = { .insert_time_ns=now, .event_time_ns=timestamp, .slot=(uint)slot, .idx=(ushort)idx, .event=event };
     186           0 :   if( FD_UNLIKELY( !batch_append( builder, &rec ) ) ) {
     187           0 :     batch_flush( gui );
     188           0 :     FD_TEST( batch_append( builder, &rec ) );
     189           0 :   }
     190           0 : }
     191             : 
     192             : int
     193             : fd_gui_shred_flush( fd_gui_t * gui,
     194           0 :                     long       now ) {
     195           0 :   if( FD_UNLIKELY( !gui->db || !gui->hist ) ) return 0;
     196           0 :   fd_gui_shred_batch_t const * batch = &gui->shreds.builder.batch;
     197           0 :   if( !batch->event_cnt || shred_window( batch->insert_time_ns )>=shred_window( now ) ) return 0;
     198           0 :   batch_flush( gui );
     199           0 :   return 1;
     200           0 : }
     201             : 
     202             : int
     203             : fd_gui_event_bounds( fd_gui_t * gui,
     204             :                      int        dbi,
     205             :                      long *     lo,
     206           0 :                      long *     hi ) {
     207           0 :   long first;
     208           0 :   long last;
     209           0 :   int have = gui->db && fd_gui_store_ts_live_timestamp_bounds( gui->db, (ulong)dbi, &first, &last );
     210           0 :   if( dbi==FD_GUI_HIST_SHRED_EVENTS && gui->shreds.builder.batch.event_cnt ) {
     211           0 :     long active = gui->shreds.builder.batch.insert_time_ns;
     212           0 :     first = have ? fd_long_min( first, active ) : active;
     213           0 :     last  = have ? fd_long_max( last, active ) : active;
     214           0 :     have  = 1;
     215           0 :   }
     216             : 
     217           0 :   if( have && dbi==FD_GUI_HIST_SHRED_EVENTS ) last = fd_long_sat_add( last, FD_GUI_HIST_RES_1S_NS-1L );
     218           0 :   *lo = have ? fd_long_max( 0L, fd_long_sat_sub( first, FD_GUI_HIST_RES_1S_NS ) ) : LONG_MAX;
     219           0 :   *hi = have ? fd_long_min( LONG_MAX-1L, fd_long_sat_add( last, FD_GUI_HIST_RES_1S_NS+1L ) ) : LONG_MIN;
     220           0 :   return have;
     221           0 : }
     222             : 
     223             : int
     224             : fd_gui_shred_window_is_empty( fd_gui_t * gui,
     225             :                               long       after_ns,
     226           0 :                               long       before_ns ) {
     227           0 :   fd_gui_shred_event_iter_t it[ 1 ];
     228           0 :   fd_gui_shred_event_iter_begin( gui, it, after_ns, before_ns );
     229           0 :   int empty = !fd_gui_shred_event_iter_next( it );
     230           0 :   fd_gui_shred_event_iter_end( it );
     231           0 :   return empty;
     232           0 : }

Generated by: LCOV version 1.14