Line data Source code
1 : #include "fd_slot_delta_parser.h" 2 : 3 : #define SLOT_DELTA_PARSER_DEBUG 0 4 : 5 177 : #define STATE_SLOT_DELTAS_LEN ( 0) 6 282 : #define STATE_SLOT_DELTA_SLOT ( 1) 7 267 : #define STATE_SLOT_DELTA_IS_ROOT ( 2) 8 204 : #define STATE_SLOT_DELTA_STATUS_LEN ( 3) 9 228 : #define STATE_STATUS_BLOCKHASH ( 4) 10 228 : #define STATE_STATUS_TXN_IDX ( 5) 11 228 : #define STATE_CACHE_STATUS_LEN ( 6) 12 432 : #define STATE_CACHE_STATUS_KEY_SLICE ( 7) 13 432 : #define STATE_CACHE_STATUS_RESULT ( 8) 14 324 : #define STATE_CACHE_STATUS_RESULT_ERR ( 9) 15 180 : #define STATE_CACHE_STATUS_RESULT_ERR_INSTR_IDX (10) 16 180 : #define STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR (11) 17 72 : #define STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM (12) 18 36 : #define STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_LEN (13) 19 36 : #define STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_ERR (14) 20 72 : #define STATE_CACHE_STATUS_RESULT_ERR_IDX (15) 21 90 : #define STATE_DONE (16) 22 : 23 3 : #define FD_SLOT_DELTA_PARSER_SLOT_SET_MAX_ENTRIES (512UL) 24 : 25 : struct fd_slot_delta_parser_private { 26 : int state; /* parser state machine */ 27 : int entry_avail; /* whether a parsed entry is available */ 28 : int group_avail; /* whether a parsed group is available */ 29 : 30 : uchar * dst; /* where to store the next parsed value */ 31 : ulong dst_cur; /* offset into dst */ 32 : ulong dst_sz; /* size of dst */ 33 : 34 : ulong len; /* number of slot delta entries */ 35 : int is_root; /* whether the current slot delta entry is rooted */ 36 : ulong txnhash_offset; /* offset into the txncache for the current slot delta entry */ 37 : ulong slot_delta_status_len; /* number of blockhashes in the slot delta entry */ 38 : ulong cache_status_len; /* number of txns associated with the blockhash */ 39 : ulong borsh_io_error_len; /* used to parse a variable len borsh_io_error string */ 40 : uint error_discriminant; /* stores the error discriminant of a txn result */ 41 : uchar error; /* stores the error code of a txn result */ 42 : 43 : fd_slot_entry_t * slot_pool; /* pool backing a slot hashset */ 44 : slot_set_t * slot_set; /* slot hash set to detect duplicate slots */ 45 : ulong slot_pool_ele_cnt; /* count of slots in pool */ 46 : fd_sstxncache_entry_t entry[1]; /* parsed slot delta entry */ 47 : }; 48 : 49 : static inline ulong 50 837 : state_size( fd_slot_delta_parser_t * parser ) { 51 837 : switch( parser->state ) { 52 36 : case STATE_SLOT_DELTAS_LEN: return sizeof(ulong); 53 57 : case STATE_SLOT_DELTA_SLOT: return sizeof(ulong); 54 54 : case STATE_SLOT_DELTA_IS_ROOT: return sizeof(uchar); 55 51 : case STATE_SLOT_DELTA_STATUS_LEN: return sizeof(ulong); 56 57 : case STATE_STATUS_BLOCKHASH: return 32UL; 57 57 : case STATE_STATUS_TXN_IDX: return sizeof(ulong); 58 57 : case STATE_CACHE_STATUS_LEN: return sizeof(ulong); 59 108 : case STATE_CACHE_STATUS_KEY_SLICE: return 20UL; 60 108 : case STATE_CACHE_STATUS_RESULT: return sizeof(uint); 61 81 : case STATE_CACHE_STATUS_RESULT_ERR: return sizeof(uint); 62 45 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_IDX: return sizeof(uchar); 63 45 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR: return sizeof(uint); 64 18 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM: return sizeof(uint); 65 9 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_LEN: return sizeof(ulong); 66 9 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_ERR: return parser->borsh_io_error_len; 67 18 : case STATE_CACHE_STATUS_RESULT_ERR_IDX: return sizeof(uchar); 68 27 : case STATE_DONE: return 0UL; 69 0 : default: FD_LOG_ERR(( "unknown state %d", parser->state )); 70 837 : } 71 837 : } 72 : 73 : static inline uchar * 74 837 : state_dst( fd_slot_delta_parser_t * parser ) { 75 837 : switch( parser->state ) { 76 36 : case STATE_SLOT_DELTAS_LEN: return (uchar*)&parser->len; 77 57 : case STATE_SLOT_DELTA_SLOT: return (uchar*)&parser->entry->slot; 78 54 : case STATE_SLOT_DELTA_IS_ROOT: return (uchar*)&parser->is_root; 79 51 : case STATE_SLOT_DELTA_STATUS_LEN: return (uchar*)&parser->slot_delta_status_len; 80 57 : case STATE_STATUS_BLOCKHASH: return parser->entry->blockhash; 81 57 : case STATE_STATUS_TXN_IDX: return (uchar*)&parser->txnhash_offset; 82 57 : case STATE_CACHE_STATUS_LEN: return (uchar*)&parser->cache_status_len; 83 108 : case STATE_CACHE_STATUS_KEY_SLICE: return parser->entry->txnhash; 84 108 : case STATE_CACHE_STATUS_RESULT: return (uchar*)&parser->error_discriminant; 85 81 : case STATE_CACHE_STATUS_RESULT_ERR: return (uchar*)&parser->error_discriminant; 86 45 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_IDX: return NULL; 87 45 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR: return (uchar*)&parser->error_discriminant; 88 18 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM: return (uchar*)&parser->error_discriminant; 89 9 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_LEN: return (uchar*)&parser->borsh_io_error_len; 90 9 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_ERR: return NULL; 91 18 : case STATE_CACHE_STATUS_RESULT_ERR_IDX: return (uchar*)&parser->error; 92 27 : case STATE_DONE: return NULL; 93 0 : default: FD_LOG_ERR(( "unknown state %d", parser->state )); 94 837 : } 95 837 : } 96 : 97 : #if SLOT_DELTA_PARSER_DEBUG 98 : static inline void 99 : state_log( fd_slot_delta_parser_t * parser ) { 100 : switch( parser->state ) { 101 : case STATE_SLOT_DELTAS_LEN: FD_LOG_NOTICE(( "STATE_SLOT_DELTAS_LEN: %lu", parser->len )); break; 102 : case STATE_SLOT_DELTA_SLOT: FD_LOG_NOTICE(( "STATE_SLOT_DELTA_SLOT: %lu", parser->entry->slot )); break; 103 : case STATE_SLOT_DELTA_IS_ROOT: FD_LOG_NOTICE(( "STATE_SLOT_DELTA_IS_ROOT: %d", parser->is_root )); break; 104 : case STATE_SLOT_DELTA_STATUS_LEN: FD_LOG_NOTICE(( "STATE_SLOT_DELTA_STATUS_LEN: %lu", parser->slot_delta_status_len )); break; 105 : case STATE_STATUS_BLOCKHASH: { 106 : FD_BASE58_ENCODE_32_BYTES( parser->entry->blockhash, blockhash_b58 ); 107 : /* */FD_LOG_NOTICE(( "STATE_STATUS_BLOCKHASH: %s", blockhash_b58 )); 108 : break; 109 : } 110 : case STATE_CACHE_STATUS_LEN: FD_LOG_NOTICE(( "STATE_CACHE_STATUS_LEN: %lu", parser->cache_status_len )); break; 111 : default: break; 112 : } 113 : } 114 : #endif 115 : 116 : static inline int 117 810 : state_validate( fd_slot_delta_parser_t * parser ) { 118 810 : switch( parser->state ) { 119 36 : case STATE_SLOT_DELTAS_LEN: 120 36 : if( FD_UNLIKELY( parser->len>FD_SLOT_DELTA_MAX_ENTRIES ) ) { 121 3 : return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_TOO_MANY_ENTRIES; 122 3 : } 123 33 : break; 124 57 : case STATE_SLOT_DELTA_SLOT: { 125 57 : ulong slot_idx = slot_set_idx_query_const( parser->slot_set, &parser->entry->slot, ULONG_MAX, parser->slot_pool ); 126 57 : if( FD_UNLIKELY( slot_idx!=ULONG_MAX ) ) return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_SLOT_HASH_MULTIPLE_ENTRIES; 127 : 128 54 : if( FD_UNLIKELY( parser->slot_pool_ele_cnt>=FD_SLOT_DELTA_MAX_ENTRIES ) ) { 129 0 : return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_TOO_MANY_ENTRIES; 130 0 : } 131 : 132 54 : fd_slot_entry_t * slot_entry = &parser->slot_pool[ parser->slot_pool_ele_cnt++ ]; 133 54 : slot_entry->slot = parser->entry->slot; 134 54 : slot_set_ele_insert( parser->slot_set, slot_entry, parser->slot_pool ); 135 54 : break; 136 54 : } 137 54 : case STATE_SLOT_DELTA_IS_ROOT: 138 54 : if( FD_UNLIKELY( !parser->is_root) ) { 139 3 : return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_SLOT_IS_NOT_ROOT; 140 3 : } 141 51 : break; 142 663 : default: break; 143 810 : } 144 : 145 801 : return 0; 146 810 : } 147 : 148 : static inline void 149 132 : loop( fd_slot_delta_parser_t * parser ) { 150 132 : if( FD_LIKELY( parser->cache_status_len ) ) { 151 66 : parser->state = STATE_CACHE_STATUS_KEY_SLICE; 152 66 : } else if( FD_LIKELY( parser->slot_delta_status_len ) ) { 153 15 : parser->state = STATE_STATUS_BLOCKHASH; 154 51 : } else if( FD_LIKELY( parser->len ) ) { 155 24 : parser->state = STATE_SLOT_DELTA_SLOT; 156 27 : } else { 157 27 : parser->state = STATE_DONE; 158 27 : } 159 132 : } 160 : 161 : static inline void 162 108 : result_loop( fd_slot_delta_parser_t * parser ) { 163 108 : parser->entry_avail = 1; 164 108 : loop( parser ); 165 108 : } 166 : 167 : static inline void 168 801 : state_process( fd_slot_delta_parser_t * parser ) { 169 801 : FD_TEST( parser->state!=STATE_DONE ); 170 : 171 801 : switch( parser->state ) { 172 33 : case STATE_SLOT_DELTAS_LEN: 173 33 : parser->state = STATE_SLOT_DELTA_SLOT; 174 33 : break; 175 54 : case STATE_SLOT_DELTA_SLOT: 176 54 : parser->state = STATE_SLOT_DELTA_IS_ROOT; 177 54 : parser->len--; 178 54 : break; 179 51 : case STATE_SLOT_DELTA_IS_ROOT: 180 51 : parser->state = STATE_SLOT_DELTA_STATUS_LEN; 181 51 : break; 182 51 : case STATE_SLOT_DELTA_STATUS_LEN: 183 51 : if( FD_UNLIKELY( !parser->slot_delta_status_len ) ) loop( parser ); 184 42 : else parser->state = STATE_STATUS_BLOCKHASH; 185 51 : break; 186 57 : case STATE_STATUS_BLOCKHASH: 187 57 : parser->state = STATE_STATUS_TXN_IDX; 188 57 : parser->slot_delta_status_len--; 189 57 : break; 190 57 : case STATE_STATUS_TXN_IDX: 191 57 : parser->state = STATE_CACHE_STATUS_LEN; 192 57 : parser->group_avail = 1; 193 57 : break; 194 57 : case STATE_CACHE_STATUS_LEN: 195 57 : if( FD_UNLIKELY( !parser->cache_status_len ) ) loop( parser ); 196 42 : else parser->state = STATE_CACHE_STATUS_KEY_SLICE; 197 57 : break; 198 108 : case STATE_CACHE_STATUS_KEY_SLICE: 199 108 : parser->state = STATE_CACHE_STATUS_RESULT; 200 108 : parser->cache_status_len--; 201 108 : break; 202 108 : case STATE_CACHE_STATUS_RESULT: 203 108 : if( FD_LIKELY( !parser->error_discriminant ) ) { 204 27 : parser->entry->result = 0; 205 27 : result_loop( parser ); 206 27 : } 207 81 : else { 208 81 : parser->state = STATE_CACHE_STATUS_RESULT_ERR; 209 81 : } 210 108 : break; 211 81 : case STATE_CACHE_STATUS_RESULT_ERR: 212 81 : if( FD_UNLIKELY( parser->error_discriminant==8U ) ) { 213 45 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_IDX; 214 45 : } else if( FD_UNLIKELY( parser->error_discriminant==30U || parser->error_discriminant==31U || parser->error_discriminant==35U ) ) { 215 18 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_IDX; 216 18 : } else { 217 18 : parser->entry->result = (uchar)parser->error_discriminant; 218 18 : result_loop( parser ); 219 18 : } 220 81 : break; 221 45 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_IDX: 222 45 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR; 223 45 : break; 224 45 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR: 225 45 : if( FD_UNLIKELY( parser->error_discriminant==25U ) ) { 226 18 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM; 227 27 : } else if( FD_UNLIKELY( parser->error_discriminant==44U ) ) { 228 9 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_LEN; 229 18 : } else { 230 18 : parser->entry->result = (uchar)parser->error_discriminant; 231 18 : result_loop( parser ); 232 18 : } 233 45 : break; 234 18 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM: 235 18 : parser->entry->result = (uchar)parser->error_discriminant; 236 18 : result_loop( parser ); 237 18 : break; 238 9 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_LEN: 239 9 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_ERR; 240 9 : break; 241 9 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_ERR: 242 9 : parser->entry->result = (uchar)parser->error_discriminant; 243 9 : result_loop( parser ); 244 9 : break; 245 18 : case STATE_CACHE_STATUS_RESULT_ERR_IDX: 246 18 : parser->entry->result = (uchar)parser->error; 247 18 : result_loop( parser ); 248 18 : break; 249 0 : default: FD_LOG_ERR(( "unknown state %d", parser->state )); 250 801 : } 251 801 : } 252 : 253 : FD_FN_CONST ulong 254 6 : fd_slot_delta_parser_align( void ) { 255 6 : return fd_ulong_max( alignof(fd_slot_delta_parser_t), slot_set_align() ); 256 6 : } 257 : 258 : FD_FN_CONST ulong 259 3 : fd_slot_delta_parser_footprint( void ) { 260 3 : ulong l = FD_LAYOUT_INIT; 261 3 : l = FD_LAYOUT_APPEND( l, alignof(fd_slot_delta_parser_t), sizeof(fd_slot_delta_parser_t) ); 262 3 : l = FD_LAYOUT_APPEND( l, slot_pool_align(), slot_pool_footprint( FD_SLOT_DELTA_MAX_ENTRIES ) ); 263 3 : l = FD_LAYOUT_APPEND( l, slot_set_align(), slot_set_footprint( FD_SLOT_DELTA_PARSER_SLOT_SET_MAX_ENTRIES ) ); 264 3 : return FD_LAYOUT_FINI( l, alignof(fd_slot_delta_parser_t) ); 265 3 : } 266 : 267 : void * 268 3 : fd_slot_delta_parser_new( void * shmem ) { 269 3 : if( FD_UNLIKELY( !shmem ) ) { 270 0 : FD_LOG_WARNING(( "NULL shmem" )); 271 0 : return NULL; 272 0 : } 273 : 274 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_slot_delta_parser_align() ) ) ) { 275 0 : FD_LOG_WARNING(( "unaligned shmem" )); 276 0 : return NULL; 277 0 : } 278 : 279 3 : FD_SCRATCH_ALLOC_INIT( l, shmem ); 280 3 : fd_slot_delta_parser_t * parser = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_slot_delta_parser_t), sizeof(fd_slot_delta_parser_t) ); 281 3 : void * slot_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, slot_pool_align(), slot_pool_footprint( FD_SLOT_DELTA_MAX_ENTRIES ) ); 282 3 : void * slot_set_mem = FD_SCRATCH_ALLOC_APPEND( l, slot_set_align(), slot_set_footprint( FD_SLOT_DELTA_PARSER_SLOT_SET_MAX_ENTRIES ) ); 283 : 284 3 : parser->slot_pool = slot_pool_join( slot_pool_new( slot_pool_mem, FD_SLOT_DELTA_MAX_ENTRIES ) ); 285 3 : FD_TEST( parser->slot_pool ); 286 : 287 3 : parser->slot_set = slot_set_join( slot_set_new( slot_set_mem, FD_SLOT_DELTA_PARSER_SLOT_SET_MAX_ENTRIES, 1UL ) ); 288 3 : FD_TEST( parser->slot_set ); 289 : 290 903 : for( ulong i=0UL; i<slot_pool_max( parser->slot_pool ); i++ ) { 291 900 : fd_slot_entry_t * slot_entry = &parser->slot_pool[ i ]; 292 900 : slot_entry->slot = ULONG_MAX; 293 900 : } 294 : 295 3 : parser->entry_avail = 0; 296 3 : parser->group_avail = 0; 297 3 : parser->slot_pool_ele_cnt = 0UL; 298 3 : parser->state = STATE_DONE; 299 : 300 3 : return parser; 301 3 : } 302 : 303 : fd_slot_delta_parser_t * 304 3 : fd_slot_delta_parser_join( void * shmem ) { 305 3 : return shmem; 306 3 : } 307 : 308 : void * 309 3 : fd_slot_delta_parser_leave( fd_slot_delta_parser_t * parser ) { 310 3 : return (void *)parser; 311 3 : } 312 : 313 : void * 314 3 : fd_slot_delta_parser_delete( void * shmem ) { 315 3 : return shmem; 316 3 : } 317 : 318 : void 319 36 : fd_slot_delta_parser_init( fd_slot_delta_parser_t * parser ) { 320 36 : parser->state = STATE_SLOT_DELTAS_LEN; 321 36 : parser->len = 0UL; 322 36 : parser->is_root = 0; 323 : 324 36 : parser->slot_delta_status_len = 0UL; 325 36 : parser->cache_status_len = 0UL; 326 36 : parser->borsh_io_error_len = 0UL; 327 36 : parser->error_discriminant = 0U; 328 : 329 90 : for( ulong i=0UL; i<parser->slot_pool_ele_cnt; i++ ) { 330 54 : fd_slot_entry_t * slot_entry = &parser->slot_pool[ i ]; 331 54 : slot_set_ele_remove_fast( parser->slot_set, slot_entry, parser->slot_pool ); 332 54 : slot_entry->slot = ULONG_MAX; 333 54 : } 334 : 335 36 : parser->slot_pool_ele_cnt = 0UL; 336 : 337 36 : parser->dst = state_dst( parser ); 338 36 : parser->dst_sz = state_size( parser ); 339 36 : parser->dst_cur = 0UL; 340 : 341 36 : parser->entry_avail = 0; 342 36 : parser->group_avail = 0; 343 36 : } 344 : 345 : int 346 : fd_slot_delta_parser_consume( fd_slot_delta_parser_t * parser, 347 : uchar const * buf, 348 : ulong bufsz, 349 180 : fd_slot_delta_parser_advance_result_t * result ) { 350 180 : uchar const * data = buf; 351 180 : ulong data_sz = bufsz; 352 816 : while( data_sz ) { 353 810 : if( FD_UNLIKELY( parser->state==STATE_DONE ) ) break; 354 : 355 810 : ulong consume = fd_ulong_min( data_sz, parser->dst_sz-parser->dst_cur ); 356 : 357 810 : if( FD_LIKELY( parser->dst && consume ) ) { 358 756 : memcpy( parser->dst+parser->dst_cur, data, consume ); 359 756 : } 360 : 361 810 : parser->dst_cur += consume; 362 810 : data += consume; 363 810 : data_sz -= consume; 364 : 365 : #if SLOT_DELTA_PARSER_DEBUG 366 : state_log( parser ); 367 : #endif 368 : 369 810 : if( FD_LIKELY( parser->dst_cur==parser->dst_sz ) ) { 370 810 : int err = state_validate( parser ); 371 810 : if( FD_UNLIKELY( err ) ) { 372 9 : result->bytes_consumed = (ulong)(data - buf); 373 9 : return err; 374 9 : } 375 : 376 801 : state_process( parser ); 377 : 378 801 : parser->dst = state_dst( parser ); 379 801 : parser->dst_sz = state_size( parser ); 380 801 : parser->dst_cur = 0UL; 381 : 382 801 : if( FD_LIKELY( parser->group_avail ) ) { 383 57 : FD_TEST( parser->entry_avail==0 ); 384 57 : parser->group_avail = 0; 385 57 : result->entry = NULL; 386 57 : result->group.blockhash = parser->entry->blockhash; 387 57 : result->group.txnhash_offset = parser->txnhash_offset; 388 57 : result->bytes_consumed = (ulong)(data - buf); 389 57 : return FD_SLOT_DELTA_PARSER_ADVANCE_GROUP; 390 744 : } else if( FD_LIKELY( parser->entry_avail ) ) { 391 108 : FD_TEST( parser->group_avail==0 ); 392 108 : parser->entry_avail = 0; 393 108 : result->entry = parser->entry; 394 108 : result->bytes_consumed = (ulong)(data - buf); 395 108 : return FD_SLOT_DELTA_PARSER_ADVANCE_ENTRY; 396 108 : } 397 801 : } 398 810 : } 399 : 400 6 : if( FD_UNLIKELY( data_sz ) ) { 401 0 : FD_LOG_WARNING(( "excess data in buffer" )); 402 0 : result->bytes_consumed = (ulong)(data - buf); 403 0 : return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_EXCESS_DATA_IN_BUFFER; 404 0 : } 405 : 406 6 : result->bytes_consumed = (ulong)(data - buf); 407 6 : return parser->state==STATE_DONE ? FD_SLOT_DELTA_PARSER_ADVANCE_DONE : FD_SLOT_DELTA_PARSER_ADVANCE_AGAIN; 408 6 : }