Line data Source code
1 : #include "fd_zstd_frame.h" 2 : 3 : #include "../../../util/bits/fd_bits.h" 4 : #include "../../../util/log/fd_log.h" 5 : 6 : #define ZSTD_STATIC_LINKING_ONLY 7 : #include <zstd.h> 8 : 9 : /* Different states of the ZSTD frame parser state machine */ 10 0 : #define FD_ZSTD_FRAME_STATE_HEADER (0U) 11 0 : #define FD_ZSTD_FRAME_STATE_BLOCK_HEADER (1U) 12 0 : #define FD_ZSTD_FRAME_STATE_PAYLOAD (2U) 13 0 : #define FD_ZSTD_FRAME_STATE_SKIPPABLE (3U) 14 0 : #define FD_ZSTD_FRAME_STATE_END (4U) 15 0 : #define FD_ZSTD_FRAME_STATE_ERR (5U) 16 : 17 : /* Different block types 18 : https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#blocks */ 19 : #define FD_ZSTD_FRAME_BLOCK_TYPE_RAW (0U) 20 0 : #define FD_ZSTD_FRAME_BLOCK_TYPE_RLE (1U) 21 : #define FD_ZSTD_FRAME_BLOCK_TYPE_COMPRESSED (2U) 22 : #define FD_ZSTD_FRAME_BLOCK_TYPE_RESERVED (3U) 23 : 24 0 : #define FAIL_IF( c, frame, consumed, off ) do { \ 25 0 : if( FD_UNLIKELY( c ) ) { \ 26 0 : (frame)->state = FD_ZSTD_FRAME_STATE_ERR; \ 27 0 : *(consumed) = (off); \ 28 0 : return FD_ZSTD_FRAME_ERR; \ 29 0 : } \ 30 0 : } while( 0 ) 31 : 32 : int 33 : fd_zstd_frame_advance( fd_zstd_frame_t * frame, 34 : void const * data, 35 : ulong data_sz, 36 0 : ulong * consumed ) { 37 0 : uchar const * bytes = (uchar const *)data; 38 0 : ulong off = 0UL; 39 : 40 0 : for(;;) { 41 0 : switch( frame->state ) { 42 0 : case FD_ZSTD_FRAME_STATE_HEADER: { 43 0 : ZSTD_frameHeader header[1]; 44 : 45 0 : for(;;) { 46 0 : ulong rc = ZSTD_getFrameHeader( header, frame->header, frame->header_sz ); 47 0 : FAIL_IF( ZSTD_isError( rc ), frame, consumed, off ); 48 : 49 0 : if( !rc ) break; 50 : 51 : /* Library invariants for rc > 0 */ 52 0 : FD_TEST( rc<=FD_ZSTD_FRAME_HEADER_MAX ); 53 0 : FD_TEST( frame->header_sz<rc ); 54 : 55 0 : if( off==data_sz ) { 56 0 : *consumed = off; 57 0 : return FD_ZSTD_FRAME_MORE; 58 0 : } 59 : 60 0 : ulong need = rc-frame->header_sz; 61 0 : ulong copy_sz = fd_ulong_min( need, data_sz-off ); 62 : 63 0 : fd_memcpy( frame->header+frame->header_sz, bytes+off, copy_sz ); 64 0 : frame->header_sz += (uchar)copy_sz; 65 0 : off += copy_sz; 66 0 : } 67 : 68 0 : if( header->frameType==ZSTD_skippableFrame ) { 69 0 : frame->bytes_remaining = (ulong)header->frameContentSize; 70 0 : frame->state = FD_ZSTD_FRAME_STATE_SKIPPABLE; 71 0 : } else { 72 0 : frame->block_sz_max = header->blockSizeMax; 73 0 : frame->checksum_sz = header->checksumFlag ? 4U : 0U; 74 0 : frame->state = FD_ZSTD_FRAME_STATE_BLOCK_HEADER; 75 0 : } 76 0 : continue; 77 0 : } 78 : 79 0 : case FD_ZSTD_FRAME_STATE_BLOCK_HEADER: { 80 0 : ulong need = FD_ZSTD_FRAME_BLOCK_HEADER_SZ-frame->block_header_sz; 81 0 : ulong copy_sz = fd_ulong_min( need, data_sz-off ); 82 : 83 0 : fd_memcpy( frame->block_header+frame->block_header_sz, bytes+off, copy_sz ); 84 0 : frame->block_header_sz += (uchar)copy_sz; 85 0 : off += copy_sz; 86 : 87 0 : if( FD_UNLIKELY( copy_sz<need ) ) { 88 0 : *consumed = data_sz; 89 0 : return FD_ZSTD_FRAME_MORE; 90 0 : } 91 : 92 0 : uint block_header = fd_uint_load_3( frame->block_header ); 93 0 : uint last_block = block_header & 1U; /* bit 0 */ 94 0 : uint block_type = (block_header>>1) & 3U; /* bits 1-2 */ 95 0 : uint block_sz = block_header>>3; /* bits 3-23 */ 96 : 97 0 : FAIL_IF( block_type==FD_ZSTD_FRAME_BLOCK_TYPE_RESERVED || block_sz>frame->block_sz_max, frame, consumed, off ); 98 : 99 0 : frame->block_header_sz = 0U; 100 0 : frame->last_block = (uchar)last_block; 101 0 : frame->bytes_remaining = block_type==FD_ZSTD_FRAME_BLOCK_TYPE_RLE ? 1UL : (ulong)block_sz; 102 0 : frame->state = FD_ZSTD_FRAME_STATE_PAYLOAD; 103 : 104 0 : if( last_block ) frame->bytes_remaining += frame->checksum_sz; 105 : 106 0 : continue; 107 0 : } 108 : 109 0 : case FD_ZSTD_FRAME_STATE_PAYLOAD: 110 0 : case FD_ZSTD_FRAME_STATE_SKIPPABLE: { 111 0 : ulong skip = fd_ulong_min( frame->bytes_remaining, data_sz-off ); 112 0 : frame->bytes_remaining -= skip; 113 0 : off += skip; 114 : 115 0 : if( frame->bytes_remaining ) { 116 0 : *consumed = data_sz; 117 0 : return FD_ZSTD_FRAME_MORE; 118 0 : } 119 : 120 0 : if( frame->state==FD_ZSTD_FRAME_STATE_PAYLOAD && !frame->last_block ) { 121 0 : frame->state = FD_ZSTD_FRAME_STATE_BLOCK_HEADER; 122 0 : continue; 123 0 : } 124 : 125 0 : frame->state = FD_ZSTD_FRAME_STATE_END; 126 0 : *consumed = off; 127 0 : return FD_ZSTD_FRAME_END; 128 0 : } 129 : 130 0 : case FD_ZSTD_FRAME_STATE_END: 131 0 : *consumed = 0UL; 132 0 : return FD_ZSTD_FRAME_END; 133 : 134 0 : default: 135 0 : *consumed = 0UL; 136 0 : return FD_ZSTD_FRAME_ERR; 137 0 : } 138 0 : } 139 0 : }