LCOV - code coverage report
Current view: top level - funk - fd_funk_val.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 52 53 98.1 %
Date: 2024-11-13 11:58:15 Functions: 22 1056 2.1 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_funk_fd_funk_val_h
       2             : #define HEADER_fd_src_funk_fd_funk_val_h
       3             : 
       4             : /* This provides APIs for managing funk record values.  It is generally
       5             :    not meant to be included directly.  Use fd_funk.h instead. */
       6             : 
       7             : #include "fd_funk_rec.h" /* Includes fd_funk_txn.h, fd_funk_base.h */
       8             : 
       9             : /* FD_FUNK_REC_VAL_MAX is the maximum size of a record value. */
      10             : 
      11   463226412 : #define FD_FUNK_REC_VAL_MAX UINT_MAX
      12   265110030 : #define FD_FUNK_VAL_ALIGN 8UL
      13             : 
      14             : FD_PROTOTYPES_BEGIN
      15             : 
      16             : /* Accessors */
      17             : 
      18             : /* fd_funk_val_{sz,max} returns the current size of the value associated
      19             :    with a record and the amount of wksp allocated currently for a value.
      20             :    Assumes funk is a current local join.  These value might change on
      21             :    subsequent calls if the record is resized.
      22             :    0<=sz<=max<=FD_FUNK_REC_VAL_MAX. */
      23             : 
      24             : FD_FN_PURE static inline ulong                /* Current size of the record's value in bytes */
      25   284132988 : fd_funk_val_sz( fd_funk_rec_t const * rec ) { /* Assumes pointer in caller's address space to a live funk record */
      26   284132988 :   return (ulong)rec->val_sz; /* Covers the marked ERASE case too */
      27   284132988 : }
      28             : 
      29             : FD_FN_PURE static inline ulong                 /* Current size of the record's value allocation in bytes */
      30   384159501 : fd_funk_val_max( fd_funk_rec_t const * rec ) { /* Assumes pointer in caller's address space to a live funk record */
      31   384159501 :   return (ulong)rec->val_max; /* Covers the marked ERASE case too */
      32   384159501 : }
      33             : 
      34             : /* fd_funk_val returns a pointer in the caller's address space to the
      35             :    current value associated with a record.  fd_funk_rec_val_const is a
      36             :    const-correct version.  There are sz bytes at the returned pointer.
      37             :    IMPORTANT SAFETY TIP!  There are _no_ alignment guarantees on the
      38             :    returned value.  Returns NULL if the record has a zero sz (which also
      39             :    covers the case where rec has been marked ERASE).  max 0 implies val
      40             :    NULL and vice versa.  Assumes no concurrent operations on rec. */
      41             : 
      42             : FD_FN_PURE static inline void *         /* Lifetime is the lesser of rec or the value size is modified */
      43             : fd_funk_val( fd_funk_rec_t const * rec,     /* Assumes pointer in caller's address space to a live funk record */
      44    79382580 :              fd_wksp_t const *     wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */
      45    79382580 :   ulong val_gaddr = rec->val_gaddr;
      46    79382580 :   if( !val_gaddr ) return NULL; /* Covers the marked ERASE case too */ /* TODO: consider branchless */
      47    67777086 :   return fd_wksp_laddr_fast( wksp, val_gaddr );
      48    79382580 : }
      49             : 
      50             : FD_FN_PURE static inline void const *             /* Lifetime is the lesser of rec or the value size is modified */
      51             : fd_funk_val_const( fd_funk_rec_t const * rec,     /* Assumes pointer in caller's address space to a live funk record */
      52   479554812 :                    fd_wksp_t const *     wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */
      53   479554812 :   ulong val_gaddr = rec->val_gaddr;
      54   479554812 :   if( !val_gaddr ) return NULL; /* Covers the marked ERASE case too */ /* TODO: consider branchless */
      55   460284615 :   return fd_wksp_laddr_fast( wksp, val_gaddr );
      56   479554812 : }
      57             : 
      58             : /* fd_funk_val_safe copies out the record value into a buffer
      59             :  * allocated by the valloc. The result should eventually be freed by
      60             :  * the same valloc. This API is safe in the presence of concurrent writes. */
      61             : 
      62             : void *
      63             : fd_funk_val_safe( fd_funk_rec_t const * rec,     /* Assumes pointer in caller's address space to a live funk record */
      64             :                   fd_wksp_t const *     wksp,
      65             :                   fd_valloc_t           valloc,
      66             :                   ulong *               result_len );
      67             : 
      68             : 
      69             : /* fd_funk_rec_read reads bytes [off,off+sz) and returns a pointer to
      70             :    the requested data on success and NULL on failure.  Reasons for
      71             :    failure include NULL rec, 0 sz, [off,off+sz) does not overlap
      72             :    completely val, NULL wksp, marked ERASE.  Assumes no concurrent
      73             :    operations on rec.
      74             : 
      75             :    The returned pointer is in the caller's address space and, if
      76             :    non-NULL, the value at the pointer is stable for its lifetime or
      77             :    until it it is modified.
      78             : 
      79             :    IMPORTANT SAFETY TIP!  There are _no_ alignment guarantees on the
      80             :    returned value (even if off itself is aligned).
      81             : 
      82             :    Note that if reading two overlapping regions of a record, the return
      83             :    pointers can overlap (along these will also overlap the regions
      84             :    returned by the above accessors).  Further, if the region is changed
      85             :    by a below write, the value at the returned pointers will immediately
      86             :    reflect those writes.  (That is, the returned pointers are zero copy
      87             :    into the actual value data of the record.)  */
      88             : 
      89             : FD_FN_PURE static inline void const *            /* Lifetime is lesser of current local join, the record or val is resized */
      90             : fd_funk_val_read( fd_funk_rec_t const * rec,     /* Assumes pointer in caller's address space to a live funk record
      91             :                                                     (NULL returns NULL) */
      92             :                   ulong                 off,     /* Should be in [0,sz] */
      93             :                   ulong                 sz,      /* Should be in [1,val_sz-off] */
      94  2854776354 :                   fd_wksp_t const *     wksp ) { /* ==fd_funk_wksp( funk ) where funk is current local join */
      95             : 
      96  2854776354 :   ulong end = off + sz;
      97             : 
      98  2854776354 :   if( FD_UNLIKELY( (!rec) | (end<=off) | (!wksp) ) ||             /* NULL rec, sz==0 or off+sz wrapped, NULL wksp */
      99  2854776354 :       FD_UNLIKELY( (end>(ulong)rec->val_sz)      ) ) return NULL; /* Read past end (covers marked ERASE case too) */
     100             : 
     101  1656906138 :   return fd_wksp_laddr_fast( wksp, rec->val_gaddr + off );
     102  2854776354 : }
     103             : 
     104             : /* Operations */
     105             : 
     106             : /* fd_funk_rec_write writes record bytes [off,off+sz) and returns rec on
     107             :    success and NULL on failure.
     108             : 
     109             :    data points to the bytes to sz write.  [data,data+sz) should not
     110             :    overlap with the current value bytes [off,off+sz) but otherwise can
     111             :    point anywhere valid in the caller's address space.
     112             : 
     113             :    The write retains no interest in data on return.  sz 0 is considered
     114             :    a no-op regardless of anything else and immediately returns rec.
     115             : 
     116             :    Reasons for failure include NULL rec, NULL data with non-zero sz,
     117             :    [data,data+sz) wraps, [off,off+sz) does not overlap with the record
     118             :    completely, [data,data+sz) overlaps with [off,off+sz).  Assumes no
     119             :    concurrent operations on rec or data. */
     120             : 
     121             : FD_FN_UNUSED static fd_funk_rec_t *           /* Returns rec on success, NULL on failure */ /* Workaround -Winline */
     122             : fd_funk_val_write( fd_funk_rec_t *   rec,     /* Assumed in caller's address space to live funk record (NULL returns NULL) */
     123             :                    ulong             off,     /* First byte of record to write, in [0,val_sz], NULL if too large */
     124             :                    ulong             sz,      /* Number of bytes to write, 0 is a no-op, in [0,val_sz-off], NULL if too large */
     125             :                    void const *      data,    /* Assumed in caller's address space, NULL okay if sz 0 */
     126  1637541933 :                    fd_wksp_t const * wksp ) { /* ==fd_funk_wksp( funk ) where funk is current local join */
     127             : 
     128  1637541933 :   if( FD_UNLIKELY( !sz ) ) return rec; /* Empty write request */
     129             : 
     130  1440117519 :   ulong end = off + sz;
     131             : 
     132  1440117519 :   ulong d0 = (ulong)data;
     133  1440117519 :   ulong d1 = d0 + sz;
     134             : 
     135  1440117519 :   if( FD_UNLIKELY( (!rec) | (end<off) | (!data) | (d1<d0) | (!wksp) ) || /* NULL rec, off+sz wrapped, NULL data w sz!=0, data wrapped, NULL wksp */
     136  1440117519 :       FD_UNLIKELY( end > (ulong)rec->val_max                         ) ) return NULL; /* too large (covers marked ERASE case too) */
     137             : 
     138    50108889 :   ulong v0 = (ulong)fd_wksp_laddr_fast( wksp, rec->val_gaddr + off );
     139    50108889 :   ulong v1 = v0 + sz;
     140             : 
     141    50108889 :   if( FD_UNLIKELY( !((d1<=v0) | (d0>=v1)) ) ) return NULL; /* data overlaps with val */
     142             : 
     143    50108889 :   fd_memcpy( (void *)v0, data, sz );
     144             : 
     145    50108889 :   if ( FD_UNLIKELY( end > (ulong)rec->val_sz ) )
     146           0 :     rec->val_sz = (uint)end;
     147             : 
     148    50108889 :   return rec;
     149    50108889 : }
     150             : 
     151             : /* fd_funk_val_copy copies sz bytes starting at data into the record
     152             :    value, replacing the existing record value.  rec's value will be able
     153             :    to accommodate at least sz_est in the future without resizing on
     154             :    return.  If sz_est is 0 on entry, it will be be set to sz as a
     155             :    reasonable default before any argument checking.
     156             : 
     157             :    data points to the bytes to write.  [data,data+sz) should not overlap
     158             :    with the existing record but can otherwise can point anywhere valid
     159             :    in the caller's address space.
     160             : 
     161             :    This generally will resize the record value to something at least
     162             :    sz_est so this function should be assumed to kill any existing
     163             :    pointers into this record's value storage.  Note that sz_est==0 will
     164             :    set the record to the NULL val.  And sz==0 with sz_est!=0 can be used
     165             :    to preallocate record sizes with a NULL initial value.
     166             : 
     167             :    Returns rec on success and NULL on failure.  If opt_err is non-NULL,
     168             :    on return, *opt_err will hold FD_FUNK_SUCCESS if successful or a
     169             :    FD_FUNK_ERR_* code on failure.  Reasons for failure include
     170             :    FD_FUNK_ERR_INVAL (NULL rec, NULL data with non-zero sz, NULL alloc,
     171             :    NULL wksp, data region wraps, sz>sz_est, sz_est too large, rec is
     172             :    marked as ERASE, data region overlaps the existing val allocation)
     173             :    and FD_FUNK_ERR_MEM (allocation failure, need a larger wksp).  On
     174             :    failure, the current value is unchanged.
     175             : 
     176             :    Assumes no concurrent operations on rec or data.  The copy retains no
     177             :    interest in data on return. */
     178             : 
     179             : fd_funk_rec_t *                              /* Returns rec on success, NULL on failure */
     180             : fd_funk_val_copy( fd_funk_rec_t * rec,       /* Assumed in caller's address space to live funk record (NULL returns NULL) */
     181             :                   void const *    data,      /* Points to first byte to copy in caller's address space, NULL okay if sz 0 */
     182             :                   ulong           sz,        /* Number of bytes to copy, in [0,sz_est], NULL if too large */
     183             :                   ulong           sz_est,    /* Est final size, 0 means use sz, in [sz,FD_FUNK_REC_VAL_MAX], NULL if too large */
     184             :                   fd_alloc_t *    alloc,     /* ==fd_funk_alloc( funk, wksp ) */
     185             :                   fd_wksp_t *     wksp,      /* ==fd_funk_wksp( funk ) where funk is current local join */
     186             :                   int *           opt_err );  /* If non-NULL, *opt_err returns operation error code */
     187             : 
     188             : /* fd_funk_val_append appends sz bytes starting at data to the end of
     189             :    the record rec.  [data,data+sz) should not overlap with the existing
     190             :    value allocation but can otherwise can point anywhere valid in the
     191             :    caller's address space.
     192             : 
     193             :    This might need to resize the record value so this function should be
     194             :    assumed to kill any existing pointers into this record's value
     195             :    storage.  Unlike copy above and truncate below, this function does
     196             :    try to minimize the amount of value allocations it might do.
     197             : 
     198             :    Returns rec on success and NULL on failure.  If opt_err is non-NULL,
     199             :    on return, *opt_err will hold FD_FUNK_SUCCESS if successful or a
     200             :    FD_FUNK_ERR_* code on failure.  Reasons for failure include
     201             :    FD_FUNK_ERR_INVAL (NULL rec, NULL data with non-zero sz,
     202             :    [data,data+sz) wraps, NULL alloc, NULL wksp, rec marked ERASE, sz too
     203             :    large, data region overlaps with existing record value allocation)
     204             :    and FD_FUNK_ERR_MEM (allocation failure, need a larger wksp).  On
     205             :    failure, the current value is unchanged.
     206             : 
     207             :    Assumes no concurrent operations on rec or data.  The append retains
     208             :    no interest in data on return.  sz 0 is considered a no-op regardless
     209             :    of anything else and immediately returns rec / SUCCESS. */
     210             : 
     211             : fd_funk_rec_t *                                /* Returns rec on success, NULL on failure */
     212             : fd_funk_val_append( fd_funk_rec_t * rec,       /* Assumed in caller's address space to a live funk record (NULL returns NULL) */
     213             :                     void const *    data,      /* Points to first byte to append in caller's address space, NULL okay if sz 0 */
     214             :                     ulong           sz,        /* Number of bytes to append, 0 is a no-op, NULL if too large */
     215             :                     fd_alloc_t *    alloc,     /* ==fd_funk_alloc( funk, wksp ) */
     216             :                     fd_wksp_t *     wksp,      /* ==fd_funk_wksp( funk ) where funk is current local join */
     217             :                     int *           opt_err ); /* If non-NULL, *opt_err returns operation error code */
     218             : 
     219             : /* fd_funk_val_truncate resizes a record to be new_val_sz bytes in size.
     220             : 
     221             :    This function is optimized for the user knowing the actual long term
     222             :    record size when they call this.  So avoid using this to
     223             :    incrementally increase a size of a value by a constant amount over
     224             :    time.  See append above for that.
     225             : 
     226             :    Likewise, regardless of the current and new value sizes, this will
     227             :    always attempt to resize the record in order to minimize the amount
     228             :    of excess allocation used by the record.  So this function should be
     229             :    assumed to kill any existing pointers into this record's value
     230             :    storage.
     231             : 
     232             :    Returns rec on success and NULL on failure.  If opt_err is non-NULL,
     233             :    on return, *opt_err will hold FD_FUNK_SUCCESS if successful or a
     234             :    FD_FUNK_ERR_* code on failure.  Reasons for failure include
     235             :    FD_FUNK_ERR_INVAL (NULL rec, too large new_val_sz, rec is marked
     236             :    ERASE) and FD_FUNK_ERR_MEM (allocation failure, need a larger wksp).
     237             :    On failure, the current value is unchanged.
     238             : 
     239             :    Assumes no concurrent operations on rec. */
     240             : 
     241             : fd_funk_rec_t *                                   /* Returns rec on success, NULL on failure */
     242             : fd_funk_val_truncate( fd_funk_rec_t * rec,        /* Assumed in caller's address space to a live funk record (NULL returns NULL) */
     243             :                       ulong           new_val_sz, /* Should be in [0,FD_FUNK_REC_VAL_MAX] (returns NULL otherwise) */
     244             :                       fd_alloc_t *    alloc,      /* ==fd_funk_alloc( funk, wksp ) */
     245             :                       fd_wksp_t *     wksp,       /* ==fd_funk_wksp( funk ) where funk is current local join */
     246             :                       int *           opt_err );  /* If non-NULL, *opt_err returns operation error code */
     247             : 
     248             : /* fd_funk_val_speed_load sets the record value to space allocated
     249             :    from the funk speed bump. This space is never freed. This function
     250             :    is used when loading large snapshots.
     251             : 
     252             :    Returns rec on success and NULL on failure.  If opt_err is non-NULL,
     253             :    on return, *opt_err will hold FD_FUNK_SUCCESS if successful or a
     254             :    FD_FUNK_ERR_* code on failure.  Reasons for failure include
     255             :    FD_FUNK_ERR_INVAL (NULL rec, too large new_val_sz, rec is marked
     256             :    ERASE) and FD_FUNK_ERR_MEM (allocation failure, need a larger wksp).
     257             :    On failure, the current value is unchanged.
     258             : 
     259             :    Assumes no concurrent operations on rec. */
     260             : 
     261             : fd_funk_rec_t *                                   /* Returns rec on success, NULL on failure */
     262             : fd_funk_val_speed_load( fd_funk_t *     funk,
     263             :                         fd_funk_rec_t * rec,        /* Assumed in caller's address space to a live funk record (NULL returns NULL) */
     264             :                         ulong           new_val_sz, /* Should be in [0,FD_FUNK_REC_VAL_MAX] (returns NULL otherwise) */
     265             :                         fd_wksp_t *     wksp,       /* ==fd_funk_wksp( funk ) where funk is current local join */
     266             :                         int *           opt_err );  /* If non-NULL, *opt_err returns operation error code */
     267             : 
     268             : /* Misc */
     269             : 
     270             : /* fd_funk_val_init sets a record with uninitialized value metadata to
     271             :    the NULL value.  Meant for internal use. */
     272             : 
     273             : static inline fd_funk_rec_t *             /* Returns rec */
     274    18256539 : fd_funk_val_init( fd_funk_rec_t * rec ) { /* Assumed record in caller's address space with uninitialized value metadata */
     275    18256539 :   rec->val_sz      = 0U;
     276    18256539 :   rec->val_max     = 0U;
     277    18256539 :   rec->val_gaddr   = 0UL;
     278    18256539 :   rec->val_no_free = 0;
     279    18256539 :   return rec;
     280    18256539 : }
     281             : 
     282             : /* fd_funk_val_flush sets a record to the NULL value, discarding the
     283             :    current value if any.  Meant for internal use. */
     284             : 
     285             : static inline fd_funk_rec_t *               /* Returns rec */
     286             : fd_funk_val_flush( fd_funk_rec_t * rec,     /* Assumed live funk record in caller's address space */
     287             :                    fd_alloc_t *    alloc,   /* ==fd_funk_alloc( funk, wksp ) */
     288    12024858 :                    fd_wksp_t *     wksp ) { /* ==fd_funk_wksp( funk ) where funk is a current local join */
     289    12024858 :   ulong val_gaddr   = rec->val_gaddr;
     290    12024858 :   int   val_no_free = rec->val_no_free;
     291    12024858 :   fd_funk_val_init( rec );
     292    12024858 :   if( val_gaddr && !val_no_free ) fd_alloc_free( alloc, fd_wksp_laddr_fast( wksp, val_gaddr ) );
     293    12024858 :   return rec;
     294    12024858 : }
     295             : 
     296             : /* fd_funk_val_verify verifies the record values.  Returns
     297             :    FD_FUNK_SUCCESS if the values appear intact and FD_FUNK_ERR_INVAL if
     298             :    not (logs details).  Meant to be called as part of fd_funk_verify.
     299             :    As such, it assumes funk is non-NULL, fd_funk_{wksp,rec_map,wksp_tag}
     300             :    have been verified to work and the rec_map has been verified. */
     301             : 
     302             : int
     303             : fd_funk_val_verify( fd_funk_t * funk );
     304             : 
     305             : FD_PROTOTYPES_END
     306             : 
     307             : /* TODO: Retune fd_alloc and fd_wksp for Solana record size optimized
     308             :    size classes and transition point to fd_wksp backing. */
     309             : 
     310             : #endif /* HEADER_fd_src_funk_fd_funk_val_h */

Generated by: LCOV version 1.14