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

Generated by: LCOV version 1.14