LCOV - code coverage report
Current view: top level - funk - fd_funk_base.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 92 93 98.9 %
Date: 2025-10-27 04:40:00 Functions: 84 2584 3.3 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_funk_fd_funk_base_h
       2             : #define HEADER_fd_src_funk_fd_funk_base_h
       3             : 
       4             : /* Funk terminology / concepts:
       5             : 
       6             :    - A funk instance stores records.
       7             : 
       8             :    - A record is a key-value pair.
       9             : 
      10             :    - keys are a fixed length fd_funk_rec_key_t.
      11             : 
      12             :    - values are variable size arbitrary binary data with an upper bound
      13             :      to the size.
      14             : 
      15             :    - Records are indexed by key.
      16             : 
      17             :    - A funk transaction describes changes to the funk records.
      18             : 
      19             :    - A transactions has a globally unique identifier and a parent
      20             :      transaction.
      21             : 
      22             :    - Transactions with children cannot be modified.
      23             : 
      24             :    - The chain of transactions through a transaction's ancestors
      25             :      (its parent, grandparent, great-grandparent, ...) provides a
      26             :      history of the funk all the way back the "root" transaction.
      27             : 
      28             :    - A transaction can be either in preparation or published.
      29             : 
      30             :    - The ancestors of a published transaction cannot be modified.
      31             : 
      32             :    - In preparation transactions can be cancelled.
      33             : 
      34             :    - Cancelling a transaction will discard all funk record updates for
      35             :      that transaction and any descendant transactions.
      36             : 
      37             :    - Published transactions cannot be cancelled.
      38             : 
      39             :    - Critically, competing/parallel transaction histories are allowed.
      40             : 
      41             :    - A user can update all funk records for the most recently
      42             :      published transactions (if it is not frozen) or all transactions
      43             :      in preparation (if they are not frozen). */
      44             : 
      45             : #include "../util/fd_util.h"
      46             : 
      47             : #if FD_HAS_X86
      48             : #include <immintrin.h>
      49             : #endif
      50             : 
      51             : /* FD_FUNK_SUCCESS is used by various APIs to indicate the operation
      52             :    successfully completed.  This will be 0.  FD_FUNK_ERR_* gives a
      53             :    number of error codes used by fd_funk APIs.  These will be negative
      54             :    integers. */
      55             : 
      56     1278495 : #define FD_FUNK_SUCCESS    (0)  /* Success */
      57           3 : #define FD_FUNK_ERR_INVAL  (-1) /* Failed due to obviously invalid inputs */
      58           3 : #define FD_FUNK_ERR_XID    (-2) /* Failed due to transaction id issue (e.g. xid present/absent when it should be absent/present) */
      59           3 : #define FD_FUNK_ERR_KEY    (-3) /* Failed due to record key issue (e.g. key present/absent when it should be absent/present) */
      60           3 : #define FD_FUNK_ERR_FROZEN (-4) /* Failed due to frozen issue (e.g. attempt to change records in a frozen transaction) */
      61           3 : #define FD_FUNK_ERR_TXN    (-5) /* Failed due to transaction map issue (e.g. funk txn_max too small) */
      62           3 : #define FD_FUNK_ERR_REC    (-6) /* Failed due to record map issue (e.g. funk rec_max too small) */
      63           3 : #define FD_FUNK_ERR_MEM    (-7) /* Failed due to wksp issue (e.g. wksp too small) */
      64           0 : #define FD_FUNK_ERR_SYS    (-8) /* Failed system call (e.g. a file write) */
      65             : 
      66             : /* FD_FUNK_REC_KEY_{ALIGN,FOOTPRINT} describe the alignment and
      67             :    footprint of a fd_funk_rec_key_t.  ALIGN is a positive integer power
      68             :    of 2.  FOOTPRINT is a multiple of ALIGN.  These are provided to
      69             :    facilitate compile time declarations. */
      70             : 
      71             : #define FD_FUNK_REC_KEY_ALIGN     (8UL)
      72             : #define FD_FUNK_REC_KEY_FOOTPRINT (32UL)
      73             : 
      74             : /* A fd_funk_rec_key_t identifies a funk record.  Compact binary keys
      75             :    are encouraged but a cstr can be used so long as it has
      76             :    strlen(cstr)<FD_FUNK_REC_KEY_FOOTPRINT and the characters c[i] for i
      77             :    in [strlen(cstr),FD_FUNK_REC_KEY_FOOTPRINT) zero.  (Also, if encoding
      78             :    a cstr in a key, recommend using first byte to encode the strlen for
      79             :    accelerating cstr operations further but this is up to the user.) */
      80             : 
      81             : union __attribute__((aligned(FD_FUNK_REC_KEY_ALIGN))) fd_funk_rec_key {
      82             :   uchar uc[ FD_FUNK_REC_KEY_FOOTPRINT ];
      83             :   uint  ui[ 8 ];
      84             :   ulong ul[ 4 ];
      85             : };
      86             : 
      87             : typedef union fd_funk_rec_key fd_funk_rec_key_t;
      88             : 
      89             : /* FD_FUNK_TXN_XID_{ALIGN,FOOTPRINT} describe the alignment and
      90             :    footprint of a fd_funk_txn_xid_t.  ALIGN is a positive integer power
      91             :    of 2.  FOOTPRINT is a multiple of ALIGN.  These are provided to
      92             :    facilitate compile time declarations. */
      93             : 
      94             : #define FD_FUNK_TXN_XID_ALIGN     (16UL)
      95             : #define FD_FUNK_TXN_XID_FOOTPRINT (16UL)
      96             : 
      97             : /* A fd_funk_txn_xid_t identifies a funk transaction currently in
      98             :    preparation.  Compact binary identifiers are encouraged but a cstr
      99             :    can be used so long as it has
     100             :    strlen(cstr)<FD_FUNK_TXN_XID_FOOTPRINT and characters c[i] for i
     101             :    in [strlen(cstr),FD_FUNK_TXN_KEY_FOOTPRINT) zero.  (Also, if
     102             :    encoding a cstr in a transaction id, recommend using first byte to
     103             :    encode the strlen for accelerating cstr operations even further but
     104             :    this is more up to the application.) */
     105             : 
     106             : union __attribute__((aligned(FD_FUNK_TXN_XID_ALIGN))) fd_funk_txn_xid {
     107             :   uchar uc[ FD_FUNK_TXN_XID_FOOTPRINT ];
     108             :   ulong ul[ FD_FUNK_TXN_XID_FOOTPRINT / sizeof(ulong) ];
     109             : #if FD_HAS_INT128
     110             :   uint128 uf[1];
     111             : #endif
     112             : #if FD_HAS_X86
     113             :   __m128i xmm[1];
     114             : #endif
     115             : };
     116             : 
     117             : typedef union fd_funk_txn_xid fd_funk_txn_xid_t;
     118             : 
     119             : /* FD_FUNK_XID_KEY_PAIR_{ALIGN,FOOTPRINT} describe the alignment and
     120             :    footprint of a fd_funk_xid_key_pair_t.  ALIGN is a positive integer
     121             :    power of 2.  FOOTPRINT is a multiple of ALIGN.  These are provided to
     122             :    facilitate compile time declarations. */
     123             : 
     124             : #define FD_FUNK_XID_KEY_PAIR_ALIGN     (16UL)
     125             : #define FD_FUNK_XID_KEY_PAIR_FOOTPRINT (48UL)
     126             : 
     127             : /* A fd_funk_xid_key_pair_t identifies a funk record.  It is just
     128             :    xid and key packed into the same structure. */
     129             : 
     130             : struct fd_funk_xid_key_pair {
     131             :   fd_funk_txn_xid_t xid[1];
     132             :   fd_funk_rec_key_t key[1];
     133             : };
     134             : 
     135             : typedef struct fd_funk_xid_key_pair fd_funk_xid_key_pair_t;
     136             : 
     137             : /* A fd_funk_shmem_t is the top part of a funk object in shared memory. */
     138             : 
     139             : struct fd_funk_shmem_private;
     140             : typedef struct fd_funk_shmem_private fd_funk_shmem_t;
     141             : 
     142             : /* A fd_funk_t * is local join handle to a funk instance */
     143             : 
     144             : struct fd_funk_private;
     145             : typedef struct fd_funk_private fd_funk_t;
     146             : 
     147             : FD_PROTOTYPES_BEGIN
     148             : 
     149             : /* fd_funk_rec_key_hash provides a family of hashes that hash the key
     150             :    pointed to by k to a uniform quasi-random 64-bit integer.  seed
     151             :    selects the particular hash function to use and can be an arbitrary
     152             :    64-bit value.  Returns the hash.  The hash functions are high quality
     153             :    but not cryptographically secure.  Assumes k is in the caller's
     154             :    address space and valid. */
     155             : 
     156             : #if FD_HAS_INT128
     157             : 
     158             : /* If the target supports uint128, fd_funk_rec_key_hash is seeded
     159             :    xxHash3 with 64-bit output size. (open source BSD licensed) */
     160             : 
     161             : static inline ulong
     162    29246322 : fd_xxh3_mul128_fold64( ulong lhs, ulong rhs ) {
     163    29246322 :   uint128 product = (uint128)lhs * (uint128)rhs;
     164    29246322 :   return (ulong)product ^ (ulong)( product>>64 );
     165    29246322 : }
     166             : 
     167             : static inline ulong
     168             : fd_xxh3_mix16b( ulong i0, ulong i1,
     169             :              ulong s0, ulong s1,
     170    29246322 :              ulong seed ) {
     171    29246322 :   return fd_xxh3_mul128_fold64( i0 ^ (s0 + seed), i1 ^ (s1 - seed) );
     172    29246322 : }
     173             : 
     174             : FD_FN_PURE static inline ulong
     175             : fd_funk_rec_key_hash1( uchar const key[ 32 ],
     176             :                        ulong       rec_type,
     177    14623161 :                        ulong       seed ) {
     178    14623161 :   seed ^= rec_type;
     179    14623161 :   ulong k0 = FD_LOAD( ulong, key+ 0 );
     180    14623161 :   ulong k1 = FD_LOAD( ulong, key+ 8 );
     181    14623161 :   ulong k2 = FD_LOAD( ulong, key+16 );
     182    14623161 :   ulong k3 = FD_LOAD( ulong, key+24 );
     183    14623161 :   ulong acc = 32 * 0x9E3779B185EBCA87ULL;
     184    14623161 :   acc += fd_xxh3_mix16b( k0, k1, 0xbe4ba423396cfeb8UL, 0x1cad21f72c81017cUL, seed );
     185    14623161 :   acc += fd_xxh3_mix16b( k2, k3, 0xdb979083e96dd4deUL, 0x1f67b3b7a4a44072UL, seed );
     186    14623161 :   acc = acc ^ (acc >> 37);
     187    14623161 :   acc *= 0x165667919E3779F9ULL;
     188    14623161 :   acc = acc ^ (acc >> 32);
     189    14623161 :   return acc;
     190    14623161 : }
     191             : 
     192             : FD_FN_PURE static inline ulong
     193             : fd_funk_rec_key_hash( fd_funk_rec_key_t const * k,
     194    14621007 :                       ulong                     seed ) {
     195    14621007 :   return fd_funk_rec_key_hash1( k->uc, 0UL, seed );
     196    14621007 : }
     197             : 
     198             : #else
     199             : 
     200             : /* If the target does not support xxHash3, fallback to the 'old' funk
     201             :    key hash function.
     202             : 
     203             :    FIXME This version is vulnerable to HashDoS */
     204             : 
     205             : FD_FN_PURE static inline ulong
     206             : fd_funk_rec_key_hash1( uchar const key[ 32 ],
     207             :                        ulong       rec_type,
     208             :                        ulong       seed ) {
     209             :   seed ^= rec_type;
     210             :   /* tons of ILP */
     211             :   return (fd_ulong_hash( seed ^ (1UL<<0) ^ FD_LOAD( ulong, key+ 0 ) )   ^
     212             :           fd_ulong_hash( seed ^ (1UL<<1) ^ FD_LOAD( ulong, key+ 8 ) ) ) ^
     213             :          (fd_ulong_hash( seed ^ (1UL<<2) ^ FD_LOAD( ulong, key+16 ) ) ^
     214             :           fd_ulong_hash( seed ^ (1UL<<3) ^ FD_LOAD( ulong, key+24 ) ) );
     215             : }
     216             : 
     217             : FD_FN_PURE static inline ulong
     218             : fd_funk_rec_key_hash( fd_funk_rec_key_t const * k,
     219             :                       ulong                     seed ) {
     220             :   return fd_funk_rec_key_hash1( k->uc, 0UL, seed );
     221             : }
     222             : 
     223             : #endif /* FD_HAS_INT128 */
     224             : 
     225             : /* fd_funk_rec_key_eq returns 1 if keys pointed to by ka and kb are
     226             :    equal and 0 otherwise.  Assumes ka and kb are in the caller's address
     227             :    space and valid. */
     228             : 
     229             : FD_FN_UNUSED FD_FN_PURE static int /* Workaround -Winline */
     230             : fd_funk_rec_key_eq( fd_funk_rec_key_t const * ka,
     231    53042898 :                     fd_funk_rec_key_t const * kb ) {
     232    53042898 :   ulong const * a = ka->ul;
     233    53042898 :   ulong const * b = kb->ul;
     234    53042898 :   return !( ((a[0]^b[0]) | (a[1]^b[1])) | ((a[2]^b[2]) | (a[3]^b[3])) ) ;
     235    53042898 : }
     236             : 
     237             : /* fd_funk_rec_key_copy copies the key pointed to by ks into the key
     238             :    pointed to by kd and returns kd.  Assumes kd and ks are in the
     239             :    caller's address space and valid. */
     240             : 
     241             : static inline fd_funk_rec_key_t *
     242             : fd_funk_rec_key_copy( fd_funk_rec_key_t *       kd,
     243    10876434 :                       fd_funk_rec_key_t const * ks ) {
     244    10876434 :   ulong *       d = kd->ul;
     245    10876434 :   ulong const * s = ks->ul;
     246    10876434 :   d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; d[3] = s[3];
     247    10876434 :   return kd;
     248    10876434 : }
     249             : 
     250             : /* fd_funk_txn_xid_hash provides a family of hashes that hash the xid
     251             :    pointed to by x to a uniform quasi-random 64-bit integer.  seed
     252             :    selects the particular hash function to use and can be an arbitrary
     253             :    64-bit value.  Returns the hash.  The hash functions are high quality
     254             :    but not cryptographically secure.  Assumes x is in the caller's
     255             :    address space and valid. */
     256             : 
     257             : FD_FN_UNUSED FD_FN_PURE static ulong /* Work around -Winline */
     258             : fd_funk_txn_xid_hash( fd_funk_txn_xid_t const * x,
     259     9540852 :                       ulong                     seed ) {
     260     9540852 :   return ( fd_ulong_hash( seed ^ (1UL<<0) ^ x->ul[0] ) ^ fd_ulong_hash( seed ^ (1UL<<1) ^ x->ul[1] ) ); /* tons of ILP */
     261     9540852 : }
     262             : 
     263             : /* fd_funk_txn_xid_eq returns 1 if transaction id pointed to by xa and
     264             :    xb are equal and 0 otherwise.  Assumes xa and xb are in the caller's
     265             :    address space and valid. */
     266             : 
     267             : FD_FN_PURE static inline int
     268             : fd_funk_txn_xid_eq( fd_funk_txn_xid_t const * xa,
     269   116292066 :                     fd_funk_txn_xid_t const * xb ) {
     270   116292066 :   ulong const * a = xa->ul;
     271   116292066 :   ulong const * b = xb->ul;
     272   116292066 :   return !( (a[0]^b[0]) | (a[1]^b[1]) );
     273   116292066 : }
     274             : 
     275             : /* fd_funk_txn_xid_copy copies the transaction id pointed to by xs into
     276             :    the transaction id pointed to by xd and returns xd.  Assumes xd and
     277             :    xs are in the caller's address space and valid. */
     278             : 
     279             : static inline fd_funk_txn_xid_t *
     280             : fd_funk_txn_xid_copy( fd_funk_txn_xid_t *       xd,
     281    14333880 :                       fd_funk_txn_xid_t const * xs ) {
     282    14333880 :   ulong *       d = xd->ul;
     283    14333880 :   ulong const * s = xs->ul;
     284    14333880 :   d[0] = s[0]; d[1] = s[1];
     285    14333880 :   return xd;
     286    14333880 : }
     287             : 
     288             : static inline fd_funk_txn_xid_t *
     289             : fd_funk_txn_xid_st_atomic( fd_funk_txn_xid_t *       xd,
     290      818046 :                            fd_funk_txn_xid_t const * xs ) {
     291      818046 : # if FD_HAS_X86
     292      818046 :   FD_VOLATILE( xd->xmm[0] ) = xs->xmm[0];
     293             : # elif FD_HAS_INT128
     294             :   FD_VOLATILE( xd->uf[0] ) = xs->uf[0];
     295             : # else
     296             :   fd_funk_txn_xid_copy( xd, xs );
     297             : # endif
     298      818046 :   return xd;
     299      818046 : }
     300             : 
     301             : static inline fd_funk_txn_xid_t *
     302             : fd_funk_txn_xid_ld_atomic( fd_funk_txn_xid_t *       xd,
     303          39 :                            fd_funk_txn_xid_t const * xs ) {
     304          39 : # if FD_HAS_X86
     305          39 :   xd->xmm[0] = FD_VOLATILE_CONST( xs->xmm[0] );
     306             : # elif FD_HAS_INT128
     307             :   xd->uf[0] = FD_VOLATILE_CONST( xs->uf[0] );
     308             : # else
     309             :   fd_funk_txn_xid_copy( xd, xs );
     310             : # endif
     311          39 :   return xd;
     312          39 : }
     313             : 
     314             : /* fd_funk_txn_xid_eq_root returns 1 if transaction id pointed to by x
     315             :    is the root transaction.  Assumes x is in the caller's address space
     316             :    and valid. */
     317             : 
     318             : FD_FN_PURE static inline int
     319    27624366 : fd_funk_txn_xid_eq_root( fd_funk_txn_xid_t const * x ) {
     320    27624366 :   ulong const * a = x->ul;
     321    27624366 :   return ((a[0] == ULONG_MAX) & (a[1] == ULONG_MAX));
     322    27624366 : }
     323             : 
     324             : /* fd_funk_txn_xid_set_root sets transaction id pointed to by x to the
     325             :    root transaction and returns x.  Assumes x is in the caller's address
     326             :    space and valid. */
     327             : 
     328             : static inline fd_funk_txn_xid_t *
     329      593769 : fd_funk_txn_xid_set_root( fd_funk_txn_xid_t * x ) {
     330      593769 :   ulong * a = x->ul;
     331      593769 :   a[0] = ULONG_MAX; a[1] = ULONG_MAX;
     332      593769 :   return x;
     333      593769 : }
     334             : 
     335             : /* fd_funk_xid_key_pair_hash produces a 64-bit hash case for a
     336             :    xid_key_pair. Assumes p is in the caller's address space and valid. */
     337             : 
     338             : FD_FN_PURE static inline ulong
     339             : fd_funk_xid_key_pair_hash( fd_funk_xid_key_pair_t const * p,
     340     8621007 :                               ulong                          seed ) {
     341             :   /* We ignore the xid part of the key because we need all the instances
     342             :      of a given record key to appear in the same hash
     343             :      chain. fd_funk_rec_query_global depends on this. */
     344     8621007 :   return fd_funk_rec_key_hash( p->key, seed );
     345     8621007 : }
     346             : 
     347             : /* fd_funk_xid_key_pair_eq returns 1 if (xid,key) pair pointed to by pa
     348             :    and pb are equal and 0 otherwise.  Assumes pa and pb are in the
     349             :    caller's address space and valid. */
     350             : 
     351             : FD_FN_UNUSED FD_FN_PURE static int /* Work around -Winline */
     352             : fd_funk_xid_key_pair_eq( fd_funk_xid_key_pair_t const * pa,
     353    29041734 :                             fd_funk_xid_key_pair_t const * pb ) {
     354    29041734 :   return fd_funk_txn_xid_eq( pa->xid, pb->xid ) & fd_funk_rec_key_eq( pa->key, pb->key );
     355    29041734 : }
     356             : 
     357             : /* fd_funk_xid_key_pair_copy copies the (xid,key) pair pointed to by ps
     358             :    into the (xid,key) pair to by pd and returns pd.  Assumes pd and ps
     359             :    are in the caller's address space and valid. */
     360             : 
     361             : static inline fd_funk_xid_key_pair_t *
     362             : fd_funk_xid_key_pair_copy( fd_funk_xid_key_pair_t *       pd,
     363     3000000 :                               fd_funk_xid_key_pair_t const * ps ) {
     364     3000000 :   fd_funk_txn_xid_copy( pd->xid, ps->xid );
     365     3000000 :   fd_funk_rec_key_copy( pd->key, ps->key );
     366     3000000 :   return pd;
     367     3000000 : }
     368             : 
     369             : /* fd_funk_xid_key_pair_init set the (xid,key) pair p to pair formed
     370             :    from the transaction id pointed to by x and the record key pointed to
     371             :    by k.  Assumes p, x and k are in the caller's address space and
     372             :    valid. */
     373             : 
     374             : static inline fd_funk_xid_key_pair_t *
     375             : fd_funk_xid_key_pair_init( fd_funk_xid_key_pair_t *  p,
     376             :                               fd_funk_txn_xid_t const * x,
     377     3000000 :                               fd_funk_rec_key_t const * k ) {
     378     3000000 :   fd_funk_txn_xid_copy( p->xid, x );
     379     3000000 :   fd_funk_rec_key_copy( p->key, k );
     380     3000000 :   return p;
     381     3000000 : }
     382             : 
     383             : /* fd_funk_strerror converts an FD_FUNK_SUCCESS / FD_FUNK_ERR_* code
     384             :    into a human readable cstr.  The lifetime of the returned pointer is
     385             :    infinite.  The returned pointer is always to a non-NULL cstr. */
     386             : 
     387             : FD_FN_CONST char const *
     388             : fd_funk_strerror( int err );
     389             : 
     390             : /* TODO: Consider renaming transaction/txn to update (too much typing)?
     391             :    upd (probably too similar to UDP) node? block? blk? state? commit?
     392             :    ... to reduce naming collisions with terminology in use elsewhere?
     393             : 
     394             :    TODO: Consider fine tuning {REC,TXN}_{ALIGN,FOOTPRINT} to balance
     395             :    application use cases with in memory packing with AVX / CPU cache
     396             :    friendly accelerability.  Likewise, virtually everything in here can
     397             :    be AVX accelerated if desired.  E.g. 8 uint hash in parallel then an
     398             :    8 wide xor lane reduction tree in hash?
     399             : 
     400             :    TODO: Consider letting the user provide alternatives for record and
     401             :    transaction hashes at compile time (e.g. ids in blockchain apps are
     402             :    often already crypto secure hashes in which case x->ul[0] ^ seed is
     403             :    just as good theoretically and faster practically). */
     404             : 
     405             : FD_PROTOTYPES_END
     406             : 
     407             : #endif /* HEADER_fd_src_funk_fd_funk_base_h */

Generated by: LCOV version 1.14