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

Generated by: LCOV version 1.14