LCOV - code coverage report
Current view: top level - ballet/pb - fd_pb_encode.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 118 231 51.1 %
Date: 2026-11-11 12:40:43 Functions: 51 1584 3.2 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_ballet_pb_fd_pb_encode_h
       2             : #define HEADER_fd_src_ballet_pb_fd_pb_encode_h
       3             : 
       4             : /* fd_pb_encode.h is a header-only Protobuf wire format encoder.  It
       5             :    is schema agnostic and designed for embedded use.  Data is written
       6             :    out in a streaming manner, with occasional fixups of length prefixes
       7             :    when writing submessages.
       8             : 
       9             :    Using this API requires understanding how Protobuf schemas map to the
      10             :    wire format: https://protobuf.dev/programming-guides/encoding/
      11             : 
      12             :    Here is how fd_pb_encode compares to a typical Protobuf encoding
      13             :    library:
      14             :    - No dependencies (no protoc)
      15             :    - No code generation (user hand-writes encoders)
      16             :    - No memory allocator needed
      17             :    - No streaming support (message written into a contiguous buffer)
      18             :    - User error can result in corrupt messages (duplicate or incorrect
      19             :      tags, etc)
      20             :    - Space efficiently is slightly worse (larger length-prefixes for
      21             :      submessages) */
      22             : 
      23             : #include "fd_pb_wire.h"
      24             : #include "../../util/log/fd_log.h"
      25             : 
      26             : /* FD_PB_ENCODE_DEPTH_MAX specifies the max submessage depth. */
      27             : 
      28           0 : #define FD_PB_ENCODER_DEPTH_MAX (63UL)
      29             : 
      30             : /* The pb_encoder class is used to serialize Protobuf messages.  The
      31             :    user calls fd_pb_encode_* with each field, top down. */
      32             : 
      33             : struct fd_pb_encoder {
      34             : 
      35             :   /* [buf0,buf1) is the encode buffer */
      36             :   uchar * buf0;
      37             :   uchar * buf1;
      38             : 
      39             :   /* cur points to where the next field is placed */
      40             :   uchar * cur;
      41             : 
      42             :   /* Sub-message nesting depth of new fields (0==topmost) */
      43             :   uint depth;
      44             : 
      45             :   /* offset to length prefix of each depth
      46             :      lp_off[0] points to the length prefix of a LEN field at depth 0
      47             :      (the LEN field's submessage is at depth 1) */
      48             :   uint lp_off[ FD_PB_ENCODER_DEPTH_MAX ];
      49             : 
      50             : };
      51             : 
      52             : typedef struct fd_pb_encoder fd_pb_encoder_t;
      53             : 
      54             : FD_PROTOTYPES_BEGIN
      55             : 
      56             : /* fd_pb_encoder_init creates a Protobuf encoder.  Data is written to
      57             :    the buffer at out (up to out_sz bytes).  Returns the initialized
      58             :    encoder object (has a mutable borrow on out).  On return, the
      59             :    encoder's current message is the topmost message of the encode op.
      60             : 
      61             :    out_sz should be slightly overallocated (by 32 bytes) since encoder
      62             :    bounds checks use overly conservative upper bounds for performance
      63             :    (i.e. the encoder might fail to add a field with "out of space" if
      64             :    less than 32 bytes of space is remaining, even if the field would
      65             :    still fit). */
      66             : 
      67             : static inline fd_pb_encoder_t *
      68             : fd_pb_encoder_init( fd_pb_encoder_t * encoder,
      69             :                     uchar *           out,
      70      130172 :                     ulong             out_sz ) {
      71      130172 :   encoder->buf0  = out;
      72      130172 :   encoder->buf1  = out + out_sz;
      73      130172 :   encoder->cur   = out;
      74      130172 :   encoder->depth = 0U;
      75      130172 :   return encoder;
      76      130172 : }
      77             : 
      78             : /* fd_pb_encoder_fini clears the encoder state. */
      79             : 
      80             : static inline void *
      81      130265 : fd_pb_encoder_fini( fd_pb_encoder_t * encoder ) {
      82      130265 :   encoder->buf0  = NULL;
      83      130265 :   encoder->buf1  = NULL;
      84      130265 :   encoder->cur   = NULL;
      85      130265 :   encoder->depth = 0;
      86      130265 :   return encoder;
      87      130265 : }
      88             : 
      89             : /* fd_pb_encoder_out returns a pointer to the first byte of the encoded
      90             :    output.  There is a valid serialized Protobuf message behind this
      91             :    pointer when the following conditions are true:
      92             :    - At least one field was fully written at the topmost message
      93             :    - No write is currently inflight (submessage, length-prefixed, etc)
      94             :    - No write failed */
      95             : 
      96             : static inline uchar *
      97           0 : fd_pb_encoder_out( fd_pb_encoder_t * encoder ) {
      98           0 :   return encoder->buf0;
      99           0 : }
     100             : 
     101             : /* fd_pb_encoder_out_sz returns the number of bytes produced so far. */
     102             : 
     103             : static inline ulong
     104      130273 : fd_pb_encoder_out_sz( fd_pb_encoder_t * encoder ) {
     105      130273 :   FD_CHECK_CRIT( encoder->cur >= encoder->buf0, "corrupt encoder state" );
     106      130273 :   return (ulong)encoder->cur - (ulong)encoder->buf0;
     107      130273 : }
     108             : 
     109             : /* fd_pb_encoder_space returns the number of encoded field bytes that
     110             :    can be appended to the current submessage. */
     111             : 
     112             : static inline ulong
     113     6493570 : fd_pb_encoder_space( fd_pb_encoder_t const * encoder ) {
     114     6493570 :   FD_CHECK_CRIT( encoder->cur <= encoder->buf1, "corrupt encoder state" );
     115     6493570 :   return (ulong)encoder->buf1 - (ulong)encoder->cur;
     116     6493570 : }
     117             : 
     118             : /* fd_pb_lp_open adds a new unknown-sized LEN field to the encoder's
     119             :    current message.  Returns encoder on success.  On failure (no space),
     120             :    returns NULL.  Every successful call must be paired with a call to
     121             :    fd_pb_lp_close.  Use fd_pb_encoder_space to check how much free
     122             :    space the frame has, and fd_pb_encoder_push to append data.
     123             : 
     124             :    This method is useful for serializing Protobuf submessages with
     125             :    another library, moving encoded submessages without a deserialize/
     126             :    serialize pass, streaming out packed repated fields, iterating over
     127             :    an unknown-sized sequence, etc.  */
     128             : 
     129             : static FD_FN_UNUSED fd_pb_encoder_t *
     130             : fd_pb_lp_open( fd_pb_encoder_t * encoder,
     131     1179977 :                uint              field_id ) {
     132     1179977 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     133     1179977 :                    (fd_pb_varint32_sz_max + fd_pb_varint32_sz_max) ) ) {
     134           0 :     return NULL;
     135           0 :   }
     136     1179977 :   uint depth = encoder->depth;
     137     1179977 :   if( FD_UNLIKELY( depth >= FD_PB_ENCODER_DEPTH_MAX ) ) {
     138             :     /* unreachable for well-written clients */
     139           0 :     FD_LOG_WARNING(( "pb_encode failed: submessage nesting depth exceeded" ));
     140           0 :     return NULL;
     141           0 :   }
     142     1179977 :   encoder->depth++;
     143             :   /* Add tag, reserve worst-case space for length-prefix */
     144     1179977 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_LEN, field_id );
     145     1179977 :   encoder->cur              = fd_pb_append_tag( encoder->cur, tag );
     146     1179977 :   encoder->lp_off[ depth ]  = (uint)( encoder->cur - encoder->buf0 );
     147     1179977 :   encoder->cur             += fd_pb_varint32_sz_max;
     148     1179977 :   return encoder;
     149     1179977 : }
     150             : 
     151             : /* fd_pb_lp_close closes the current LEN field (opened with
     152             :    fd_pb_lp_open)  Returns encoder on success and NULL on failure
     153             :    (LEN field exceeds max size). */
     154             : 
     155             : static FD_FN_UNUSED fd_pb_encoder_t *
     156     1183438 : fd_pb_lp_close( fd_pb_encoder_t * encoder ) {
     157     1183438 :   FD_CHECK_CRIT( encoder->depth, "unmatched lp_close" );
     158     1183438 :   FD_CHECK_CRIT( encoder->depth<=FD_PB_ENCODER_DEPTH_MAX, "corrupt submessage depth" );
     159     1183438 :   uint    depth = --encoder->depth;
     160     1183438 :   uchar * lp    = encoder->buf0 + encoder->lp_off[ depth ];
     161     1183438 :   uchar * sub0  = lp + fd_pb_varint32_sz_max;
     162     1183438 :   uchar * sub1  = encoder->cur;
     163     1183438 :   ulong   sz    = (ulong)( sub1-sub0 );
     164     1183438 :   FD_CHECK_CRIT( sub0<=sub1,          "corrupt submessage state" );
     165     1183438 :   FD_CHECK_CRIT( sub1<=encoder->buf1, "out-of-bounds write"      );
     166     1183438 :   if( FD_UNLIKELY( sz>UINT_MAX ) ) {
     167           0 :     FD_LOG_WARNING(( "pb_encode failed: submessage is too large" ));
     168           0 :   }
     169     1183438 :   fd_pb_append_varint32_sz5( lp, (uint)sz );
     170     1183438 :   return encoder;
     171     1183438 : }
     172             : 
     173             : /* fd_pb_submsg_open adds a new submessage to the encoder's current
     174             :    message, then pivots the current message to the newly created
     175             :    submessage.  Returns encoder on success, NULL on failure (no space
     176             :    remaining).  Every successful call must be paired with a call to
     177             :    fd_pb_submsg_close. */
     178             : 
     179             : static inline fd_pb_encoder_t *
     180             : fd_pb_submsg_open( fd_pb_encoder_t * encoder,
     181     1177301 :                    uint              field_id ) {
     182     1177301 :   return fd_pb_lp_open( encoder, field_id );
     183     1177301 : }
     184             : 
     185             : /* fd_pb_submsg_close finishes the encoder's current submessage.  Sets
     186             :    the encoder's current message to the parent message.  Returns
     187             :    encoder on success and NULL on failure (submessage exceeds max msg
     188             :    size). */
     189             : 
     190             : static inline fd_pb_encoder_t *
     191           0 : fd_pb_submsg_close( fd_pb_encoder_t * encoder ) {
     192           0 :   return fd_pb_lp_close( encoder );
     193           0 : }
     194             : 
     195             : /* The below fd_pb_push_<type> methods append a field to the encoder's
     196             :    current message.  type and field_id are the field's parameters as
     197             :    defined in the schema.  value depends on the message type.  Note that
     198             :    the provided type MUST match the schema (e.g. encoding uint32 where
     199             :    the schema says sint32 would result in memory corruption).  Returns
     200             :    encoder on success, or NULL on failure (out of space).  On any write
     201             :    failure, discard the entire message.
     202             : 
     203             :    fd_pb_push_bool adds a boolean field.  value==0 implies false,
     204             :    otherwise implies true.
     205             : 
     206             :    fd_pb_push_{int32,int64} add a signed integer field (optimized for
     207             :    small unsigned numbers).
     208             : 
     209             :    fd_pb_push_{uint32,uint64} add an unsigned integer field (optimized
     210             :    for small numbers).
     211             : 
     212             :    fd_pb_push_{sint32,sint64} add a signed integer field (optimized
     213             :    for numbers close to zero).
     214             : 
     215             :    fd_pb_push_{fixed32,fixed64} add an unsigned integer field
     216             :    (optimized for very large numbers).
     217             : 
     218             :    fd_pb_push_{sfixed32,sfixed64} add a signed integer field
     219             :    (optimized for very small or large numbers).
     220             : 
     221             :    fd_pb_push_{float,double} add a {32,64} bit precision floating-
     222             :    point field.
     223             : 
     224             :    fd_pb_push_bytes adds a byte array field.
     225             : 
     226             :    fd_pb_push_string adds a UTF-8 string field.
     227             : 
     228             :    fd_pb_push_cstr adds a UTF-8 string field from a NULL-delimited C
     229             :    string. */
     230             : 
     231             : static inline fd_pb_encoder_t *
     232             : fd_pb_push_bool( fd_pb_encoder_t * encoder,
     233             :                  uint              field_id,
     234      292688 :                  int               value ) {
     235      292688 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     236      292688 :                    (fd_pb_varint32_sz_max + 1) ) ) {
     237           0 :     return NULL;
     238           0 :   }
     239      292688 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_VARINT, field_id );
     240      292688 :   encoder->cur = fd_pb_append_tag ( encoder->cur, tag   );
     241      292688 :   encoder->cur = fd_pb_append_bool( encoder->cur, value );
     242      292688 :   return encoder;
     243      292688 : }
     244             : 
     245             : static inline fd_pb_encoder_t *
     246             : fd_pb_push_int32( fd_pb_encoder_t * encoder,
     247             :                   uint              field_id,
     248       61203 :                   int               value ) {
     249       61203 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     250       61203 :                    (fd_pb_varint32_sz_max + fd_pb_varint32_sz_max) ) ) {
     251           0 :     return NULL;
     252           0 :   }
     253       61203 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_VARINT, field_id );
     254       61203 :   encoder->cur = fd_pb_append_tag  ( encoder->cur, tag   );
     255       61203 :   encoder->cur = fd_pb_append_int32( encoder->cur, value );
     256       61203 :   return encoder;
     257       61203 : }
     258             : 
     259             : static inline fd_pb_encoder_t *
     260             : fd_pb_push_int64( fd_pb_encoder_t * encoder,
     261             :                   uint              field_id,
     262        7592 :                   long              value ) {
     263        7592 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     264        7592 :                    (fd_pb_varint32_sz_max + fd_pb_varint64_sz_max) ) ) {
     265           0 :     return NULL;
     266           0 :   }
     267        7592 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_VARINT, field_id );
     268        7592 :   encoder->cur = fd_pb_append_tag  ( encoder->cur, tag   );
     269        7592 :   encoder->cur = fd_pb_append_int64( encoder->cur, value );
     270        7592 :   return encoder;
     271        7592 : }
     272             : 
     273             : static inline fd_pb_encoder_t *
     274             : fd_pb_push_uint32( fd_pb_encoder_t * encoder,
     275             :                    uint              field_id,
     276       47429 :                    uint              value ) {
     277       47429 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     278       47429 :                    (fd_pb_varint32_sz_max + fd_pb_varint32_sz_max) ) ) {
     279           0 :     return NULL;
     280           0 :   }
     281       47429 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_VARINT, field_id );
     282       47429 :   encoder->cur = fd_pb_append_tag   ( encoder->cur, tag   );
     283       47429 :   encoder->cur = fd_pb_append_uint32( encoder->cur, value );
     284       47429 :   return encoder;
     285       47429 : }
     286             : 
     287             : static inline fd_pb_encoder_t *
     288             : fd_pb_push_uint64( fd_pb_encoder_t * encoder,
     289             :                    uint              field_id,
     290     1755311 :                    ulong             value ) {
     291     1755311 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     292     1755311 :                    (fd_pb_varint32_sz_max + fd_pb_varint64_sz_max) ) ) {
     293           0 :     return NULL;
     294           0 :   }
     295     1755311 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_VARINT, field_id );
     296     1755311 :   encoder->cur = fd_pb_append_tag   ( encoder->cur, tag   );
     297     1755311 :   encoder->cur = fd_pb_append_uint64( encoder->cur, value );
     298     1755311 :   return encoder;
     299     1755311 : }
     300             : 
     301             : static inline fd_pb_encoder_t *
     302             : fd_pb_push_sint32( fd_pb_encoder_t * encoder,
     303             :                    uint              field_id,
     304           0 :                    int               value ) {
     305           0 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     306           0 :                    (fd_pb_varint32_sz_max + fd_pb_varint32_sz_max) ) ) {
     307           0 :     return NULL;
     308           0 :   }
     309           0 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_VARINT, field_id );
     310           0 :   encoder->cur = fd_pb_append_tag   ( encoder->cur, tag   );
     311           0 :   encoder->cur = fd_pb_append_sint32( encoder->cur, value );
     312           0 :   return encoder;
     313           0 : }
     314             : 
     315             : static inline fd_pb_encoder_t *
     316             : fd_pb_push_sint64( fd_pb_encoder_t * encoder,
     317             :                    uint              field_id,
     318           0 :                    long              value ) {
     319           0 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     320           0 :                    (fd_pb_varint32_sz_max + fd_pb_varint64_sz_max) ) ) {
     321           0 :     return NULL;
     322           0 :   }
     323           0 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_VARINT, field_id );
     324           0 :   encoder->cur = fd_pb_append_tag   ( encoder->cur, tag   );
     325           0 :   encoder->cur = fd_pb_append_sint64( encoder->cur, value );
     326           0 :   return encoder;
     327           0 : }
     328             : 
     329             : static inline fd_pb_encoder_t *
     330             : fd_pb_push_fixed32( fd_pb_encoder_t * encoder,
     331             :                     uint              field_id,
     332          88 :                     uint              value ) {
     333          88 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     334          88 :                    (fd_pb_varint32_sz_max + sizeof(uint)) ) ) {
     335           0 :     return NULL;
     336           0 :   }
     337          88 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_I32, field_id );
     338          88 :   encoder->cur = fd_pb_append_tag    ( encoder->cur, tag   );
     339          88 :   encoder->cur = fd_pb_append_fixed32( encoder->cur, value );
     340          88 :   return encoder;
     341          88 : }
     342             : 
     343             : static inline fd_pb_encoder_t *
     344             : fd_pb_push_fixed64( fd_pb_encoder_t * encoder,
     345             :                     uint              field_id,
     346     1018793 :                     ulong             value ) {
     347     1018793 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     348     1018793 :                    (fd_pb_varint32_sz_max + sizeof(ulong)) ) ) {
     349           0 :     return NULL;
     350           0 :   }
     351     1018793 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_I64, field_id );
     352     1018793 :   encoder->cur = fd_pb_append_tag    ( encoder->cur, tag   );
     353     1018793 :   encoder->cur = fd_pb_append_fixed64( encoder->cur, value );
     354     1018793 :   return encoder;
     355     1018793 : }
     356             : 
     357             : static inline fd_pb_encoder_t *
     358             : fd_pb_push_float( fd_pb_encoder_t * encoder,
     359             :                   uint              field_id,
     360           0 :                   float             value ) {
     361           0 :   union { float f; uint u; } cast;
     362           0 :   cast.f = value;
     363           0 :   return fd_pb_push_fixed32( encoder, field_id, cast.u );
     364           0 : }
     365             : 
     366             : static inline fd_pb_encoder_t *
     367             : fd_pb_push_double( fd_pb_encoder_t * encoder,
     368             :                    uint              field_id,
     369           0 :                    double            value ) {
     370           0 :   union { double d; ulong v; } cast;
     371           0 :   cast.d = value;
     372           0 :   return fd_pb_push_fixed64( encoder, field_id, cast.v );
     373           0 : }
     374             : 
     375             : static inline fd_pb_encoder_t *
     376             : fd_pb_push_bytes( fd_pb_encoder_t * encoder,
     377             :                   uint              field_id,
     378             :                   void const *      buf,
     379     1920766 :                   ulong             sz ) {
     380     1920766 :   if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <
     381     1920766 :                    (fd_pb_varint32_sz_max + fd_pb_varint32_sz_max + sz) ||
     382     1920766 :                    sz>UINT_MAX ) ) {  /* max message size, overflow protect */
     383           0 :     return NULL;
     384           0 :   }
     385     1920766 :   uint tag = fd_pb_tag( FD_PB_WIRE_TYPE_LEN, field_id );
     386     1920766 :   encoder->cur = fd_pb_append_tag   ( encoder->cur, tag      );
     387     1920766 :   encoder->cur = fd_pb_append_uint32( encoder->cur, (uint)sz );
     388     1920766 :   fd_memcpy( encoder->cur, buf, sz );
     389     1920766 :   encoder->cur += sz;
     390     1920766 :   return encoder;
     391     1920766 : }
     392             : 
     393             : static inline fd_pb_encoder_t *
     394             : fd_pb_push_string( fd_pb_encoder_t * encoder,
     395             :                    uint              field_id,
     396             :                    char const *      str,
     397           0 :                    ulong             len ) {
     398           0 :   return fd_pb_push_bytes( encoder, field_id, str, len );
     399           0 : }
     400             : 
     401             : static inline fd_pb_encoder_t *
     402             : fd_pb_push_cstr( fd_pb_encoder_t * encoder,
     403             :                  uint              field_id,
     404           0 :                  char const *      cstr ) {
     405             :   /* FIXME could do this in a single pass */
     406           0 :   return fd_pb_push_string( encoder, field_id, cstr, strlen( cstr ) );
     407           0 : }
     408             : 
     409             : /* The below fd_pb_push_packed_<type> methods append a packed repeated
     410             :    field (a sequence of scalars).  These are analogous to the above
     411             :    encoders, except that they encode multiple values.  values points to
     412             :    a contiguous array of values.  cnt is the number of values (zero is
     413             :    fine).
     414             : 
     415             :    On the wire, a packed repeated field is a LEN type field with the
     416             :    content set to the concatenated serializations of each element.
     417             : 
     418             :    These are all equivalent to the following pattern:
     419             : 
     420             :      fd_pb_lp_open()
     421             :      for( each element ) fd_pb_append_type()
     422             :      fd_pb_lp_close()
     423             : 
     424             :    fd_pb_push_packed_bool is deliberately omitted. */
     425             : 
     426             : #define FD_PB_PUSH_PACKED( type, ctype )                                  \
     427             :   FD_FN_UNUSED static fd_pb_encoder_t *                                   \
     428             :   fd_pb_push_packed_##type( fd_pb_encoder_t * encoder,                    \
     429             :                             uint              field_id,                   \
     430             :                             ctype const *     values,                     \
     431           0 :                             ulong             cnt ) {                     \
     432           0 :     /* would exceed max message size? */                                  \
     433           0 :     if( FD_UNLIKELY( cnt>UINT_MAX ) ) return NULL;                        \
     434           0 :     if( FD_UNLIKELY( !fd_pb_lp_open( encoder, field_id ) ) ) return NULL; \
     435           0 :     if( FD_LIKELY( cnt*fd_pb_##type##_sz_max <                            \
     436           0 :                    fd_pb_encoder_space( encoder ) ) ) {                   \
     437           0 :       /* optimize for fast append */                                      \
     438           0 :       for( ulong i=0UL; i<cnt; i++ ) {                                    \
     439           0 :         encoder->cur = fd_pb_append_##type( encoder->cur, values[ i ] );  \
     440           0 :       }                                                                   \
     441           0 :     } else {                                                              \
     442           0 :       /* cold code */                                                     \
     443           0 :       for( ulong i=0UL; i<cnt; i++ ) {                                    \
     444           0 :         if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <                 \
     445           0 :                          fd_pb_##type##_sz_max ) ) return NULL;           \
     446           0 :         encoder->cur = fd_pb_append_##type( encoder->cur, values[ i ] );  \
     447           0 :       }                                                                   \
     448           0 :     }                                                                     \
     449           0 :     fd_pb_lp_close( encoder );                                            \
     450           0 :     return encoder;                                                       \
     451           0 :   }
     452             : 
     453             : FD_PB_PUSH_PACKED( int32,   int   )
     454             : FD_PB_PUSH_PACKED( int64,   long  )
     455             : FD_PB_PUSH_PACKED( uint32,  uint  )
     456             : FD_PB_PUSH_PACKED( uint64,  ulong )
     457             : FD_PB_PUSH_PACKED( sint32,  int   )
     458             : FD_PB_PUSH_PACKED( sint64,  long  )
     459             : 
     460             : #define FD_PB_PUSH_PACKED_STATIC( type, ctype )                           \
     461             :   FD_FN_UNUSED static fd_pb_encoder_t *                                   \
     462             :   fd_pb_push_packed_##type( fd_pb_encoder_t * encoder,                    \
     463             :                             uint              field_id,                   \
     464             :                             ctype const *     values,                     \
     465           0 :                             ulong             cnt ) {                     \
     466           0 :     /* write header */                                                    \
     467           0 :     if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) <                     \
     468           0 :                      (fd_pb_varint32_sz_max + fd_pb_varint64_sz_max) ) ) {\
     469           0 :       return NULL;                                                        \
     470           0 :     }                                                                     \
     471           0 :     uint  tag     = fd_pb_tag( FD_PB_WIRE_TYPE_LEN, field_id );           \
     472           0 :     if( FD_UNLIKELY( cnt>fd_pb_encoder_space( encoder ) /                 \
     473           0 :                          fd_pb_##type##_sz_max ) ) return NULL;           \
     474           0 :     ulong data_sz = cnt*fd_pb_##type##_sz_max;                            \
     475           0 :     encoder->cur  = fd_pb_append_uint32 ( encoder->cur, tag      );       \
     476           0 :     encoder->cur  = fd_pb_append_uint64 ( encoder->cur, data_sz );        \
     477           0 :     /* write data */                                                      \
     478           0 :     if( FD_UNLIKELY( fd_pb_encoder_space( encoder ) < data_sz ) ) {       \
     479           0 :       return NULL;                                                        \
     480           0 :     }                                                                     \
     481           0 :     fd_memcpy( encoder->cur, values, data_sz );                           \
     482           0 :     encoder->cur += data_sz;                                              \
     483           0 :     return encoder;                                                       \
     484           0 :   }
     485             : 
     486             : FD_PB_PUSH_PACKED_STATIC( fixed32, uint  )
     487             : FD_PB_PUSH_PACKED_STATIC( fixed64, ulong )
     488             : 
     489             : #undef FD_PB_PUSH_PACKED
     490             : 
     491             : static inline fd_pb_encoder_t *
     492             : fd_pb_push_packed_float( fd_pb_encoder_t * encoder,
     493             :                          uint              field_id,
     494             :                          float const *     value,
     495           0 :                          ulong             cnt ) {
     496           0 :   return fd_pb_push_packed_fixed32( encoder, field_id, fd_type_pun_const( value ), cnt );
     497           0 : }
     498             : 
     499             : static inline fd_pb_encoder_t *
     500             : fd_pb_push_packed_double( fd_pb_encoder_t * encoder,
     501             :                           uint              field_id,
     502             :                           double const *    value,
     503           0 :                           ulong             cnt ) {
     504           0 :   return fd_pb_push_packed_fixed64( encoder, field_id, fd_type_pun_const( value ), cnt );
     505           0 : }
     506             : 
     507             : FD_PROTOTYPES_END
     508             : 
     509             : #endif /* HEADER_fd_src_ballet_pb_fd_pb_encode_h */

Generated by: LCOV version 1.14