LCOV - code coverage report
Current view: top level - waltz/h2 - fd_h2_rbuf.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 144 163 88.3 %
Date: 2026-08-21 04:38:30 Functions: 53 252 21.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_waltz_h2_fd_h2_rbuf_h
       2             : #define HEADER_fd_src_waltz_h2_fd_h2_rbuf_h
       3             : 
       4             : /* fd_h2_rbuf.h provides a byte oriented unaligend ring buffer. */
       5             : 
       6             : #include "fd_h2_base.h"
       7             : #include "../../util/log/fd_log.h"
       8             : 
       9             : struct fd_h2_rbuf {
      10             :   uchar * buf0;    /* points to first byte of buffer */
      11             :   uchar * buf1;    /* points one past last byte of buffer */
      12             :   uchar * lo;      /* in [buf0,buf1) */
      13             :   uchar * hi;      /* in [buf0,buf1) */
      14             :   ulong   lo_off;
      15             :   ulong   hi_off;
      16             :   ulong   bufsz;
      17             : };
      18             : 
      19             : FD_PROTOTYPES_BEGIN
      20             : 
      21             : /* fd_h2_rbuf_init initializes an h2_rbuf backed by the given buffer.
      22             :    On return, h2_rbuf has a read-write interested in buf.  bufsz has no
      23             :    alignment requirements. */
      24             : 
      25             : static inline fd_h2_rbuf_t *
      26             : fd_h2_rbuf_init( fd_h2_rbuf_t * rbuf,
      27             :                  void *         buf,
      28         192 :                  ulong          bufsz ) {
      29         192 :   *rbuf = (fd_h2_rbuf_t) {
      30         192 :     .buf0  = (uchar *)buf,
      31         192 :     .buf1  = (uchar *)buf+bufsz,
      32         192 :     .lo    = (uchar *)buf,
      33         192 :     .hi    = (uchar *)buf,
      34         192 :     .bufsz = bufsz
      35         192 :   };
      36         192 :   return rbuf;
      37         192 : }
      38             : 
      39             : /* fd_h2_rbuf_used_sz returns the number of unconsumed bytes in rbuf. */
      40             : 
      41             : FD_FN_PURE static inline ulong
      42        1812 : fd_h2_rbuf_used_sz( fd_h2_rbuf_t const * rbuf ) {
      43        1812 :   return rbuf->hi_off - rbuf->lo_off;
      44        1812 : }
      45             : 
      46             : /* fd_h2_rbuf_free_sz returns the number of bytes that can be appended
      47             :    using fd_h2_rbuf_push. */
      48             : 
      49             : FD_FN_PURE static inline ulong
      50         951 : fd_h2_rbuf_free_sz( fd_h2_rbuf_t const * rbuf ) {
      51         951 :   long used = (long)fd_h2_rbuf_used_sz( rbuf );
      52         951 :   return (ulong)fd_long_max( 0L, rbuf->buf1 - rbuf->buf0 - used );
      53         951 : }
      54             : 
      55             : /* fd_h2_rbuf_validate_private validates invariants of rbuf. */
      56             : static void
      57             : fd_h2_rbuf_validate_private( fd_h2_rbuf_t * rbuf );
      58             : 
      59             : /* fd_h2_rbuf_push appends a series of newly received bytes into rbuf.
      60             :    Returns chunk_sz.
      61             : 
      62             :    WARNING: The caller must not pass a chunk_sz larger than
      63             :    fd_h2_rbuf_free_sz bytes. */
      64             : 
      65             : static inline void
      66             : fd_h2_rbuf_push( fd_h2_rbuf_t * rbuf,
      67             :                  void const *   chunk,
      68         162 :                  ulong          chunk_sz ) {
      69         162 :   FD_CHECK_CRIT( chunk_sz<=fd_h2_rbuf_free_sz( rbuf ), "out of memory" );
      70         162 :   uchar * buf0 = rbuf->buf0;
      71         162 :   uchar * buf1 = rbuf->buf1;
      72         162 :   uchar * lo   = rbuf->lo;
      73         162 :   uchar * hi   = rbuf->hi;
      74         162 :   rbuf->hi_off += chunk_sz;
      75             : 
      76         162 :   if( FD_UNLIKELY( hi+chunk_sz > rbuf->buf1 ) ) {
      77             :     /* Split copy */
      78           3 :     if( FD_UNLIKELY( lo>hi ) ) {
      79           0 :       FD_LOG_CRIT(( "rbuf overflow: buf_sz=%lu lo=%ld hi=%ld chunk_sz=%lu",
      80           0 :                     rbuf->bufsz, rbuf->lo-buf0, rbuf->hi-buf0, chunk_sz ));
      81           0 :     }
      82           3 :     ulong part1 = (ulong)( buf1-hi        );
      83           3 :     ulong part2 = (ulong)( chunk_sz-part1 );
      84           3 :     fd_memcpy( hi,                    chunk,         part1 );
      85           3 :     fd_memcpy( buf0, (void *)( (ulong)chunk+part1 ), part2 );
      86           3 :     rbuf->hi = buf0+part2;
      87           3 :     fd_h2_rbuf_validate_private( rbuf );
      88           3 :     return;
      89           3 :   }
      90             : 
      91             :   /* One-shot copy */
      92         159 :   uchar * new_hi = hi+chunk_sz;
      93         159 :   if( new_hi==buf1 ) new_hi = buf0;
      94         159 :   fd_memcpy( hi, chunk, chunk_sz );
      95         159 :   rbuf->hi = new_hi;
      96         159 :   fd_h2_rbuf_validate_private( rbuf );
      97         159 :   return;
      98         162 : }
      99             : 
     100             : /* fd_h2_rbuf_peek_used returns a pointer to the first contiguous
     101             :    fragment of unconsumed data.  *sz is set to the number of contiguous
     102             :    bytes starting at rbuf->lo.  *split_sz is set to the number of bytes
     103             :    that are unconsumed, but in a separate fragment.  The caller may
     104             :    mangle bytes in [retval,retval+sz) if it consumes these bytes
     105             :    immediately afterwards. */
     106             : 
     107             : static inline uchar *
     108             : fd_h2_rbuf_peek_used( fd_h2_rbuf_t * rbuf,
     109             :                       ulong *        sz,
     110         291 :                       ulong *        split_sz ) {
     111         291 :   ulong used_sz = fd_h2_rbuf_used_sz( rbuf );
     112         291 :   uchar * buf0 = rbuf->buf0;
     113         291 :   uchar * buf1 = rbuf->buf1;
     114         291 :   uchar * lo   = rbuf->lo;
     115         291 :   uchar * hi   = rbuf->hi;
     116         291 :   uchar * end  = lo+used_sz;
     117             :   /* FIXME make this branchless */
     118         291 :   if( end<=buf1 ) {
     119         273 :     *sz       = (ulong)( used_sz );
     120         273 :     *split_sz = 0UL;
     121         273 :   } else {
     122          18 :     *sz       = (ulong)( buf1 - lo   );
     123          18 :     *split_sz = (ulong)( hi   - buf0 );
     124          18 :   }
     125         291 :   return lo;
     126         291 : }
     127             : 
     128             : /* fd_h2_rbuf_peek_free is like fd_h2_rbuf_peek_used, but refers to the
     129             :    free region. */
     130             : 
     131             : static inline uchar *
     132             : fd_h2_rbuf_peek_free( fd_h2_rbuf_t * rbuf,
     133             :                       ulong *        sz,
     134         276 :                       ulong *        split_sz ) {
     135         276 :   ulong free_sz = fd_h2_rbuf_free_sz( rbuf );
     136         276 :   uchar * buf0 = rbuf->buf0;
     137         276 :   uchar * buf1 = rbuf->buf1;
     138         276 :   uchar * lo   = rbuf->lo;
     139         276 :   uchar * hi   = rbuf->hi;
     140         276 :   uchar * end  = hi+free_sz;
     141             :   /* FIXME make this branchless */
     142         276 :   if( end<=buf1 ) {
     143         159 :     *sz       = (ulong)( free_sz );
     144         159 :     *split_sz = 0UL;
     145         159 :   } else {
     146         117 :     *sz       = (ulong)( buf1 - hi );
     147         117 :     *split_sz = (ulong)( lo - buf0 );
     148         117 :   }
     149         276 :   return hi;
     150         276 : }
     151             : 
     152             : /* fd_h2_rbuf_skip frees n bytes from rbuf.  Freeing more bytes than
     153             :    returned by fd_h2_rbuf_used_sz corrupts the buffer state. */
     154             : 
     155             : static inline void
     156             : fd_h2_rbuf_skip( fd_h2_rbuf_t * rbuf,
     157          33 :                  ulong          n ) {
     158          33 :   uchar * lo    = rbuf->lo;
     159          33 :   ulong   bufsz = rbuf->bufsz;
     160          33 :   uchar * buf1  = rbuf->buf1;
     161          33 :   rbuf->lo_off += n;
     162          33 :   lo += n;
     163          33 :   if( FD_UNLIKELY( lo>=buf1 ) ) {
     164           3 :     lo -= bufsz;
     165           3 :   }
     166          33 :   rbuf->lo = lo;
     167          33 :   fd_h2_rbuf_validate_private( rbuf );
     168          33 : }
     169             : 
     170             : /* fd_h2_rbuf_alloc marks the next n free bytes as used. */
     171             : 
     172             : static inline void
     173             : fd_h2_rbuf_alloc( fd_h2_rbuf_t * rbuf,
     174           3 :                    ulong          n ) {
     175           3 :   uchar * hi    = rbuf->hi;
     176           3 :   ulong   bufsz = rbuf->bufsz;
     177           3 :   uchar * buf1  = rbuf->buf1;
     178           3 :   rbuf->hi_off += n;
     179           3 :   hi += n;
     180           3 :   if( FD_UNLIKELY( hi>=buf1 ) ) {
     181           0 :     hi -= bufsz;
     182           0 :   }
     183           3 :   rbuf->hi = hi;
     184           3 :   fd_h2_rbuf_validate_private( rbuf );
     185           3 : }
     186             : 
     187             : /* fd_h2_rbuf_pop consumes n bytes from rbuf.  n is the number of bytes
     188             :    to consume.  n is assumed to be <= fd_h2_rbuf_used(rbuf).  scratch
     189             :    points to scratch memory with space for n bytes.
     190             : 
     191             :    If the bytes are available contiguously in rbuf, returns a pointer to
     192             :    them.  Otherwise, the bytes are copied into scratch.  The returned
     193             :    pointer is valid until the next mutating rbuf operation. */
     194             : 
     195             : static inline uchar *
     196             : fd_h2_rbuf_pop( fd_h2_rbuf_t * rbuf,
     197             :                 uchar *        scratch,
     198           9 :                 ulong          n ) {
     199           9 :   FD_CHECK_CRIT( n<=fd_h2_rbuf_used_sz( rbuf ), "invariant violation" );
     200           9 :   uchar * lo    = rbuf->lo;
     201           9 :   uchar * buf0  = rbuf->buf0;
     202           9 :   uchar * buf1  = rbuf->buf1;
     203           9 :   ulong   bufsz = rbuf->bufsz;
     204           9 :   uchar * ret   = lo;
     205           9 :   rbuf->lo_off += n;
     206           9 :   uchar * end = lo+n;
     207           9 :   if( FD_UNLIKELY( (lo+n)>=buf1 ) ) {
     208           0 :     end -= bufsz;
     209           0 :   }
     210           9 :   if( FD_UNLIKELY( (lo+n)>buf1 ) ) {
     211           0 :     ulong part0 = (ulong)( buf1-lo );
     212           0 :     ulong part1 = n-part0;
     213           0 :     fd_memcpy( scratch,       lo,   part0 );
     214           0 :     fd_memcpy( scratch+part0, buf0, part1 );
     215           0 :     ret = scratch;
     216           0 :   }
     217           9 :   rbuf->lo = end;
     218           9 :   fd_h2_rbuf_validate_private( rbuf );
     219           9 :   return ret;
     220           9 : }
     221             : 
     222             : static inline void
     223             : fd_h2_rbuf_pop_copy( fd_h2_rbuf_t * rbuf,
     224             :                      void *         out,
     225          69 :                      ulong          n ) {
     226          69 :   FD_CHECK_CRIT( n<=fd_h2_rbuf_used_sz( rbuf ), "invariant violation" );
     227          69 :   uchar * lo    = rbuf->lo;
     228          69 :   uchar * buf0  = rbuf->buf0;
     229          69 :   uchar * buf1  = rbuf->buf1;
     230          69 :   ulong   bufsz = rbuf->bufsz;
     231          69 :   rbuf->lo_off += n;
     232          69 :   uchar * end = lo+n;
     233          69 :   if( FD_UNLIKELY( (lo+n)>=buf1 ) ) {
     234           0 :     end -= bufsz;
     235           0 :   }
     236          69 :   if( FD_UNLIKELY( (lo+n)>buf1 ) ) {
     237           0 :     ulong part0 = (ulong)( buf1-lo );
     238           0 :     ulong part1 = n-part0;
     239           0 :     fd_memcpy(                  out,         lo,   part0 );
     240           0 :     fd_memcpy( (void *)( (ulong)out+part0 ), buf0, part1 );
     241          69 :   } else {
     242          69 :     fd_memcpy( out, lo, n );
     243          69 :   }
     244          69 :   rbuf->lo = end;
     245          69 :   fd_h2_rbuf_validate_private( rbuf );
     246          69 : }
     247             : 
     248             : FD_FN_PURE static inline int
     249         114 : fd_h2_rbuf_is_empty( fd_h2_rbuf_t const * rbuf ) {
     250         114 :   return rbuf->lo_off==rbuf->hi_off;
     251         114 : }
     252             : 
     253             : static void
     254         276 : fd_h2_rbuf_validate_private( fd_h2_rbuf_t * rbuf ) {
     255         276 :   ulong used = fd_h2_rbuf_used_sz( rbuf );
     256         276 :   ulong free = fd_h2_rbuf_free_sz( rbuf );
     257         276 :   FD_DCHECK_CRIT( used <= rbuf->bufsz,        "corrupt h2_rbuf" );
     258         276 :   FD_DCHECK_CRIT( free <= rbuf->bufsz,        "corrupt h2_rbuf" );
     259         276 :   FD_DCHECK_CRIT( used + free == rbuf->bufsz, "corrupt h2_rbuf" );
     260             : 
     261         276 :   ulong used0, used1;
     262         276 :   uchar * used_p = fd_h2_rbuf_peek_used( rbuf, &used0, &used1 );
     263         276 :   FD_DCHECK_CRIT( used_p == rbuf->lo,    "corrupt h2_rbuf" );
     264         276 :   FD_DCHECK_CRIT( used0 + used1 == used, "corrupt h2_rbuf" );
     265             : 
     266         276 :   ulong free0, free1;
     267         276 :   uchar * free_p = fd_h2_rbuf_peek_free( rbuf, &free0, &free1 );
     268         276 :   FD_DCHECK_CRIT( free_p == rbuf->hi,    "corrupt h2_rbuf" );
     269         276 :   FD_DCHECK_CRIT( free0 + free1 == free, "corrupt h2_rbuf" );
     270         276 : }
     271             : 
     272             : FD_PROTOTYPES_END
     273             : 
     274             : #endif /* HEADER_fd_src_waltz_h2_fd_h2_rbuf_h */

Generated by: LCOV version 1.14