LCOV - code coverage report
Current view: top level - flamenco/types - fd_bincode.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 192 277 69.3 %
Date: 2026-03-19 18:19:27 Functions: 33 16245 0.2 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_types_fd_bincode_h
       2             : #define HEADER_fd_src_flamenco_types_fd_bincode_h
       3             : 
       4             : #include "../../util/fd_util.h"
       5             : 
       6             : /* Context argument used for encoding */
       7             : struct fd_bincode_encode_ctx {
       8             :   /* Current position in data buffer */
       9             :   void * data;
      10             :   /* End of buffer */
      11             :   void * dataend;
      12             : };
      13             : typedef struct fd_bincode_encode_ctx fd_bincode_encode_ctx_t;
      14             : 
      15             : /* Context argument used for decoding */
      16             : struct fd_bincode_decode_ctx {
      17             :   /* Current position in data buffer */
      18             :   void const * data;
      19             :   /* End of buffer */
      20             :   void const * dataend;
      21             : };
      22             : typedef struct fd_bincode_decode_ctx fd_bincode_decode_ctx_t;
      23             : 
      24    18723112 : #define FD_BINCODE_SUCCESS         (    0)
      25        1795 : #define FD_BINCODE_ERR_UNDERFLOW   (-1001) /* Attempted to read past end of buffer */
      26        7178 : #define FD_BINCODE_ERR_OVERFLOW    (-1002) /* Attempted to write past end of buffer */
      27        8046 : #define FD_BINCODE_ERR_ENCODING    (-1003) /* Invalid encoding */
      28             : 
      29             : #define FD_BINCODE_PRIMITIVE_STUBS( name, type ) \
      30             :   static inline int \
      31             :   fd_bincode_##name##_decode( type *                    self, \
      32      147179 :                               fd_bincode_decode_ctx_t * ctx ) { \
      33      147179 :     uchar const * ptr = (uchar const *) ctx->data; \
      34      147179 :     if ( FD_UNLIKELY((void const *)(ptr + sizeof(type)) > ctx->dataend ) ) \
      35      147179 :       return FD_BINCODE_ERR_UNDERFLOW; \
      36      147179 :     memcpy( self, ptr, sizeof(type) );  /* unaligned */ \
      37      146562 :     ctx->data = ptr + sizeof(type); \
      38      146562 :     return FD_BINCODE_SUCCESS; \
      39      147179 :   } \
      40             :   static inline int \
      41    12933805 :   fd_bincode_##name##_decode_footprint( fd_bincode_decode_ctx_t * ctx ) { \
      42    12933805 :     uchar const * ptr = (uchar const *) ctx->data; \
      43    12933805 :     if ( FD_UNLIKELY((void const *)(ptr + sizeof(type)) > ctx->dataend ) ) \
      44    12933805 :       return FD_BINCODE_ERR_UNDERFLOW; \
      45    12933805 :     ctx->data = ptr + sizeof(type); \
      46    12933634 :     return FD_BINCODE_SUCCESS; \
      47    12933805 :   } \
      48             :   static inline void \
      49             :   fd_bincode_##name##_decode_unsafe( type *                    self, \
      50    29968885 :                                      fd_bincode_decode_ctx_t * ctx ) { \
      51    29968885 :     uchar const * ptr = (uchar const *) ctx->data; \
      52    29968885 :     memcpy( self, ptr, sizeof(type) );  /* unaligned */ \
      53    29968885 :     ctx->data = ptr + sizeof(type); \
      54    29968885 :   } \
      55             :   static inline int \
      56             :   fd_bincode_##name##_encode( type                      self, \
      57     5044375 :                               fd_bincode_encode_ctx_t * ctx ) { \
      58     5044375 :     uchar * ptr = (uchar *)ctx->data; \
      59     5044375 :     if ( FD_UNLIKELY((void *)(ptr + sizeof(type)) > ctx->dataend ) ) \
      60     5044375 :       return FD_BINCODE_ERR_OVERFLOW; \
      61     5044375 :     memcpy( ptr, &self, sizeof(type) );  /* unaligned */ \
      62     5044375 :     ctx->data = ptr + sizeof(type); \
      63     5044375 :     return FD_BINCODE_SUCCESS; \
      64     5044375 :   }
      65             : 
      66             : /* fd_w_u128 is a wrapped "uint128" type providing basic 128-bit
      67             :    unsigned int functionality to fd_types, even if the compile target
      68             :    does not natively support uint128. */
      69             : 
      70             : union __attribute__((aligned(16))) fd_w_u128 {
      71             :   uchar   uc[16];
      72             :   ulong   ul[2];
      73             : # if FD_HAS_INT128
      74             :   uint128 ud;
      75             : # endif
      76             : };
      77             : 
      78             : typedef union fd_w_u128 fd_w_u128_t;
      79             : 
      80             : FD_BINCODE_PRIMITIVE_STUBS( uint8,   uchar       )
      81             : FD_BINCODE_PRIMITIVE_STUBS( uint16,  ushort      )
      82             : FD_BINCODE_PRIMITIVE_STUBS( uint32,  uint        )
      83             : FD_BINCODE_PRIMITIVE_STUBS( uint64,  ulong       )
      84             : FD_BINCODE_PRIMITIVE_STUBS( int64,   long        )
      85             : FD_BINCODE_PRIMITIVE_STUBS( uint128, fd_w_u128_t )
      86             : FD_BINCODE_PRIMITIVE_STUBS( double,  double      )
      87             : 
      88             : static inline int
      89             : fd_bincode_bool_decode( uchar *                   self,
      90       28473 :                         fd_bincode_decode_ctx_t * ctx ) {
      91             : 
      92       28473 :   uchar const * ptr = (uchar const *)ctx->data;
      93       28473 :   if( FD_UNLIKELY( ptr+1 > (uchar const *)ctx->dataend ) )
      94         201 :     return FD_BINCODE_ERR_UNDERFLOW;
      95             : 
      96       28272 :   if( FD_UNLIKELY( *ptr & (~1U) ) )
      97        3444 :     return FD_BINCODE_ERR_ENCODING;
      98             : 
      99       24828 :   *self = *ptr;
     100       24828 :   ctx->data = ptr + 1;
     101             : 
     102       24828 :   return FD_BINCODE_SUCCESS;
     103       28272 : }
     104             : 
     105             : static inline int
     106       35165 : fd_bincode_bool_decode_footprint( fd_bincode_decode_ctx_t * ctx ) {
     107             : 
     108       35165 :   uchar const * ptr = (uchar const *)ctx->data;
     109       35165 :   if( FD_UNLIKELY( ptr+1 > (uchar const *)ctx->dataend ) )
     110           9 :     return FD_BINCODE_ERR_UNDERFLOW;
     111             : 
     112       35156 :   if( FD_UNLIKELY( *ptr & (~1U) ) )
     113          17 :     return FD_BINCODE_ERR_ENCODING;
     114             : 
     115       35139 :   ctx->data = ptr + 1;
     116             : 
     117       35139 :   return FD_BINCODE_SUCCESS;
     118       35156 : }
     119             : 
     120             : static inline void
     121             : fd_bincode_bool_decode_unsafe( uchar *                   self,
     122       58356 :                                fd_bincode_decode_ctx_t * ctx ) {
     123       58356 :   fd_bincode_uint8_decode_unsafe( self, ctx );
     124       58356 : }
     125             : 
     126             : static inline int
     127             : fd_bincode_bool_encode( uchar                     self,
     128        1028 :                         fd_bincode_encode_ctx_t * ctx ) {
     129             : 
     130        1028 :   uchar * ptr = (uchar *)ctx->data;
     131        1028 :   if ( FD_UNLIKELY( (void *)(ptr + 1) > ctx->dataend ) )
     132           0 :     return FD_BINCODE_ERR_OVERFLOW;
     133             : 
     134        1028 :   *ptr = !!self;
     135        1028 :   ctx->data = ptr + 1;
     136             : 
     137        1028 :   return FD_BINCODE_SUCCESS;
     138        1028 : }
     139             : 
     140             : static inline int
     141             : fd_bincode_bytes_decode( uchar *                   self,
     142             :                          ulong                     len,
     143           0 :                          fd_bincode_decode_ctx_t * ctx ) {
     144           0 :   uchar * ptr = (uchar *) ctx->data;
     145           0 :   if ( FD_UNLIKELY((ulong)( (uchar *) ctx->dataend - ptr) < len ) ) // Get wrap-around case right
     146           0 :     return FD_BINCODE_ERR_UNDERFLOW;
     147           0 : 
     148           0 :   fd_memcpy(self, ptr, len);
     149           0 :   ctx->data = ptr + len;
     150           0 : 
     151           0 :   return FD_BINCODE_SUCCESS;
     152           0 : }
     153             : 
     154             : static inline int
     155             : fd_bincode_bytes_decode_footprint( ulong                     len,
     156       79595 :                                    fd_bincode_decode_ctx_t * ctx ) {
     157       79595 :   uchar * ptr = (uchar *) ctx->data;
     158       79595 :   if ( FD_UNLIKELY((ulong)( (uchar *) ctx->dataend - ptr) < len ) ) { // Get wrap-around case right
     159         651 :     return FD_BINCODE_ERR_UNDERFLOW;
     160         651 :   }
     161             : 
     162       78944 :   ctx->data = ptr + len;
     163             : 
     164       78944 :   return FD_BINCODE_SUCCESS;
     165       79595 : }
     166             : 
     167             : static inline void
     168             : fd_bincode_bytes_decode_unsafe( uchar *                   self,
     169             :                                 ulong                     len,
     170     5369748 :                                 fd_bincode_decode_ctx_t * ctx ) {
     171     5369748 :   uchar * ptr = (uchar *) ctx->data;
     172     5369748 :   fd_memcpy(self, ptr, len);
     173     5369748 :   ctx->data = ptr + len;
     174     5369748 : }
     175             : 
     176             : static inline int
     177             : fd_bincode_bytes_encode( uchar const *             self,
     178             :                          ulong                     len,
     179      156259 :                          fd_bincode_encode_ctx_t * ctx ) {
     180      156259 :   fd_msan_check( self, len );
     181             : 
     182      156259 :   uchar * ptr = (uchar *)ctx->data;
     183      156259 :   if( FD_UNLIKELY( (void *)( ptr+len ) > ctx->dataend ) )
     184           0 :     return FD_BINCODE_ERR_OVERFLOW;
     185             : 
     186      156259 :   fd_memcpy( ptr, self, len );
     187      156259 :   ctx->data = ptr + len;
     188             : 
     189      156259 :   return FD_BINCODE_SUCCESS;
     190      156259 : }
     191             : 
     192             : /* Alternate versions of fd_cu16_dec to make the function signature more consistent with the
     193             :    other fd_bincode_decode functions.  */
     194             : static inline int
     195             : fd_bincode_compact_u16_decode( ushort *                  self,
     196        1303 :                                fd_bincode_decode_ctx_t * ctx ) {
     197        1303 :   const uchar * ptr = (const uchar*) ctx->data;
     198        1303 :   if( FD_UNLIKELY( ptr==NULL ) ) {
     199           0 :     return FD_BINCODE_ERR_UNDERFLOW;
     200           0 :   }
     201             : 
     202        1303 :   if( FD_LIKELY( (void *) (ptr + 1) <= ctx->dataend && !(0x80U & ptr[0]) ) ) {
     203        1176 :     *self = (ushort)ptr[0];
     204        1176 :     ctx->data = ptr + 1;
     205        1176 :     return FD_BINCODE_SUCCESS;
     206        1176 :   }
     207             : 
     208         127 :   if( FD_LIKELY( (void *) (ptr + 2) <= ctx->dataend && !(0x80U & ptr[1]) ) ) {
     209          40 :     if( FD_UNLIKELY( !ptr[1] ) ) /* Detect non-minimal encoding */
     210           5 :       return FD_BINCODE_ERR_ENCODING;
     211          35 :     *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)ptr[1])<<7));
     212          35 :     ctx->data = ptr + 2;
     213          35 :     return FD_BINCODE_SUCCESS;
     214          40 :   }
     215             : 
     216          87 :   if( FD_LIKELY( (void *) (ptr + 3) <= ctx->dataend && !(0xFCU & ptr[2]) ) ) {
     217          21 :     if( FD_UNLIKELY( !ptr[2] ) ) /* Detect non-minimal encoding */
     218           6 :       return FD_BINCODE_ERR_ENCODING;
     219          15 :     *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)(ptr[1]&0x7FUL))<<7) + (((ulong)ptr[2])<<14));
     220          15 :     ctx->data = ptr + 3;
     221          15 :     return FD_BINCODE_SUCCESS;
     222          21 :   }
     223             : 
     224          66 :   return FD_BINCODE_ERR_UNDERFLOW;
     225          87 : }
     226             : 
     227             : static inline void
     228             : fd_bincode_compact_u16_decode_unsafe( ushort *                  self,
     229        1022 :                                       fd_bincode_decode_ctx_t * ctx ) {
     230        1022 :   const uchar * ptr = (const uchar*) ctx->data;
     231             : 
     232        1022 :   if( !(0x80U & ptr[0]) ) {
     233        1021 :     *self = (ushort)ptr[0];
     234        1021 :     ctx->data = ptr + 1;
     235        1021 :     return;
     236        1021 :   }
     237             : 
     238           1 :   if( !(0x80U & ptr[1]) ) {
     239           1 :     *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)ptr[1])<<7));
     240           1 :     ctx->data = ptr + 2;
     241           1 :     return;
     242           1 :   }
     243             : 
     244           0 :   *self = (ushort)((ulong)(ptr[0]&0x7FUL) + (((ulong)(ptr[1]&0x7FUL))<<7) + (((ulong)ptr[2])<<14));
     245           0 :   ctx->data = ptr + 3;
     246           0 : }
     247             : 
     248             : static inline int
     249             : fd_bincode_compact_u16_encode( ushort const *            self,
     250       38448 :                                fd_bincode_encode_ctx_t * ctx ) {
     251       38448 :   uchar * ptr = (uchar*) ctx->data;
     252       38448 :   ulong val = *self;
     253             : 
     254       38448 :   if ( val < 0x80UL ) {
     255       36641 :     if ( FD_UNLIKELY((void *) (ptr + 1) > ctx->dataend ) )
     256           0 :       return FD_BINCODE_ERR_OVERFLOW;
     257       36641 :     *ptr = (uchar)val;
     258       36641 :     ctx->data = ptr + 1;
     259       36641 :     return FD_BINCODE_SUCCESS;
     260       36641 :   }
     261             : 
     262        1812 :   else if ( val < 0x4000UL ) {
     263        1812 :     if ( FD_UNLIKELY((void *) (ptr + 2) > ctx->dataend ) )
     264           0 :       return FD_BINCODE_ERR_OVERFLOW;
     265        1812 :     ptr[0] = (uchar)((val&0x7FUL)|0x80UL);
     266        1812 :     ptr[1] = (uchar)(val>>7);
     267        1812 :     ctx->data = ptr + 2;
     268        1812 :     return FD_BINCODE_SUCCESS;
     269        1812 :   }
     270             : 
     271 >1844*10^16 :   else {
     272 >1844*10^16 :     if ( FD_UNLIKELY((void *) (ptr + 3) > ctx->dataend ) )
     273           0 :       return FD_BINCODE_ERR_OVERFLOW;
     274 >1844*10^16 :     ptr[0] = (uchar)((val&0x7FUL)|0x80UL);
     275 >1844*10^16 :     ptr[1] = (uchar)(((val>>7)&0x7FUL)|0x80UL);
     276 >1844*10^16 :     ptr[2] = (uchar)(val>>14);
     277 >1844*10^16 :     ctx->data = ptr + 3;
     278 >1844*10^16 :     return FD_BINCODE_SUCCESS;
     279 >1844*10^16 :   }
     280       38448 : }
     281             : 
     282             : static inline ulong
     283           0 : fd_bincode_compact_u16_size( ushort const * self ) {
     284           0 :   ulong val = *self;
     285             : 
     286           0 :   if ( val < 0x80UL ) {
     287           0 :     return 1;
     288           0 :   }
     289           0 :   else if ( val < 0x4000UL ) {
     290           0 :     return 2;
     291           0 :   }
     292           0 :   else {
     293           0 :     return 3;
     294           0 :   }
     295           0 : }
     296             : 
     297             : /* Decodes an integer encoded using the serde_varint algorithm:
     298             :    https://github.com/solana-labs/solana/blob/master/sdk/program/src/serde_varint.rs
     299             : 
     300             :    A variable number of bytes could have been used to encode the integer.
     301             :    The most significant bit of each byte indicates if more bytes have been used, so we keep consuming until
     302             :    we reach a byte where the most significant bit is 0.
     303             : */
     304             : static inline int
     305             : fd_bincode_varint_decode( ulong *                   self,
     306           0 :                           fd_bincode_decode_ctx_t * ctx ) {
     307           0 :   ulong out   = 0UL;
     308           0 :   uint  shift = 0U;
     309           0 : 
     310           0 :   while( FD_LIKELY( shift < 64U ) ) {
     311           0 : 
     312           0 :     if( FD_UNLIKELY( ctx->data >= ctx->dataend ) )
     313           0 :       return FD_BINCODE_ERR_UNDERFLOW;
     314           0 : 
     315           0 :     uint byte = *(uchar const *)ctx->data;
     316           0 :     ctx->data = (uchar const *)ctx->data + 1;
     317           0 :     out |= (byte & 0x7FUL) << shift;
     318           0 : 
     319           0 :     if( (byte & 0x80U) == 0U ) {
     320           0 :       if( (out>>shift) != byte )
     321           0 :         return FD_BINCODE_ERR_ENCODING;
     322           0 :       if( byte==0U && (shift!=0U || out!=0UL) )
     323           0 :         return FD_BINCODE_ERR_ENCODING;
     324           0 :       *self = out;
     325           0 :       return FD_BINCODE_SUCCESS;
     326           0 :     }
     327           0 : 
     328           0 :     shift += 7U;
     329           0 : 
     330           0 :   }
     331           0 : 
     332           0 :   return FD_BINCODE_ERR_ENCODING;
     333           0 : }
     334             : 
     335             : static inline int
     336       21647 : fd_bincode_varint_decode_footprint( fd_bincode_decode_ctx_t * ctx ) {
     337       21647 :   ulong out   = 0UL;
     338       21647 :   uint  shift = 0U;
     339             : 
     340       38320 :   while( FD_LIKELY( shift < 64U ) ) {
     341             : 
     342       38318 :     if( FD_UNLIKELY( ctx->data >= ctx->dataend ) )
     343          18 :       return FD_BINCODE_ERR_UNDERFLOW;
     344             : 
     345       38300 :     uint byte = *(uchar const *)ctx->data;
     346       38300 :     ctx->data = (uchar const *)ctx->data + 1;
     347       38300 :     out |= (byte & 0x7FUL) << shift;
     348             : 
     349       38300 :     if( (byte & 0x80U) == 0U ) {
     350       21627 :       if( (out>>shift) != byte )
     351           0 :         return FD_BINCODE_ERR_ENCODING;
     352       21627 :       if( byte==0U && (shift!=0U || out!=0UL) )
     353          25 :         return FD_BINCODE_ERR_ENCODING;
     354       21602 :       return FD_BINCODE_SUCCESS;
     355       21627 :     }
     356             : 
     357       16673 :     shift += 7U;
     358             : 
     359       16673 :   }
     360             : 
     361           2 :   return FD_BINCODE_ERR_ENCODING;
     362       21647 : }
     363             : 
     364             : static inline void
     365             : fd_bincode_varint_decode_unsafe( ulong *                   self,
     366       23073 :                                  fd_bincode_decode_ctx_t * ctx ) {
     367       23073 :   ulong out   = 0UL;
     368       23073 :   uint  shift = 0U;
     369             : 
     370       28523 :   for(;;) {
     371       28523 :     uint byte = *(uchar const *)ctx->data;
     372       28523 :     ctx->data = (uchar const *)ctx->data + 1;
     373       28523 :     out |= (byte & 0x7FUL) << shift;
     374             : 
     375       28523 :     if( (byte & 0x80U) == 0U ) {
     376       23073 :       *self = out;
     377       23073 :       return;
     378       23073 :     }
     379             : 
     380        5450 :     shift += 7U;
     381        5450 :   }
     382       23073 : }
     383             : 
     384             : static inline int
     385             : fd_bincode_varint_encode( ulong                     val,
     386           0 :                           fd_bincode_encode_ctx_t * ctx ) {
     387           0 :   uchar * ptr = (uchar *) ctx->data;
     388           0 :   while (1) {
     389           0 :     if ( FD_UNLIKELY((void *) (ptr + 1) > ctx->dataend ) )
     390           0 :       return FD_BINCODE_ERR_OVERFLOW;
     391           0 :     if ( val < 0x80UL ) {
     392           0 :       *(ptr++) = (uchar)val;
     393           0 :       ctx->data = ptr;
     394           0 :       return FD_BINCODE_SUCCESS;
     395           0 :     }
     396           0 :     *(ptr++) = (uchar)((val&0x7FUL)|0x80UL);
     397           0 :     val >>= 7;
     398           0 :   }
     399           0 : }
     400             : 
     401             : static inline ulong
     402           0 : fd_bincode_varint_size( ulong val ) {
     403           0 :   ulong sz = 0;
     404           0 :   while (1) {
     405           0 :     if ( val < 0x80UL ) {
     406           0 :       return sz+1;
     407           0 :     }
     408           0 :     sz++;
     409           0 :     val >>= 7;
     410           0 :   }
     411           0 : }
     412             : 
     413             : /* Convenience API for deserializing */
     414             : 
     415             : /* fd_bincode_decode_static decodes a statically-sized bincode type.
     416             : 
     417             :    Example usage:
     418             : 
     419             :    fd_epoch_schedule_t es[1];
     420             :    if( FD_UNLIKELY( fd_bincode_decode_static( epoch_schedule, es, buf, bufsz ) ) ) {
     421             :      ... parse fail ...
     422             :      return;
     423             :    }
     424             :    ... parse success ... */
     425             : 
     426             : #define fd_bincode_decode_static1( type, suffix, out, buf, buf_sz )    \
     427        9382 :   __extension__({                                                      \
     428        9382 :     void const * const buf_    = (buf);                                \
     429       16942 :     ulong        const buf_sz_ = (buf_sz);                             \
     430        9382 :     fd_##type##suffix##_t *    res     = NULL;                         \
     431        9382 :     fd_bincode_decode_ctx_t ctx = {0};                                 \
     432        9382 :     ctx.data    = (void const *)( buf_ );                              \
     433        9382 :     ctx.dataend = (void const *)( (ulong)ctx.data + buf_sz_ );         \
     434        9382 :     ulong total_sz = 0UL;                                              \
     435        9382 :     int err = fd_##type##_decode_footprint( &ctx, &total_sz );         \
     436        9382 :     if( FD_LIKELY( err==FD_BINCODE_SUCCESS ) ) {                       \
     437        7577 :       res = fd_##type##_decode##suffix( (out), &ctx );                 \
     438        7577 :     }                                                                  \
     439        9382 :     res;                                                               \
     440        9382 :   })
     441             : 
     442             : #define fd_bincode_decode_static( t,o,b,s ) \
     443        9382 :   fd_bincode_decode_static1( t, , o, b, s )
     444             : 
     445             : #define fd_bincode_decode_static_global( t,o,b,s ) \
     446             :   fd_bincode_decode_static1( t, _global, o, b, s )
     447             : 
     448             : #define fd_bincode_decode_static_limited_deserialize( type, out, buf, buf_sz, limit ) \
     449        8471 :   fd_bincode_decode_static( type, out, buf, buf_sz>limit ? limit : buf_sz )
     450             : 
     451             : #endif /* HEADER_fd_src_flamenco_types_fd_bincode_h */

Generated by: LCOV version 1.14