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: FD_LOG_NOTICE(( "STATE_STATUS_BLOCKHASH: %s", FD_BASE58_ENC_32_ALLOCA( parser->entry->blockhash ) )); break; 106 : case STATE_CACHE_STATUS_LEN: FD_LOG_NOTICE(( "STATE_CACHE_STATUS_LEN: %lu", parser->cache_status_len )); break; 107 : default: break; 108 : } 109 : } 110 : #endif 111 : 112 : static inline int 113 810 : state_validate( fd_slot_delta_parser_t * parser ) { 114 810 : switch( parser->state ) { 115 36 : case STATE_SLOT_DELTAS_LEN: 116 36 : if( FD_UNLIKELY( parser->len>FD_SLOT_DELTA_MAX_ENTRIES ) ) { 117 3 : return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_TOO_MANY_ENTRIES; 118 3 : } 119 33 : break; 120 57 : case STATE_SLOT_DELTA_SLOT: { 121 57 : ulong slot_idx = slot_set_idx_query_const( parser->slot_set, &parser->entry->slot, ULONG_MAX, parser->slot_pool ); 122 57 : if( FD_UNLIKELY( slot_idx!=ULONG_MAX ) ) return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_SLOT_HASH_MULTIPLE_ENTRIES; 123 : 124 54 : if( FD_UNLIKELY( parser->slot_pool_ele_cnt>=FD_SLOT_DELTA_MAX_ENTRIES ) ) { 125 0 : return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_TOO_MANY_ENTRIES; 126 0 : } 127 : 128 54 : fd_slot_entry_t * slot_entry = &parser->slot_pool[ parser->slot_pool_ele_cnt++ ]; 129 54 : slot_entry->slot = parser->entry->slot; 130 54 : slot_set_ele_insert( parser->slot_set, slot_entry, parser->slot_pool ); 131 54 : break; 132 54 : } 133 54 : case STATE_SLOT_DELTA_IS_ROOT: 134 54 : if( FD_UNLIKELY( !parser->is_root) ) { 135 3 : return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_SLOT_IS_NOT_ROOT; 136 3 : } 137 51 : break; 138 663 : default: break; 139 810 : } 140 : 141 801 : return 0; 142 810 : } 143 : 144 : static inline void 145 132 : loop( fd_slot_delta_parser_t * parser ) { 146 132 : if( FD_LIKELY( parser->cache_status_len ) ) { 147 66 : parser->state = STATE_CACHE_STATUS_KEY_SLICE; 148 66 : } else if( FD_LIKELY( parser->slot_delta_status_len ) ) { 149 15 : parser->state = STATE_STATUS_BLOCKHASH; 150 51 : } else if( FD_LIKELY( parser->len ) ) { 151 24 : parser->state = STATE_SLOT_DELTA_SLOT; 152 27 : } else { 153 27 : parser->state = STATE_DONE; 154 27 : } 155 132 : } 156 : 157 : static inline void 158 108 : result_loop( fd_slot_delta_parser_t * parser ) { 159 108 : parser->entry_avail = 1; 160 108 : loop( parser ); 161 108 : } 162 : 163 : static inline void 164 801 : state_process( fd_slot_delta_parser_t * parser ) { 165 801 : FD_TEST( parser->state!=STATE_DONE ); 166 : 167 801 : switch( parser->state ) { 168 33 : case STATE_SLOT_DELTAS_LEN: 169 33 : parser->state = STATE_SLOT_DELTA_SLOT; 170 33 : break; 171 54 : case STATE_SLOT_DELTA_SLOT: 172 54 : parser->state = STATE_SLOT_DELTA_IS_ROOT; 173 54 : parser->len--; 174 54 : break; 175 51 : case STATE_SLOT_DELTA_IS_ROOT: 176 51 : parser->state = STATE_SLOT_DELTA_STATUS_LEN; 177 51 : break; 178 51 : case STATE_SLOT_DELTA_STATUS_LEN: 179 51 : if( FD_UNLIKELY( !parser->slot_delta_status_len ) ) loop( parser ); 180 42 : else parser->state = STATE_STATUS_BLOCKHASH; 181 51 : break; 182 57 : case STATE_STATUS_BLOCKHASH: 183 57 : parser->state = STATE_STATUS_TXN_IDX; 184 57 : parser->slot_delta_status_len--; 185 57 : break; 186 57 : case STATE_STATUS_TXN_IDX: 187 57 : parser->state = STATE_CACHE_STATUS_LEN; 188 57 : parser->group_avail = 1; 189 57 : break; 190 57 : case STATE_CACHE_STATUS_LEN: 191 57 : if( FD_UNLIKELY( !parser->cache_status_len ) ) loop( parser ); 192 42 : else parser->state = STATE_CACHE_STATUS_KEY_SLICE; 193 57 : break; 194 108 : case STATE_CACHE_STATUS_KEY_SLICE: 195 108 : parser->state = STATE_CACHE_STATUS_RESULT; 196 108 : parser->cache_status_len--; 197 108 : break; 198 108 : case STATE_CACHE_STATUS_RESULT: 199 108 : if( FD_LIKELY( !parser->error_discriminant ) ) { 200 27 : parser->entry->result = 0; 201 27 : result_loop( parser ); 202 27 : } 203 81 : else { 204 81 : parser->state = STATE_CACHE_STATUS_RESULT_ERR; 205 81 : } 206 108 : break; 207 81 : case STATE_CACHE_STATUS_RESULT_ERR: 208 81 : if( FD_UNLIKELY( parser->error_discriminant==8U ) ) { 209 45 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_IDX; 210 45 : } else if( FD_UNLIKELY( parser->error_discriminant==30U || parser->error_discriminant==31U || parser->error_discriminant==35U ) ) { 211 18 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_IDX; 212 18 : } else { 213 18 : parser->entry->result = (uchar)parser->error_discriminant; 214 18 : result_loop( parser ); 215 18 : } 216 81 : break; 217 45 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_IDX: 218 45 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR; 219 45 : break; 220 45 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR: 221 45 : if( FD_UNLIKELY( parser->error_discriminant==25U ) ) { 222 18 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM; 223 27 : } else if( FD_UNLIKELY( parser->error_discriminant==44U ) ) { 224 9 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_LEN; 225 18 : } else { 226 18 : parser->entry->result = (uchar)parser->error_discriminant; 227 18 : result_loop( parser ); 228 18 : } 229 45 : break; 230 18 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM: 231 18 : parser->entry->result = (uchar)parser->error_discriminant; 232 18 : result_loop( parser ); 233 18 : break; 234 9 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_LEN: 235 9 : parser->state = STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_ERR; 236 9 : break; 237 9 : case STATE_CACHE_STATUS_RESULT_ERR_INSTR_ERR_CUSTOM_BORSH_ERR: 238 9 : parser->entry->result = (uchar)parser->error_discriminant; 239 9 : result_loop( parser ); 240 9 : break; 241 18 : case STATE_CACHE_STATUS_RESULT_ERR_IDX: 242 18 : parser->entry->result = (uchar)parser->error; 243 18 : result_loop( parser ); 244 18 : break; 245 0 : default: FD_LOG_ERR(( "unknown state %d", parser->state )); 246 801 : } 247 801 : } 248 : 249 : FD_FN_CONST ulong 250 6 : fd_slot_delta_parser_align( void ) { 251 6 : return fd_ulong_max( alignof(fd_slot_delta_parser_t), slot_set_align() ); 252 6 : } 253 : 254 : FD_FN_CONST ulong 255 3 : fd_slot_delta_parser_footprint( void ) { 256 3 : ulong l = FD_LAYOUT_INIT; 257 3 : l = FD_LAYOUT_APPEND( l, alignof(fd_slot_delta_parser_t), sizeof(fd_slot_delta_parser_t) ); 258 3 : l = FD_LAYOUT_APPEND( l, slot_pool_align(), slot_pool_footprint( FD_SLOT_DELTA_MAX_ENTRIES ) ); 259 3 : l = FD_LAYOUT_APPEND( l, slot_set_align(), slot_set_footprint( FD_SLOT_DELTA_PARSER_SLOT_SET_MAX_ENTRIES ) ); 260 3 : return FD_LAYOUT_FINI( l, alignof(fd_slot_delta_parser_t) ); 261 3 : } 262 : 263 : void * 264 3 : fd_slot_delta_parser_new( void * shmem ) { 265 3 : if( FD_UNLIKELY( !shmem ) ) { 266 0 : FD_LOG_WARNING(( "NULL shmem" )); 267 0 : return NULL; 268 0 : } 269 : 270 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_slot_delta_parser_align() ) ) ) { 271 0 : FD_LOG_WARNING(( "unaligned shmem" )); 272 0 : return NULL; 273 0 : } 274 : 275 3 : FD_SCRATCH_ALLOC_INIT( l, shmem ); 276 3 : fd_slot_delta_parser_t * parser = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_slot_delta_parser_t), sizeof(fd_slot_delta_parser_t) ); 277 3 : void * slot_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, slot_pool_align(), slot_pool_footprint( FD_SLOT_DELTA_MAX_ENTRIES ) ); 278 3 : void * slot_set_mem = FD_SCRATCH_ALLOC_APPEND( l, slot_set_align(), slot_set_footprint( FD_SLOT_DELTA_PARSER_SLOT_SET_MAX_ENTRIES ) ); 279 : 280 3 : parser->slot_pool = slot_pool_join( slot_pool_new( slot_pool_mem, FD_SLOT_DELTA_MAX_ENTRIES ) ); 281 3 : FD_TEST( parser->slot_pool ); 282 : 283 3 : parser->slot_set = slot_set_join( slot_set_new( slot_set_mem, FD_SLOT_DELTA_PARSER_SLOT_SET_MAX_ENTRIES, 1UL ) ); 284 3 : FD_TEST( parser->slot_set ); 285 : 286 903 : for( ulong i=0UL; i<slot_pool_max( parser->slot_pool ); i++ ) { 287 900 : fd_slot_entry_t * slot_entry = &parser->slot_pool[ i ]; 288 900 : slot_entry->slot = ULONG_MAX; 289 900 : } 290 : 291 3 : parser->entry_avail = 0; 292 3 : parser->group_avail = 0; 293 3 : parser->slot_pool_ele_cnt = 0UL; 294 3 : parser->state = STATE_DONE; 295 : 296 3 : return parser; 297 3 : } 298 : 299 : fd_slot_delta_parser_t * 300 3 : fd_slot_delta_parser_join( void * shmem ) { 301 3 : return shmem; 302 3 : } 303 : 304 : void * 305 3 : fd_slot_delta_parser_leave( fd_slot_delta_parser_t * parser ) { 306 3 : return (void *)parser; 307 3 : } 308 : 309 : void * 310 3 : fd_slot_delta_parser_delete( void * shmem ) { 311 3 : return shmem; 312 3 : } 313 : 314 : void 315 36 : fd_slot_delta_parser_init( fd_slot_delta_parser_t * parser ) { 316 36 : parser->state = STATE_SLOT_DELTAS_LEN; 317 36 : parser->len = 0UL; 318 36 : parser->is_root = 0; 319 : 320 36 : parser->slot_delta_status_len = 0UL; 321 36 : parser->cache_status_len = 0UL; 322 36 : parser->borsh_io_error_len = 0UL; 323 36 : parser->error_discriminant = 0U; 324 : 325 90 : for( ulong i=0UL; i<parser->slot_pool_ele_cnt; i++ ) { 326 54 : fd_slot_entry_t * slot_entry = &parser->slot_pool[ i ]; 327 54 : slot_set_ele_remove_fast( parser->slot_set, slot_entry, parser->slot_pool ); 328 54 : slot_entry->slot = ULONG_MAX; 329 54 : } 330 : 331 36 : parser->slot_pool_ele_cnt = 0UL; 332 : 333 36 : parser->dst = state_dst( parser ); 334 36 : parser->dst_sz = state_size( parser ); 335 36 : parser->dst_cur = 0UL; 336 : 337 36 : parser->entry_avail = 0; 338 36 : parser->group_avail = 0; 339 36 : } 340 : 341 : int 342 : fd_slot_delta_parser_consume( fd_slot_delta_parser_t * parser, 343 : uchar const * buf, 344 : ulong bufsz, 345 180 : fd_slot_delta_parser_advance_result_t * result ) { 346 180 : uchar const * data = buf; 347 180 : ulong data_sz = bufsz; 348 816 : while( data_sz ) { 349 810 : if( FD_UNLIKELY( parser->state==STATE_DONE ) ) break; 350 : 351 810 : ulong consume = fd_ulong_min( data_sz, parser->dst_sz-parser->dst_cur ); 352 : 353 810 : if( FD_LIKELY( parser->dst && consume ) ) { 354 756 : memcpy( parser->dst+parser->dst_cur, data, consume ); 355 756 : } 356 : 357 810 : parser->dst_cur += consume; 358 810 : data += consume; 359 810 : data_sz -= consume; 360 : 361 : #if SLOT_DELTA_PARSER_DEBUG 362 : state_log( parser ); 363 : #endif 364 : 365 810 : if( FD_LIKELY( parser->dst_cur==parser->dst_sz ) ) { 366 810 : int err = state_validate( parser ); 367 810 : if( FD_UNLIKELY( err ) ) { 368 9 : result->bytes_consumed = (ulong)(data - buf); 369 9 : return err; 370 9 : } 371 : 372 801 : state_process( parser ); 373 : 374 801 : parser->dst = state_dst( parser ); 375 801 : parser->dst_sz = state_size( parser ); 376 801 : parser->dst_cur = 0UL; 377 : 378 801 : if( FD_LIKELY( parser->group_avail ) ) { 379 57 : FD_TEST( parser->entry_avail==0 ); 380 57 : parser->group_avail = 0; 381 57 : result->entry = NULL; 382 57 : result->group.blockhash = parser->entry->blockhash; 383 57 : result->group.txnhash_offset = parser->txnhash_offset; 384 57 : result->bytes_consumed = (ulong)(data - buf); 385 57 : return FD_SLOT_DELTA_PARSER_ADVANCE_GROUP; 386 744 : } else if( FD_LIKELY( parser->entry_avail ) ) { 387 108 : FD_TEST( parser->group_avail==0 ); 388 108 : parser->entry_avail = 0; 389 108 : result->entry = parser->entry; 390 108 : result->bytes_consumed = (ulong)(data - buf); 391 108 : return FD_SLOT_DELTA_PARSER_ADVANCE_ENTRY; 392 108 : } 393 801 : } 394 810 : } 395 : 396 6 : if( FD_UNLIKELY( data_sz ) ) { 397 0 : FD_LOG_WARNING(( "excess data in buffer" )); 398 0 : result->bytes_consumed = (ulong)(data - buf); 399 0 : return FD_SLOT_DELTA_PARSER_ADVANCE_ERROR_EXCESS_DATA_IN_BUFFER; 400 0 : } 401 : 402 6 : result->bytes_consumed = (ulong)(data - buf); 403 6 : return parser->state==STATE_DONE ? FD_SLOT_DELTA_PARSER_ADVANCE_DONE : FD_SLOT_DELTA_PARSER_ADVANCE_AGAIN; 404 6 : }