LCOV - code coverage report
Current view: top level - discof/restore/utils - fd_ssparse.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 375 427 87.8 %
Date: 2026-11-12 04:25:03 Functions: 15 15 100.0 %

          Line data    Source code
       1             : #include "fd_ssparse.h"
       2             : 
       3             : #include "../../../util/log/fd_log.h"
       4             : #include "../../../util/archive/fd_tar.h"
       5             : #include "../../../flamenco/runtime/fd_runtime_const.h"
       6             : #include "../../../flamenco/runtime/fd_system_ids.h"
       7             : 
       8             : #include <stdlib.h>
       9             : #include <errno.h>
      10             : 
      11       50562 : #define FD_SSPARSE_STATE_TAR_HEADER             (0)
      12       32112 : #define FD_SSPARSE_STATE_SCROLL_TAR_HEADER      (1)
      13         648 : #define FD_SSPARSE_STATE_VERSION                (2)
      14         531 : #define FD_SSPARSE_STATE_MANIFEST               (3)
      15        2634 : #define FD_SSPARSE_STATE_ACCOUNT_HEADER         (4)
      16         276 : #define FD_SSPARSE_STATE_ACCOUNT_DATA           (5)
      17         228 : #define FD_SSPARSE_STATE_ACCOUNT_PADDING        (6)
      18         423 : #define FD_SSPARSE_STATE_STATUS_CACHE           (7)
      19         618 : #define FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE (8)
      20             : #define FD_SSPARSE_STATE_ACCOUNT_BATCH          (9)
      21             : 
      22             : fd_ssparse_t *
      23        1554 : fd_ssparse_init( fd_ssparse_t * ssparse ) {
      24        1554 :   memset( ssparse, 0, sizeof(fd_ssparse_t) );
      25        1554 :   ssparse->state = FD_SSPARSE_STATE_TAR_HEADER;
      26        1554 :   return ssparse;
      27        1554 : }
      28             : 
      29             : static int
      30             : parse_tar_header_name( char const * name,
      31             :                        ulong *      id,
      32         135 :                        ulong *      slot ) {
      33         135 :   char name_buf[ FD_TAR_NAME_SZ ];
      34         135 :   fd_memcpy( name_buf, name, FD_TAR_NAME_SZ );
      35         135 :   name_buf[ FD_TAR_NAME_SZ-1 ] = '\0';
      36             : 
      37         135 :   char const * ptr = name_buf;
      38             : 
      39         135 :   if( FD_UNLIKELY( strncmp( ptr, "accounts/", 9UL ) ) ) {
      40           0 :     *id   = ULONG_MAX;
      41           0 :     *slot = ULONG_MAX;
      42           0 :     return -1;
      43           0 :   }
      44             : 
      45         135 :   ptr += 9UL;
      46         135 :   char const * next = strchr( ptr, '.' );
      47         135 :   if( FD_UNLIKELY( !next ) ) {
      48           0 :     *id   = ULONG_MAX;
      49           0 :     *slot = ULONG_MAX;
      50           0 :     return -1;
      51           0 :   }
      52         135 :   errno = 0;
      53         135 :   char * endptr;
      54         135 :   *slot = strtoul( ptr, &endptr, 10 );
      55         135 :   if( FD_UNLIKELY( errno==ERANGE || *endptr!='.' || endptr==ptr ) ) {
      56           6 :     *id   = ULONG_MAX;
      57           6 :     *slot = ULONG_MAX;
      58           6 :     return -1;
      59           6 :   }
      60             : 
      61         135 :   errno = 0;
      62         129 :   ptr = next + 1;
      63         129 :   *id = strtoul( ptr, &endptr, 10 );
      64         129 :   if( FD_UNLIKELY( errno==ERANGE || *endptr!='\0' || endptr==ptr ) ) {
      65           6 :     *id   = ULONG_MAX;
      66           6 :     *slot = ULONG_MAX;
      67           6 :     return -1;
      68           6 :   }
      69             : 
      70         123 :   return 0;
      71         129 : }
      72             : 
      73             : static int
      74             : advance_tar( fd_ssparse_t *                ssparse,
      75             :              uchar const *                 data,
      76             :              ulong                         data_sz,
      77       48540 :              fd_ssparse_advance_result_t * result ) {
      78       48540 :   ulong consume = fd_ulong_min( data_sz, 512UL - ssparse->tar.header_bytes_consumed );
      79       48540 :   if( FD_UNLIKELY( !consume ) ) {
      80           0 :     FD_LOG_WARNING(( "unexpected end of data in tar header, data_sz=%lu, header_bytes_consumed=%lu", data_sz, ssparse->tar.header_bytes_consumed ));
      81           0 :     return FD_SSPARSE_ADVANCE_ERROR;
      82           0 :   }
      83             : 
      84       48540 :   fd_memcpy( ssparse->tar.header+ssparse->tar.header_bytes_consumed, data, consume );
      85       48540 :   ssparse->bytes_consumed            += consume;
      86       48540 :   result->bytes_consumed              = consume;
      87       48540 :   ssparse->tar.header_bytes_consumed += consume;
      88             : 
      89       48540 :   if( FD_UNLIKELY( ssparse->tar.header_bytes_consumed<512UL ) ) return FD_SSPARSE_ADVANCE_AGAIN;
      90             : 
      91         735 :   fd_tar_meta_t const * hdr = (fd_tar_meta_t const *)ssparse->tar.header;
      92         735 :   ssparse->tar.header_bytes_consumed = 0UL;
      93             : 
      94             :   /* "ustar\x00" and "ustar  \x00" (overlaps with version) are both
      95             :      valid values for magic.  These are POSIX ustar and OLDGNU versions
      96             :      respectively. */
      97         735 :   if( FD_UNLIKELY( memcmp( hdr->magic, FD_TAR_MAGIC, FD_TAR_MAGIC_SZ ) ) ) {
      98         192 :     int not_zero = 0;
      99       98496 :     for( ulong i=0UL; i<512UL; i++ ) not_zero |= ssparse->tar.header[ i ];
     100         192 :     if( FD_UNLIKELY( not_zero ) ) {
     101           6 :       FD_LOG_WARNING(( "invalid tar header magic `%." FD_EXPAND_THEN_STRINGIFY(FD_TAR_MAGIC_SZ) "s`", hdr->magic ));
     102           6 :       return FD_SSPARSE_ADVANCE_ERROR;
     103           6 :     }
     104             : 
     105         186 :     if( FD_LIKELY( ssparse->flags.seen_zero_tar_frame ) ) {
     106          90 :       if( FD_UNLIKELY( !ssparse->flags.seen_version || !ssparse->flags.seen_manifest || !ssparse->flags.seen_status_cache ) ) {
     107           9 :         FD_LOG_WARNING(( "unexpected end of file before version or manifest or status cache" ));
     108           9 :         return FD_SSPARSE_ADVANCE_ERROR;
     109           9 :       }
     110             : 
     111          81 :       return FD_SSPARSE_ADVANCE_DONE;
     112          90 :     }
     113             : 
     114          96 :     ssparse->flags.seen_zero_tar_frame = 1;
     115          96 :     return FD_SSPARSE_ADVANCE_AGAIN;
     116         186 :   }
     117             : 
     118         543 :   if( FD_UNLIKELY( ssparse->flags.seen_zero_tar_frame ) ) {
     119           6 :     FD_LOG_WARNING(( "unexpected valid tar header after zero frame" ));
     120           6 :     return FD_SSPARSE_ADVANCE_ERROR;
     121           6 :   }
     122             : 
     123         537 :   ssparse->tar.file_bytes = fd_tar_meta_get_size( hdr );
     124         537 :   if( FD_UNLIKELY( ssparse->tar.file_bytes==ULONG_MAX ) ) {
     125           0 :     FD_LOG_WARNING(( "invalid tar header size %." FD_EXPAND_THEN_STRINGIFY(FD_TAR_SIZE_SZ) "s "
     126           0 :                      "for tar header name %." FD_EXPAND_THEN_STRINGIFY(FD_TAR_NAME_SZ) "s", hdr->size, hdr->name ));
     127           0 :     return FD_SSPARSE_ADVANCE_ERROR;
     128           0 :   }
     129             : 
     130         537 :   if( FD_UNLIKELY( hdr->typeflag==FD_TAR_TYPE_DIR ) ) {
     131           6 :     if( FD_UNLIKELY( ssparse->tar.file_bytes ) ) {
     132           3 :       FD_LOG_WARNING(( "invalid tar directory entry with non-zero size %lu", ssparse->tar.file_bytes ));
     133           3 :       return FD_SSPARSE_ADVANCE_ERROR;
     134           3 :     }
     135           3 :     return FD_SSPARSE_ADVANCE_AGAIN;
     136           6 :   }
     137             : 
     138         531 :   if( FD_UNLIKELY( !fd_tar_meta_is_reg( hdr ) ) ) {
     139           3 :     FD_LOG_WARNING(( "invalid tar header type %d", hdr->typeflag ));
     140           3 :     return FD_SSPARSE_ADVANCE_ERROR;
     141           3 :   }
     142         528 :   if( FD_UNLIKELY( !ssparse->tar.file_bytes ) ) {
     143           3 :     FD_LOG_WARNING(( "invalid tar header size %lu", ssparse->tar.file_bytes ));
     144           3 :     return FD_SSPARSE_ADVANCE_ERROR;
     145           3 :   }
     146             : 
     147             :   /* TODO: Check every header field here for validity? */
     148             : 
     149         525 :   int desired_state;
     150         525 :   if( FD_LIKELY( !strncmp( hdr->name, "version", 7UL ) ) ) {
     151         150 :     desired_state = FD_SSPARSE_STATE_VERSION;
     152         150 :     if( FD_UNLIKELY( ssparse->tar.file_bytes!=5UL ) ) {
     153           6 :       FD_LOG_WARNING(( "invalid version file size %lu", ssparse->tar.file_bytes ));
     154           6 :       return FD_SSPARSE_ADVANCE_ERROR;
     155           6 :     }
     156         375 :   } else if( FD_LIKELY( !strncmp( hdr->name, "accounts/", 9UL ) ) ) {
     157         135 :     ssparse->account.header_bytes_consumed = 0UL;
     158         135 :     desired_state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
     159         135 :     ulong id, slot;
     160         135 :     if( FD_UNLIKELY( -1==parse_tar_header_name( hdr->name, &id, &slot ) ) ) {
     161          12 :       FD_LOG_WARNING(( "invalid account append vec name %." FD_EXPAND_THEN_STRINGIFY(FD_TAR_NAME_SZ) "s", hdr->name ));
     162          12 :       return FD_SSPARSE_ADVANCE_ERROR;
     163          12 :     }
     164         123 :     ssparse->slot = slot;
     165         123 :     ssparse->acc_vec_bytes = ssparse->tar.file_bytes;
     166         240 :   } else if( FD_LIKELY( !strncmp( hdr->name, "snapshots/status_cache", 22UL ) ) ) desired_state = FD_SSPARSE_STATE_STATUS_CACHE;
     167         135 :   else if( FD_LIKELY( !strncmp( hdr->name, "snapshots/", 10UL ) ) ) {
     168         132 :     desired_state = FD_SSPARSE_STATE_MANIFEST;
     169         132 :   } else {
     170           3 :     FD_LOG_WARNING(( "unexpected tar header name `%." FD_EXPAND_THEN_STRINGIFY(FD_TAR_NAME_SZ) "s`", hdr->name ));
     171           3 :     return FD_SSPARSE_ADVANCE_ERROR;
     172           3 :   }
     173             : 
     174         504 :   ssparse->tar.file_bytes_consumed = 0UL;
     175             : 
     176         504 :   switch( desired_state ) {
     177         144 :     case FD_SSPARSE_STATE_VERSION:
     178         144 :       if( FD_UNLIKELY( ssparse->flags.seen_version ) ) {
     179           3 :         FD_LOG_WARNING(( "unexpected duplicate version file" ));
     180           3 :         return FD_SSPARSE_ADVANCE_ERROR;
     181           3 :       }
     182             : 
     183         141 :       ssparse->flags.seen_version = 1;
     184         141 :       ssparse->state = FD_SSPARSE_STATE_VERSION;
     185         141 :       break;
     186         132 :     case FD_SSPARSE_STATE_MANIFEST:
     187         132 :       if( FD_UNLIKELY( ssparse->flags.seen_manifest ) ) {
     188           3 :         FD_LOG_WARNING(( "unexpected duplicate manifest file" ));
     189           3 :         return FD_SSPARSE_ADVANCE_ERROR;
     190           3 :       }
     191             : 
     192         129 :       ssparse->flags.seen_manifest = 1;
     193         129 :       ssparse->state = FD_SSPARSE_STATE_MANIFEST;
     194         129 :       break;
     195         123 :     case FD_SSPARSE_STATE_ACCOUNT_HEADER:
     196         123 :       if( FD_UNLIKELY( !ssparse->flags.seen_manifest ) ) {
     197           3 :         FD_LOG_WARNING(( "unexpected account append vec file before manifest" ));
     198           3 :         return FD_SSPARSE_ADVANCE_ERROR;
     199           3 :       }
     200             : 
     201         120 :       ssparse->account.header_bytes_consumed = 0UL;
     202         120 :       ssparse->state = FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE;
     203         120 :       result->appendvec.slot    = ssparse->slot;
     204         120 :       result->appendvec.data_sz = ssparse->tar.file_bytes;
     205         120 :       return FD_SSPARSE_ADVANCE_APPENDVEC;
     206         105 :     case FD_SSPARSE_STATE_STATUS_CACHE:
     207         105 :       if( FD_UNLIKELY( ssparse->flags.seen_status_cache ) ) {
     208           3 :         FD_LOG_WARNING(( "unexpected status cache file" ));
     209           3 :         return FD_SSPARSE_ADVANCE_ERROR;
     210           3 :       }
     211             : 
     212         102 :       ssparse->flags.seen_status_cache = 1;
     213         102 :       ssparse->state = FD_SSPARSE_STATE_STATUS_CACHE;
     214         102 :       break;
     215           0 :     default:
     216           0 :       FD_LOG_ERR(( "unexpected tar header desired state %d", desired_state ));
     217           0 :       break;
     218         504 :   }
     219             : 
     220         372 :   return FD_SSPARSE_ADVANCE_AGAIN;
     221         504 : }
     222             : 
     223             : static int
     224             : advance_version( fd_ssparse_t *                ssparse,
     225             :                  uchar const *                 data,
     226             :                  ulong                         data_sz,
     227         213 :                  fd_ssparse_advance_result_t * result ) {
     228         213 :   ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
     229         213 :   if( FD_UNLIKELY( !consume ) ) {
     230           0 :     FD_LOG_WARNING(( "unexpected end of data while parsing version file, data_sz=%lu, file_bytes_consumed=%lu, file_bytes=%lu", data_sz, ssparse->tar.file_bytes_consumed, ssparse->tar.file_bytes ));
     231           0 :     return FD_SSPARSE_ADVANCE_ERROR;
     232           0 :   }
     233             : 
     234         213 :   fd_memcpy( ssparse->version+ssparse->tar.file_bytes_consumed, data, consume );
     235             : 
     236         213 :   ssparse->tar.file_bytes_consumed += consume;
     237         213 :   ssparse->bytes_consumed          += consume;
     238         213 :   result->bytes_consumed            = consume;
     239             : 
     240         213 :   if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) return FD_SSPARSE_ADVANCE_AGAIN;
     241             : 
     242         138 :   FD_TEST( ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes );
     243         138 :   FD_TEST( ssparse->tar.file_bytes_consumed==5UL );
     244             : 
     245         138 :   if( FD_UNLIKELY( memcmp( ssparse->version, "1.2.0", 5UL ) ) ) {
     246           3 :     FD_LOG_WARNING(( "invalid version file %.*s", 5, ssparse->version ));
     247           3 :     return FD_SSPARSE_ADVANCE_ERROR;
     248           3 :   }
     249             : 
     250         135 :   ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
     251         135 :   return FD_SSPARSE_ADVANCE_AGAIN;
     252         138 : }
     253             : 
     254             : static int
     255             : advance_status_cache( fd_ssparse_t *                 ssparse,
     256             :                        uchar const *                 data,
     257             :                        ulong                         data_sz,
     258         111 :                        fd_ssparse_advance_result_t * result ) {
     259         111 :   ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
     260         111 :   if( FD_UNLIKELY( !consume ) ) {
     261           0 :     FD_LOG_WARNING(( "unexpected end of data while parsing status cache, data_sz=%lu, file_bytes_consumed=%lu, file_bytes=%lu", data_sz, ssparse->tar.file_bytes_consumed, ssparse->tar.file_bytes ));
     262           0 :     return FD_SSPARSE_ADVANCE_ERROR;
     263           0 :   }
     264             : 
     265         111 :   ssparse->tar.file_bytes_consumed += consume;
     266         111 :   ssparse->bytes_consumed          += consume;
     267             : 
     268         111 :   result->bytes_consumed            = consume;
     269         111 :   result->status_cache.data         = data;
     270         111 :   result->status_cache.data_sz      = consume;
     271         111 :   result->status_cache.done         = ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes;
     272             : 
     273         111 :   if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) {
     274          12 :     return FD_SSPARSE_ADVANCE_STATUS_CACHE;
     275          12 :   }
     276          99 :   else { /* ssparse->tar.file_bytes_consumed==ssparse->tar.file_bytes */
     277             :     /* finished parsing status cache */
     278          99 :     ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
     279          99 :     return FD_SSPARSE_ADVANCE_STATUS_CACHE;
     280          99 :   }
     281         111 : }
     282             : 
     283             : static int
     284             : advance_manifest( fd_ssparse_t *                ssparse,
     285             :                   uchar const *                 data,
     286             :                   ulong                         data_sz,
     287         138 :                   fd_ssparse_advance_result_t * result ) {
     288         138 :   ulong consume = fd_ulong_min( data_sz, ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed );
     289         138 :   if( FD_UNLIKELY( !consume ) ) {
     290           0 :     FD_LOG_WARNING(( "unexpected end of data while parsing manifest, data_sz=%lu, file_bytes_consumed=%lu, file_bytes=%lu", data_sz, ssparse->tar.file_bytes_consumed, ssparse->tar.file_bytes ));
     291           0 :     return FD_SSPARSE_ADVANCE_ERROR;
     292           0 :   }
     293             : 
     294         138 :   ssparse->tar.file_bytes_consumed += consume;
     295         138 :   ssparse->bytes_consumed          += consume;
     296             : 
     297         138 :   result->bytes_consumed           = consume;
     298         138 :   result->manifest.data            = data;
     299         138 :   result->manifest.data_sz         = consume;
     300             : 
     301         138 :   if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) return FD_SSPARSE_ADVANCE_MANIFEST;
     302             : 
     303         126 :   ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
     304         126 :   return FD_SSPARSE_ADVANCE_MANIFEST_DONE;
     305         138 : }
     306             : 
     307             : static int
     308             : advance_next_tar( fd_ssparse_t *               ssparse,
     309             :                  uchar const *                 data,
     310             :                  ulong                         data_sz,
     311       31644 :                  fd_ssparse_advance_result_t * result ) {
     312       31644 :   (void)data;
     313             :   /* skip padding */
     314       31644 :   ulong bytes_remaining    = fd_ulong_align_up( ssparse->bytes_consumed, 512UL ) - ssparse->bytes_consumed;
     315       31644 :   ulong pad_sz             = bytes_remaining;
     316       31644 :         pad_sz             = fd_ulong_min( pad_sz, data_sz );
     317       31644 :   if( FD_UNLIKELY( !pad_sz && bytes_remaining ) ) {
     318           0 :     FD_LOG_WARNING(( "unexpected end of data while parsing tar header padding, data_sz=%lu, bytes_consumed=%lu, bytes_remaining=%lu", data_sz, ssparse->bytes_consumed, bytes_remaining ));
     319           0 :     return FD_SSPARSE_ADVANCE_ERROR;
     320           0 :   }
     321             : 
     322       31644 :   ssparse->bytes_consumed += pad_sz;
     323       31644 :   result->bytes_consumed   = pad_sz;
     324       31644 :   bytes_remaining         -= pad_sz;
     325             : 
     326       31644 :   if( FD_LIKELY( !bytes_remaining ) ) ssparse->state = FD_SSPARSE_STATE_TAR_HEADER;
     327       31644 :   return FD_SSPARSE_ADVANCE_AGAIN;
     328       31644 : }
     329             : 
     330             : static int
     331             : advance_account_batch( fd_ssparse_t *                ssparse,
     332             :                        uchar const *                 data,
     333             :                        ulong                         data_sz,
     334          60 :                        fd_ssparse_advance_result_t * result ) {
     335             :   /* Cannot create a batch unless the parser is aligned to an account. */
     336          60 :   if( FD_UNLIKELY( ssparse->account.header_bytes_consumed ) ) return FD_SSPARSE_ADVANCE_AGAIN;
     337             : 
     338             :   /* Each account is at least 136 bytes large.  Don't attempt to create
     339             :      a batch unless at least 4 accounts fit. */
     340          60 :   ulong avail = fd_ulong_min( data_sz, ssparse->acc_vec_bytes - ssparse->tar.file_bytes_consumed );
     341          60 :   if( FD_UNLIKELY( avail<(4*136UL) ) ) return FD_SSPARSE_ADVANCE_AGAIN;
     342             : 
     343             :   /* Prefetch data to reduce cache misses in hot path. */
     344         321 :   for( ulong i=0UL; i<fd_ulong_min( avail, 4096UL ); i+=64UL ) {
     345         303 :     __builtin_prefetch( (uchar *)data+i, 0, 0 );
     346         303 :   }
     347             : 
     348             :   /* Skip over accounts until we reached EOF or batch is full */
     349          18 :   result->account_batch.batch_cnt = 0;
     350          18 :   ulong off = 0UL;
     351         135 :   for( ulong idx=0UL; idx<FD_SSPARSE_ACC_BATCH_MAX && off+136UL<=avail; idx++ ) {
     352         120 :     uchar const * acc_hdr     = (uchar *)data+off;
     353             : 
     354             :     /* We want ConfigProgram accounts to go through the slow path,
     355             :        since they are published from there to consumers for monitoring. */
     356         120 :     if( FD_UNLIKELY( !memcmp( acc_hdr+64UL, fd_solana_config_program_id.key, sizeof(fd_hash_t) ) ) ) {
     357           3 :       if( FD_UNLIKELY( idx==0UL  ) ) return FD_SSPARSE_ADVANCE_AGAIN; /* At the front of the batch, abort */
     358           0 :       else                           break; /* otherwise, break early.  */
     359           3 :     }
     360             : 
     361         117 :     ulong         acc_data_sz = fd_ulong_load_8_fast( acc_hdr+8 );
     362         117 :     if( FD_UNLIKELY( acc_data_sz>FD_RUNTIME_ACC_SZ_MAX ) ) {
     363           0 :       FD_LOG_WARNING(( "invalid account data size %lu", acc_data_sz ));
     364           0 :       return FD_SSPARSE_ADVANCE_ERROR;
     365           0 :     }
     366             :     /* acc_hdr[96] is the executable flag (uchar), must be 0 or 1 */
     367         117 :     if( FD_UNLIKELY( acc_hdr[ 96UL ]>1 ) ) {
     368           0 :       FD_LOG_WARNING(( "invalid account header executable %u", acc_hdr[ 96UL ] ));
     369           0 :       return FD_SSPARSE_ADVANCE_ERROR;
     370           0 :     }
     371         117 :     ulong         next_off    = off+136UL+acc_data_sz;
     372         117 :     ulong         pad_sz      = fd_ulong_align_up( ssparse->tar.file_bytes_consumed+next_off, 8UL ) -
     373         117 :                                                  ( ssparse->tar.file_bytes_consumed+next_off );
     374         117 :     next_off += pad_sz;
     375         117 :     if( FD_UNLIKELY( next_off>avail ) ) break; /* account is fragmented */
     376         117 :     result->account_batch.batch_cnt        = idx+1UL;
     377         117 :     result->account_batch.batch[ idx ]     = acc_hdr;
     378         117 :     ssparse->account.header_bytes_consumed = 136UL;
     379         117 :     ssparse->account.data_bytes_consumed   = acc_data_sz;
     380         117 :     ssparse->account.data_len              = acc_data_sz;
     381         117 :     off = next_off;
     382         117 :   }
     383             : 
     384             :   /* Skip if no batches to process. */
     385          15 :   if( FD_UNLIKELY( !result->account_batch.batch_cnt ) ) {
     386           0 :     return FD_SSPARSE_ADVANCE_AGAIN;
     387           0 :   }
     388             : 
     389          15 :   ssparse->tar.file_bytes_consumed += off;
     390          15 :   ssparse->bytes_consumed          += off;
     391          15 :   result->bytes_consumed            = off;
     392             : 
     393             :   /* reset state */
     394             : 
     395          15 :   ssparse->state = FD_SSPARSE_STATE_ACCOUNT_PADDING;
     396             : 
     397          15 :   result->account_batch.slot = ssparse->slot;
     398             : 
     399          15 :   return FD_SSPARSE_ADVANCE_ACCOUNT_BATCH;
     400          15 : }
     401             : 
     402             : static int
     403             : advance_account_header( fd_ssparse_t *                ssparse,
     404             :                         uchar const *                 data,
     405             :                         ulong                         data_sz,
     406        2241 :                         fd_ssparse_advance_result_t * result ) {
     407        2241 :   ulong consume = fd_ulong_min( 136UL-ssparse->account.header_bytes_consumed, fd_ulong_min( data_sz, ssparse->acc_vec_bytes-ssparse->tar.file_bytes_consumed ) );
     408             : 
     409        2241 :   if( FD_UNLIKELY( !consume ) ) {
     410          15 :     if( FD_LIKELY( ssparse->tar.file_bytes_consumed==ssparse->acc_vec_bytes ) ) {
     411          15 :       ssparse->state = FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE;
     412          15 :       return FD_SSPARSE_ADVANCE_AGAIN;
     413          15 :     } else {
     414           0 :       FD_LOG_WARNING(( "unexpected end of data while advancing account header, data_sz=%lu, file_bytes_consumed=%lu, acc_vec_bytes=%lu", data_sz, ssparse->tar.file_bytes_consumed, ssparse->acc_vec_bytes ));
     415           0 :       return FD_SSPARSE_ADVANCE_ERROR;
     416           0 :     }
     417          15 :   }
     418             : 
     419        2226 :   if( FD_UNLIKELY( consume<136UL ) ) {
     420        2127 :     fd_memcpy( ssparse->account.header+ssparse->account.header_bytes_consumed, data, consume );
     421        2127 :   } else if( ssparse->batch_enabled ) {
     422             :     /* fast path */
     423          60 :     int res = advance_account_batch( ssparse, data, data_sz, result );
     424          60 :     if( res==FD_SSPARSE_ADVANCE_ACCOUNT_BATCH ) return res;
     425             :     /* fall through and continue processing account header */
     426          60 :   }
     427             : 
     428        2211 :   ssparse->account.header_bytes_consumed += consume;
     429        2211 :   ssparse->tar.file_bytes_consumed       += consume;
     430        2211 :   ssparse->bytes_consumed                += consume;
     431        2211 :   result->bytes_consumed                  = consume;
     432             : 
     433        2211 :   if( FD_UNLIKELY( ssparse->account.header_bytes_consumed<136UL ) ) return FD_SSPARSE_ADVANCE_AGAIN;
     434             : 
     435         105 :   uchar const * hdr = ssparse->account.header;
     436         105 :   if( FD_LIKELY( consume==136UL ) ) hdr = data;
     437             : 
     438         105 :   result->account_header.data_len = fd_ulong_load_8_fast( hdr+8UL );
     439         105 :   if( FD_UNLIKELY( result->account_header.data_len>FD_RUNTIME_ACC_SZ_MAX ) ) {
     440           3 :     FD_LOG_WARNING(( "invalid account header data length %lu", result->account_header.data_len ));
     441           3 :     return FD_SSPARSE_ADVANCE_ERROR;
     442           3 :   }
     443             : 
     444         102 :   result->account_header.pubkey     = hdr+16UL;
     445         102 :   result->account_header.lamports   = fd_ulong_load_8_fast( hdr+48UL );
     446         102 :   result->account_header.rent_epoch = fd_ulong_load_8_fast( hdr+56UL );
     447         102 :   result->account_header.owner      = hdr+64UL;
     448         102 :   result->account_header.executable = hdr[ 96UL ];
     449         102 :   if( FD_UNLIKELY( result->account_header.executable>1 ) ) {
     450           3 :     char pubkey_str[ FD_BASE58_ENCODED_32_SZ ];
     451           3 :     fd_base58_encode_32( result->account_header.pubkey, NULL, pubkey_str );
     452           3 :     FD_LOG_WARNING(( "invalid account header executable %d for account %s", result->account_header.executable, pubkey_str ));
     453           3 :     return FD_SSPARSE_ADVANCE_ERROR;
     454           3 :   }
     455          99 :   result->account_header.hash       = hdr+104UL;
     456          99 :   result->account_header.slot       = ssparse->slot;
     457             : 
     458          99 :   ssparse->account.data_len            = result->account_header.data_len;
     459          99 :   ssparse->account.data_bytes_consumed = 0UL;
     460          99 :   ssparse->state = FD_SSPARSE_STATE_ACCOUNT_DATA;
     461             : 
     462          99 :   return FD_SSPARSE_ADVANCE_ACCOUNT_HEADER;
     463         102 : }
     464             : 
     465             : static int
     466             : advance_account_data( fd_ssparse_t *                ssparse,
     467             :                       uchar const *                 data,
     468             :                       ulong                         data_sz,
     469         177 :                       fd_ssparse_advance_result_t * result ) {
     470         177 :   if( FD_UNLIKELY( ssparse->account.data_bytes_consumed==ssparse->account.data_len ) ) {
     471          48 :     ssparse->state = FD_SSPARSE_STATE_ACCOUNT_PADDING;
     472          48 :     return FD_SSPARSE_ADVANCE_AGAIN;
     473          48 :   }
     474             : 
     475         129 :   ulong consume = fd_ulong_min( data_sz, ssparse->acc_vec_bytes-ssparse->tar.file_bytes_consumed );
     476         129 :   if( FD_UNLIKELY( !consume ) ) {
     477           3 :     FD_LOG_WARNING(( "account data extends beyond append vec size" ));
     478           3 :     return FD_SSPARSE_ADVANCE_ERROR;
     479           3 :   }
     480             : 
     481         126 :   consume = fd_ulong_min( consume, ssparse->account.data_len-ssparse->account.data_bytes_consumed );
     482         126 :   if( FD_UNLIKELY( !consume ) ) {
     483           0 :     FD_LOG_WARNING(( "unexpected end of data while parsing account data, data_sz=%lu, data_bytes_consumed=%lu, data_len=%lu", data_sz, ssparse->account.data_bytes_consumed, ssparse->account.data_len ));
     484           0 :     return FD_SSPARSE_ADVANCE_ERROR;
     485           0 :   }
     486             : 
     487         126 :   ssparse->tar.file_bytes_consumed     += consume;
     488         126 :   ssparse->bytes_consumed              += consume;
     489         126 :   ssparse->account.data_bytes_consumed += consume;
     490         126 :   result->bytes_consumed                = consume;
     491             : 
     492         126 :   result->account_data.data_sz  = consume;
     493         126 :   result->account_data.data     = data;
     494             : 
     495         126 :   FD_TEST( ssparse->account.data_bytes_consumed<=ssparse->account.data_len );
     496         126 :   if( FD_LIKELY( ssparse->account.data_bytes_consumed==ssparse->account.data_len ) ) {
     497          45 :     ssparse->state = FD_SSPARSE_STATE_ACCOUNT_PADDING;
     498          45 :   }
     499             : 
     500         126 :   return FD_SSPARSE_ADVANCE_ACCOUNT_DATA;
     501         126 : }
     502             : 
     503             : static int
     504             : advance_account_padding( fd_ssparse_t *                ssparse,
     505             :                          uchar const *                 data,
     506             :                          ulong                         data_sz,
     507         120 :                          fd_ssparse_advance_result_t * result ) {
     508         120 :   (void)data;
     509             : 
     510         120 :   ulong pad_sz = fd_ulong_align_up( ssparse->tar.file_bytes_consumed, 8UL ) - ssparse->tar.file_bytes_consumed;
     511         120 :         pad_sz = fd_ulong_min( pad_sz, ssparse->acc_vec_bytes - ssparse->tar.file_bytes_consumed );
     512         120 :   if( FD_UNLIKELY( !pad_sz ) ) {
     513          90 :     if( FD_LIKELY( ssparse->tar.file_bytes_consumed==ssparse->acc_vec_bytes ) ) ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
     514          12 :     else                                                                        ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
     515             : 
     516          90 :     ssparse->account.header_bytes_consumed = 0UL;
     517          90 :     return FD_SSPARSE_ADVANCE_AGAIN;
     518          90 :   }
     519             : 
     520          30 :   ulong consume = fd_ulong_min( data_sz, pad_sz );
     521          30 :   if( FD_UNLIKELY( !consume ) ) {
     522           0 :     FD_LOG_WARNING(( "unexpected end of data while parsing account padding, data_sz=%lu, file_bytes_consumed=%lu, acc_vec_bytes=%lu", data_sz, ssparse->tar.file_bytes_consumed, ssparse->acc_vec_bytes ));
     523           0 :     return FD_SSPARSE_ADVANCE_ERROR;
     524           0 :   }
     525             : 
     526          30 :   ssparse->tar.file_bytes_consumed += consume;
     527          30 :   ssparse->bytes_consumed          += consume;
     528          30 :   result->bytes_consumed            = consume;
     529             : 
     530          30 :   ulong remaining = fd_ulong_align_up( ssparse->tar.file_bytes_consumed, 8UL ) - ssparse->tar.file_bytes_consumed;
     531          30 :   if( FD_LIKELY( !remaining ) ) {
     532          18 :     ssparse->account.header_bytes_consumed = 0UL;
     533          18 :     ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
     534          18 :   }
     535          30 :   return FD_SSPARSE_ADVANCE_AGAIN;
     536          30 : }
     537             : 
     538             : static int
     539             : advance_account_garbage( fd_ssparse_t *                ssparse,
     540             :                          uchar const *                 data,
     541             :                          ulong                         data_sz,
     542         483 :                          fd_ssparse_advance_result_t * result ) {
     543         483 :   (void)data;
     544         483 :   ulong rem = ssparse->tar.file_bytes-ssparse->tar.file_bytes_consumed;
     545         483 :   if( FD_UNLIKELY( !rem ) ) {
     546          15 :     ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
     547          15 :     return FD_SSPARSE_ADVANCE_AGAIN;
     548          15 :   }
     549             : 
     550         468 :   if( FD_UNLIKELY( !data_sz ) ) {
     551           0 :     FD_LOG_WARNING(( "unexpected end of data while parsing append vec garbage, data_sz=%lu, remaining_bytes=%lu", data_sz, rem ));
     552           0 :     return FD_SSPARSE_ADVANCE_ERROR;
     553           0 :   }
     554             : 
     555         468 :   ulong consume = fd_ulong_min( data_sz, rem );
     556         468 :   ssparse->tar.file_bytes_consumed += consume;
     557         468 :   ssparse->bytes_consumed          += consume;
     558         468 :   result->bytes_consumed            = consume;
     559             : 
     560         468 :   if( FD_LIKELY( ssparse->tar.file_bytes_consumed<ssparse->tar.file_bytes ) ) return FD_SSPARSE_ADVANCE_AGAIN;
     561             : 
     562          15 :   ssparse->state = FD_SSPARSE_STATE_SCROLL_TAR_HEADER;
     563          15 :   return FD_SSPARSE_ADVANCE_AGAIN;
     564         468 : }
     565             : 
     566             : int
     567             : fd_ssparse_advance( fd_ssparse_t *                ssparse,
     568             :                     uchar const *                 data,
     569             :                     ulong                         data_sz,
     570       83667 :                     fd_ssparse_advance_result_t * result ) {
     571       83667 :   result->bytes_consumed = 0UL;
     572             : 
     573       83667 :   switch( ssparse->state ) {
     574       48540 :     case FD_SSPARSE_STATE_TAR_HEADER:             return advance_tar( ssparse, data, data_sz, result );
     575       31644 :     case FD_SSPARSE_STATE_SCROLL_TAR_HEADER:      return advance_next_tar( ssparse, data, data_sz, result );
     576         213 :     case FD_SSPARSE_STATE_VERSION:                return advance_version( ssparse, data, data_sz, result );
     577         138 :     case FD_SSPARSE_STATE_MANIFEST:               return advance_manifest( ssparse, data, data_sz, result );
     578        2241 :     case FD_SSPARSE_STATE_ACCOUNT_HEADER:         return advance_account_header( ssparse, data, data_sz, result );
     579         177 :     case FD_SSPARSE_STATE_ACCOUNT_DATA:           return advance_account_data( ssparse, data, data_sz, result );
     580         120 :     case FD_SSPARSE_STATE_ACCOUNT_PADDING:        return advance_account_padding( ssparse, data, data_sz, result );
     581         111 :     case FD_SSPARSE_STATE_STATUS_CACHE:           return advance_status_cache( ssparse, data, data_sz, result );
     582         483 :     case FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE: return advance_account_garbage( ssparse, data, data_sz, result );
     583           0 :     default: FD_LOG_ERR(( "invalid state %d", ssparse->state ));
     584       83667 :   }
     585       83667 : }
     586             : 
     587             : void
     588             : fd_ssparse_batch_enable( fd_ssparse_t * ssparse,
     589        1368 :                          int            enabled ) {
     590        1368 :   ssparse->batch_enabled = !!enabled;
     591        1368 : }
     592             : 
     593             : void
     594         105 : fd_ssparse_appendvec_parse( fd_ssparse_t * ssparse ) {
     595         105 :   FD_TEST( ssparse->state==FD_SSPARSE_STATE_SCROLL_ACCOUNT_GARBAGE );
     596         105 :   FD_TEST( !ssparse->tar.file_bytes_consumed                       );
     597         105 :   ssparse->state = FD_SSPARSE_STATE_ACCOUNT_HEADER;
     598         105 : }

Generated by: LCOV version 1.14