Line data Source code
1 : #ifndef HEADER_fd_src_ballet_txn_fd_compact_u16_h 2 : #define HEADER_fd_src_ballet_txn_fd_compact_u16_h 3 : 4 : /* This file declares some utility methods for decoding compact-u16, a variable 5 : length encoding format for unsigned 16 bit numbers that Solana transactions 6 : use in the wireline format. 7 : The format is documented at 8 : https://docs.solana.com/developing/programming-model/transactions#compact-u16-format 9 : but briefly: 10 : If the 16 bit number has (big endian bits) ponmlkji hgfedcba 11 : [ 0x00, 0x80) (implies [h..p] = 0) -> 0gfedcba (1 byte ) 12 : [ 0x80, 0x4000) (implies [o..p] = 0) -> 1gfedcba 0nmlkjih (2 bytes) 13 : [0x4000, 0x10000) -> 1gfedcba 1nmlkjih 000000po (3 bytes) 14 : Numbers must be encoded with the minimal number of bytes possible. 15 : 16 : This encoding format is filled with sadness, some of which this API 17 : reflects. To limit the sadness, this header is for internal use in fd_txn 18 : and not exported more widely. */ 19 : 20 : #include "../fd_ballet_base.h" 21 : 22 : 23 : FD_PROTOTYPES_BEGIN 24 : /* fd_cu16_dec_fixed: Reads a compact-u16 whose width is known. High 25 : performance API that does no error checking, and as such, it's designed to 26 : be used with fd_cu16_dec_sz, which performs all necessary validation to 27 : ensure this is safe. buf points to the first byte of the encoded value. 28 : sz in {1, 2, 3}. Reads exactly sz bytes. */ 29 : static inline ushort 30 : fd_cu16_dec_fixed( uchar const * buf, 31 10603653594 : ulong sz ) { 32 : /* Branch-free hardware friendly format that is slower on a CPU. If you 33 : switch to this version, be sure to update the documentation to note that 34 : it reads more than sz bytes. */ 35 : /* 36 : ulong w = (ulong)*(uint *)buf; 37 : ulong b0 = (w & 0x00007FUL); 38 : ulong b1 = (w & 0x007F00UL)>>1UL; 39 : ulong b2 = (w & 0xFF0000UL)>>2UL; 40 : ulong m0 = (ulong)(((long)(1UL-sz))>>63); *//* Maps [0,1] to 0; [2,3] to 0xFF..FF */ 41 : /* ulong m01 = (ulong)(((long)(2UL-sz))>>63); *//* Maps [0,2] to 0; [3,3] to 0xFF..FF */ 42 : /* return (ushort)((b0) | (b1 & m0) | (b2 & m01)); */ 43 : 44 : /* This version is actually substantially faster */ 45 10603653594 : FD_DCHECK_CRIT( (1<=sz) & (sz<=3), "invalid compact-u16 size" ); 46 10603653594 : if( FD_LIKELY( sz==1 ) ) 47 7357158123 : return (ushort)buf[0]; 48 3246495471 : if( FD_LIKELY( sz==2 ) ) 49 3208599267 : return (ushort)((ulong)(buf[0]&0x7F) + (((ulong)buf[1])<<7)); 50 37896204 : return (ushort)((ulong)(buf[0]&0x7F) + (((ulong)buf[1]&0x7F)<<7) + (((ulong)buf[2])<<14)); 51 3246495471 : } 52 : 53 : /*fd_cu16_dec_sz: Returns the number of bytes in the compact-u16. Also 54 : validates that it is a legally-encoded compact-u16 and that it is stored in 55 : no more than bytes_avail bytes. buf points to the first byte of the encoded 56 : value. Result will be in {0, 1, 2, 3}, where 0 indicates validation failed 57 : (not enough bytes avail, illegal encoding, or number is larger than a u16).*/ 58 : static inline ulong 59 : fd_cu16_dec_sz( uchar const * buf, 60 13824890238 : ulong bytes_avail ) { 61 13824890238 : if( FD_LIKELY( bytes_avail>=1 && !(0x80UL & buf[0]) ) ) { 62 7357158123 : return 1UL; 63 7357158123 : } 64 6467732115 : if( FD_LIKELY( bytes_avail>=2 && !(0x80UL & buf[1]) ) ) { 65 3233863788 : if( FD_UNLIKELY( !buf[1] ) ) return 0UL; /* Detect non-minimal encoding */ 66 3208599267 : return 2UL; 67 3233863788 : } 68 3233868327 : if( FD_LIKELY( bytes_avail>=3 && !(0xFCUL & buf[2]) ) ) { 69 50528268 : if( FD_UNLIKELY( !buf[2] ) ) return 0UL; /* Detect non-minimal encoding */ 70 37896204 : return 3UL; 71 50528268 : } 72 3183340059 : return 0UL; 73 3233868327 : } 74 : 75 : /* fd_cu16_dec: Reads a compact-u16. buf points to the first byte of the 76 : encoded value. Validates that the compact-u16 is legally encoded, and 77 : returns 0 to indicate that validation failed. If the compact-u16 is valid, 78 : the decoded value is stored in the location pointed to by result_out. On 79 : success, returns the length of the encoded compact-u16. */ 80 : static inline ulong 81 : fd_cu16_dec( uchar const * buf, 82 : ulong bytes_avail, 83 12935430915 : ushort * result_out ) { 84 12935430915 : ulong sz = fd_cu16_dec_sz( buf, bytes_avail ); 85 12935430915 : if( sz ) *result_out = fd_cu16_dec_fixed( buf, sz ); 86 12935430915 : return sz; 87 12935430915 : } 88 : 89 : static inline uint 90 198843 : fd_cu16_enc( ushort val, uchar * out ) { 91 198843 : ulong v = (ulong)val; 92 198843 : ulong byte0 = (v )&0x7FUL; 93 198843 : ulong byte1 = (v>> 7)&0x7FUL; 94 198843 : ulong byte2 = (v>>14); 95 198843 : int needs_byte1 = (v>0x007FUL); 96 198843 : int needs_byte2 = (v>0x3FFFUL); 97 198843 : fd_uchar_store_if( 1, out + 0, (uchar)(byte0 | ((ulong)needs_byte1<<7)) ); 98 198843 : fd_uchar_store_if( needs_byte1, out + 1, (uchar)(byte1 | ((ulong)needs_byte2<<7)) ); 99 198843 : fd_uchar_store_if( needs_byte2, out + 2, (uchar)(byte2 ) ); 100 198843 : return (uint)(1+needs_byte1+needs_byte2); 101 198843 : } 102 : 103 : FD_PROTOTYPES_END 104 : #endif /* HEADER_fd_src_ballet_txn_fd_compact_u16_h */