LCOV - code coverage report
Current view: top level - funk - fd_funk_val.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 148 173 85.5 %
Date: 2025-01-08 12:08:44 Functions: 4 5 80.0 %

          Line data    Source code
       1             : #include "fd_funk.h"
       2             : 
       3             : fd_funk_rec_t *
       4             : fd_funk_val_copy( fd_funk_rec_t * rec,
       5             :                   void const *    data,
       6             :                   ulong           sz,
       7             :                   ulong           sz_est,
       8             :                   fd_alloc_t *    alloc,
       9             :                   fd_wksp_t *     wksp,
      10    74953857 :                   int *           opt_err ) {
      11             : 
      12             :   /* Check input args */
      13             : 
      14    74953857 :   sz_est = fd_ulong_if( !sz_est, sz, sz_est ); /* Use reasonable default for sz_est */
      15             : 
      16    74953857 :   ulong d0 = (ulong)data;
      17    74953857 :   ulong d1 = d0 + sz;
      18             : 
      19    74953857 :   if( FD_UNLIKELY( (!rec) | ((!data) & (!!sz)) | (!alloc) | (!wksp) |         /* NULL rec,NULL data w sz!=0,NULL alloc,NULL wksp */
      20    74953857 :                    (d1<d0) | (sz>sz_est) | (sz_est>FD_FUNK_REC_VAL_MAX) ) ) { /* data wraps, too large sz, too large sz_est */
      21     7450896 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
      22     7450896 :     return NULL;
      23     7450896 :   }
      24             : 
      25    67502961 :   ulong val_max   = (ulong)rec->val_max;
      26    67502961 :   ulong val_gaddr = rec->val_gaddr;
      27             : 
      28    67502961 :   ulong v0 = val_max ? (ulong)fd_wksp_laddr_fast( wksp, val_gaddr ) : 0UL; /* Technically don't need trinary */
      29    67502961 :   ulong v1 = v0 + val_max;
      30             : 
      31    67502961 :   if( FD_UNLIKELY( ((!!sz) & (!!val_max) & (!((d1<=v0) | (d0>=v1)))) |     /* data overlaps val alloc */
      32    67502961 :                    (!!(rec->flags & FD_FUNK_REC_FLAG_ERASE))         ) ) { /* marked erase */
      33    55709796 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
      34    55709796 :     return NULL;
      35    55709796 :   }
      36             : 
      37    11793165 :   uchar * val = (uchar *)v0;
      38             : 
      39    11793165 :   if( FD_UNLIKELY( !sz_est ) ) {
      40             : 
      41             :     /* User requested to flush the existing value */
      42             : 
      43     3449085 :     fd_funk_val_flush( rec, alloc, wksp );
      44             : 
      45     8344080 :   } else {
      46             : 
      47             :     /* User requested to allocate at least sz_est for value.  Allocate
      48             :        space for the copy.  If allocation fails, we do any remaining
      49             :        data copy into the current allocation (if possible).  Otherwise,
      50             :        we copy the data into the new space, free the old space (if any)
      51             :        and switch the value to the new space.  We do the alloc first
      52             :        such that if it fails, we haven't affected the state. */
      53             : 
      54     8344080 :     ulong   new_val_max;
      55     8344080 :     uchar * new_val = (uchar *)fd_alloc_malloc_at_least( alloc, FD_FUNK_VAL_ALIGN, sz_est, &new_val_max );
      56             : 
      57     8344080 :     if( FD_UNLIKELY( !new_val ) ) { /* Fallback on in-place */
      58             : 
      59           0 :       new_val_max = rec->val_max;
      60           0 :       if( FD_UNLIKELY( new_val_max < sz ) ) { /* Fallback failed too */
      61           0 :         fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_MEM );
      62           0 :         return NULL;
      63           0 :       }
      64             : 
      65     8344080 :     } else { /* Out-of-place */
      66             : 
      67     8344080 :       rec->val_max   = (uint)fd_ulong_min( new_val_max, FD_FUNK_REC_VAL_MAX );
      68     8344080 :       rec->val_gaddr = fd_wksp_gaddr_fast( wksp, new_val );
      69             : 
      70     8344080 :       if( val ) fd_alloc_free( alloc, val );
      71     8344080 :       val = new_val;
      72             : 
      73     8344080 :     }
      74             : 
      75             :     /* At this point we have room for the copy, do the copy, clear out
      76             :        trailing padding to be on the safe side and update the value
      77             :        size. */
      78             : 
      79     8344080 :     if( FD_LIKELY( sz ) ) fd_memcpy( val, data, sz );
      80     8344080 :     fd_memset( val + sz, 0, new_val_max - sz );
      81     8344080 :     rec->val_sz = (uint)sz;
      82             : 
      83     8344080 :   }
      84             : 
      85    11793165 :   fd_int_store_if( !!opt_err, opt_err, FD_FUNK_SUCCESS );
      86    11793165 :   return rec;
      87    11793165 : }
      88             : 
      89             : fd_funk_rec_t *
      90             : fd_funk_val_append( fd_funk_rec_t * rec,
      91             :                     void const *    data,
      92             :                     ulong           sz,
      93             :                     fd_alloc_t *    alloc,
      94             :                     fd_wksp_t *     wksp,
      95  4940016336 :                     int *           opt_err ) {
      96             : 
      97             :   /* Check input args */
      98             : 
      99  4940016336 :   if( FD_UNLIKELY( !sz ) ) { /* Empty append request */
     100   635276898 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNK_SUCCESS );
     101   635276898 :     return rec;
     102   635276898 :   }
     103             : 
     104  4304739438 :   ulong d0 = (ulong)data;
     105  4304739438 :   ulong d1 = d0 + sz;
     106             : 
     107  4304739438 :   if( FD_UNLIKELY( (!rec) | (!d0) | (d1<d0) | (!alloc) | (!wksp) ) ) { /* NULL rec, NULL data, data wrap, NULL alloc, NULL wksp */
     108  3811661388 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
     109  3811661388 :     return NULL;
     110  3811661388 :   }
     111             : 
     112   493078050 :   ulong val_sz    = (ulong)rec->val_sz;
     113   493078050 :   ulong val_max   = (ulong)rec->val_max;
     114   493078050 :   ulong val_gaddr = rec->val_gaddr;
     115             : 
     116   493078050 :   ulong new_val_sz = val_sz + sz;
     117             : 
     118   493078050 :   ulong v0 = val_max ? (ulong)fd_wksp_laddr_fast( wksp, val_gaddr ) : 0UL; /* Technically don't need trinary */
     119   493078050 :   ulong v1 = v0 + val_max;
     120             : 
     121   493078050 :   if( FD_UNLIKELY( (new_val_sz<val_sz) | (new_val_sz>FD_FUNK_REC_VAL_MAX) |     /* too large sz */
     122   493078050 :                    ((!!val_max) & (!((d1<=v0) | (d0>=v1))))               |     /* data overlaps with val alloc */
     123   493078050 :                    (!!(rec->flags & FD_FUNK_REC_FLAG_ERASE))              ) ) { /* marked erase */
     124   419407206 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
     125   419407206 :     return NULL;
     126   419407206 :   }
     127             : 
     128    73670844 :   uchar * val = (uchar *)v0;
     129             : 
     130             :   /* If we need to resize val or do the initial allocation of val),
     131             :      compute target new size, allocate a new region of at least the
     132             :      target new size, copy the current value into it, free the old one
     133             :      and update the value.  We use malloc_at_least with 1 alignment to
     134             :      pack wksp memory as tight as possible. */
     135             : 
     136    73670844 :   if( FD_UNLIKELY( new_val_sz > val_max ) ) {
     137             : 
     138    73670844 :     ulong new_val_max = fd_ulong_min( fd_alloc_max_expand( val_max, FD_FUNK_VAL_ALIGN, new_val_sz ), FD_FUNK_REC_VAL_MAX );
     139    73670844 :     if( FD_UNLIKELY( new_val_max<=val_max ) ) { /* Already expanded as much as possible */
     140           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
     141           0 :       return NULL;
     142           0 :     }
     143             : 
     144    73670844 :     uchar * new_val = (uchar *)fd_alloc_malloc_at_least( alloc, FD_FUNK_VAL_ALIGN, new_val_max, &new_val_max );
     145    73670844 :     if( FD_UNLIKELY( !new_val ) ) { /* Allocation failure */
     146           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_MEM );
     147           0 :       return NULL;
     148           0 :     }
     149             : 
     150    73670844 :     if( val_sz ) fd_memcpy( new_val, val, val_sz ); /* Copy the existing val */
     151    73670844 :     fd_memset( new_val + val_sz, 0, new_val_max - val_sz ); /* Clear out trailing padding to be on the safe side */
     152    73670844 :     fd_alloc_free( alloc, val ); /* Free the old val */
     153             : 
     154    73670844 :     rec->val_max   = (uint)fd_ulong_min( new_val_max, FD_FUNK_REC_VAL_MAX );
     155    73670844 :     rec->val_gaddr = fd_wksp_gaddr_fast( wksp, new_val );
     156             : 
     157    73670844 :     val = new_val;
     158             : 
     159    73670844 :   }
     160             : 
     161             :   /* At this point, we have room to do the append.  Do the append.
     162             :      Trailing padding was cleared out previously. */
     163             : 
     164    73670844 :   fd_memcpy( val+val_sz, data, sz );
     165             : 
     166    73670844 :   rec->val_sz = (uint)new_val_sz;
     167             : 
     168    73670844 :   fd_int_store_if( !!opt_err, opt_err, FD_FUNK_SUCCESS );
     169    73670844 :   return rec;
     170    73670844 : }
     171             : 
     172             : fd_funk_rec_t *
     173             : fd_funk_val_truncate( fd_funk_rec_t * rec,
     174             :                       ulong           new_val_sz,
     175             :                       fd_alloc_t *    alloc,
     176             :                       fd_wksp_t *     wksp,
     177    98902581 :                       int *           opt_err ) {
     178             : 
     179             :   /* Check input args */
     180             : 
     181    98902581 :   if( FD_UNLIKELY( (!rec) | (new_val_sz>FD_FUNK_REC_VAL_MAX) | (!alloc) | (!wksp) ) ||  /* NULL rec,too big,NULL alloc,NULL wksp */
     182    98902581 :       FD_UNLIKELY( rec->flags & FD_FUNK_REC_FLAG_ERASE                            ) ) { /* Marked erase */
     183    54778434 :     fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_INVAL );
     184    54778434 :     return NULL;
     185    54778434 :   }
     186             : 
     187    44124147 :   ulong val_sz = (ulong)rec->val_sz;
     188             : 
     189    44124147 :   if( FD_UNLIKELY( !new_val_sz ) ) {
     190             : 
     191             :     /* User asked to truncate to 0.  Flush the any existing value. */
     192             : 
     193     1201311 :     fd_funk_val_flush( rec, alloc, wksp );
     194             : 
     195    42922836 :   } else if( FD_LIKELY( new_val_sz > val_sz ) ) {
     196             : 
     197             :     /* User requested to increase the value size.  We presume they are
     198             :        asking for a specific size (as opposed to bumping up the size ala
     199             :        append) so we don't build in extra padding to amortize the cost
     200             :        of future truncates.  Note that new_val_sz is at least 1 at this
     201             :        point but val_sz / val_gaddr could be zero / zero. */
     202             : 
     203     3420279 :     ulong   val_max   = (ulong)rec->val_max;
     204     3420279 :     ulong   val_gaddr = rec->val_gaddr;
     205     3420279 :     uchar * val       = val_max ? fd_wksp_laddr_fast( wksp, val_gaddr ) : NULL; /* TODO: branchless */
     206             : 
     207     3420279 :     ulong   new_val_max;
     208     3420279 :     uchar * new_val = (uchar *)fd_alloc_malloc_at_least( alloc, FD_FUNK_VAL_ALIGN, new_val_sz, &new_val_max );
     209     3420279 :     if( FD_UNLIKELY( !new_val ) ) { /* Allocation failure! */
     210           0 :       fd_int_store_if( !!opt_err, opt_err, FD_FUNK_ERR_MEM );
     211           0 :       return NULL;
     212           0 :     }
     213             : 
     214     3420279 :     if( val_sz ) fd_memcpy( new_val, val, val_sz ); /* Copy the existing value */
     215     3420279 :     fd_memset( new_val + val_sz, 0, new_val_max - val_sz ); /* Clear out trailing padding to be on the safe side */
     216             : 
     217             :     /* Order of updates is important for fd_funk_val_safe */
     218     3420279 :     rec->val_gaddr = fd_wksp_gaddr_fast( wksp, new_val );
     219     3420279 :     rec->val_sz    = (uint)new_val_sz;
     220     3420279 :     rec->val_max   = (uint)fd_ulong_min( new_val_max, FD_FUNK_REC_VAL_MAX );
     221             : 
     222     3420279 :     if( val ) fd_alloc_free( alloc, val ); /* Free the old value (if any) */
     223             : 
     224    39502557 :   } else {
     225             : 
     226             :     /* User requested to reduce the value size or keep it the same.
     227             :        Even though we could in principle just set rec->val_sz to its new
     228             :        value, we do a new allocation as it is still (usually) O(1),
     229             :        presumably the caller knew it wanted a particular size and that
     230             :        the resize might free up resources needed in the future.  Note
     231             :        that new_val_sz is at least 1, val_sz at least 2 and val_gaddr is
     232             :        non-zero at this point. */
     233             : 
     234    39502557 :     uchar * val = (uchar *)fd_wksp_laddr_fast( wksp, rec->val_gaddr );
     235             : 
     236    39502557 :     ulong   new_val_max;
     237    39502557 :     uchar * new_val = (uchar *)fd_alloc_malloc_at_least( alloc, FD_FUNK_VAL_ALIGN, new_val_sz, &new_val_max );
     238             : 
     239    39502557 :     if( FD_UNLIKELY( !new_val ) ) { /* Fallback on in-place */
     240             : 
     241           0 :       new_val_max = (ulong)rec->val_max;
     242             : 
     243           0 :       fd_memset( val + new_val_sz, 0, new_val_max - new_val_sz ); /* Clear out the trailing padding to be on the safe side */
     244             : 
     245           0 :       rec->val_sz = (uint)new_val_sz;
     246             : 
     247    39502557 :     } else { /* Out of place */
     248             : 
     249    39502557 :       fd_memcpy( new_val, val, new_val_sz ); /* Copy the (truncated) existing value */
     250    39502557 :       fd_memset( new_val + new_val_sz, 0, new_val_max - new_val_sz ); /* Clear out the trailing padding to be on the safe side */
     251             : 
     252             :       /* Order of updates is important for fd_funk_val_safe */
     253    39502557 :       rec->val_sz    = (uint)new_val_sz;
     254    39502557 :       rec->val_max   = (uint)fd_ulong_min( new_val_max, FD_FUNK_REC_VAL_MAX );
     255    39502557 :       rec->val_gaddr = fd_wksp_gaddr_fast( wksp, new_val );
     256             : 
     257    39502557 :       if( val ) fd_alloc_free( alloc, val ); /* Free the old value (if any) */
     258             : 
     259    39502557 :     }
     260             : 
     261    39502557 :   }
     262             : 
     263    44124147 :   fd_int_store_if( !!opt_err, opt_err, FD_FUNK_SUCCESS );
     264    44124147 :   return rec;
     265    44124147 : }
     266             : 
     267             : void *
     268             : fd_funk_val_safe( fd_funk_rec_t const * rec,     /* Assumes pointer in caller's address space to a live funk record */
     269             :                   fd_wksp_t const *     wksp,
     270             :                   fd_valloc_t           valloc,
     271           0 :                   ulong *               result_len ) {
     272           0 :   uint val_sz = rec->val_sz;
     273           0 :   *result_len = val_sz;
     274           0 :   if( !val_sz ) return NULL;
     275           0 :   void * res = fd_valloc_malloc( valloc, FD_FUNK_VAL_ALIGN, val_sz );
     276             :   /* Note that this memcpy may copy recently freed memory, but it
     277             :      won't crash, which is the important thing */
     278           0 :   fd_memcpy( res, fd_wksp_laddr_fast( wksp, rec->val_gaddr ), val_sz );
     279           0 :   return res;
     280           0 : }
     281             : 
     282             : int
     283    22275108 : fd_funk_val_verify( fd_funk_t * funk ) {
     284    22275108 :   fd_wksp_t *     wksp     = fd_funk_wksp( funk );          /* Previously verified */
     285    22275108 :   fd_funk_rec_t * rec_map  = fd_funk_rec_map( funk, wksp ); /* Previously verified */
     286    22275108 :   ulong           wksp_tag = funk->wksp_tag;                /* Previously verified */
     287             : 
     288             :   /* At this point, rec_map has been extensively verified */
     289             : 
     290  2848661277 : # define TEST(c) do {                                                                           \
     291  2848661277 :     if( FD_UNLIKELY( !(c) ) ) { FD_LOG_WARNING(( "FAIL: %s", #c )); return FD_FUNK_ERR_INVAL; } \
     292  2848661277 :   } while(0)
     293             : 
     294             :   /* Iterate over all records in use */
     295             : 
     296    22275108 :   for( fd_funk_rec_map_iter_t iter = fd_funk_rec_map_iter_init( rec_map );
     297   903401892 :        !fd_funk_rec_map_iter_done( rec_map, iter );
     298   881126784 :        iter = fd_funk_rec_map_iter_next( rec_map, iter ) ) {
     299   881126784 :     fd_funk_rec_t * rec = fd_funk_rec_map_iter_ele( rec_map, iter );
     300             : 
     301             :     /* Make sure values look sane */
     302             :     /* TODO: consider doing an alias analysis on allocated values?
     303             :        (tricky to do algo efficient in place) */
     304             : 
     305   881126784 :     ulong val_sz    = (ulong)rec->val_sz;
     306   881126784 :     ulong val_max   = (ulong)rec->val_max;
     307   881126784 :     ulong val_gaddr = rec->val_gaddr;
     308             : 
     309   881126784 :     TEST( val_sz<=val_max );
     310             : 
     311   881126784 :     if( rec->flags & FD_FUNK_REC_FLAG_ERASE ) {
     312   355153215 :       TEST( !val_max   );
     313   355153215 :       TEST( !val_gaddr );
     314   525973569 :     } else {
     315   525973569 :       TEST( val_max<=FD_FUNK_REC_VAL_MAX );
     316   525973569 :       if( !val_gaddr ) TEST( !val_max );
     317   205280925 :       else {
     318   205280925 :         TEST( (0UL<val_max) & (val_max<=FD_FUNK_REC_VAL_MAX) );
     319   205280925 :         TEST( fd_wksp_tag( wksp, val_gaddr )==wksp_tag );
     320   205280925 :       }
     321   525973569 :     }
     322   881126784 :   }
     323             : 
     324    22275108 : # undef TEST
     325             : 
     326    22275108 :   return FD_FUNK_SUCCESS;
     327    22275108 : }

Generated by: LCOV version 1.14