LCOV - code coverage report
Current view: top level - util/pod - fd_pod.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 415 432 96.1 %
Date: 2024-11-13 11:58:15 Functions: 15 15 100.0 %

          Line data    Source code
       1             : #include "fd_pod.h"
       2             : 
       3             : /* FD_POD_PATH_SPLIT splits the path into a leading key prefix and a
       4             :    suffix.  On return, prefix_len is the number of bytes in a strlen
       5             :    sense of the leading key prefix.  If delim is '.', suffix points to
       6             :    the first byte of the path suffix.  If delim is '\0', the path is
       7             :    just a single key with no suffix and suffix should be ignored.  This
       8             :    macro is not robust. */
       9             : 
      10    30073752 : #define FD_POD_PATH_SPLIT( path, prefix_len, delim, suffix ) do { \
      11    30073752 :     suffix = path;                                                \
      12   145736415 :     for(;;) {                                                     \
      13   145736415 :       delim = suffix[0];                                          \
      14   145736415 :       if( FD_UNLIKELY( (delim=='.') | (!delim) ) ) break;         \
      15   145736415 :       suffix++;                                                   \
      16   115662663 :     }                                                             \
      17    30073752 :     prefix_len = (ulong)(suffix - path);                          \
      18    30073752 :     suffix++;                                                     \
      19    30073752 :   } while(0)
      20             : 
      21             : /* FD_POD_FOR_ALL_BEGIN / FD_POD_FOR_ALL_END iterates over all key-val
      22             :    pairs in a pod.  Each iteration:
      23             : 
      24             :      uchar * pair     points to the first byte of the encoded key-val pair
      25             :      uchar * next     points to the one after the last byte of the encoded key-val pair
      26             :      ulong   ksz      is the SVW encoded width of the key_sz field
      27             :      ulong   key_sz   is the strlen(key)+1 of the key
      28             :      char  * key      is points to the first byte of the key cstr
      29             :      int     val_type is the type of val associated with this key (FD_POD_VAL_TYPE_*)
      30             :      ulong   vsz      is the SVW encoded width of the val_sz field
      31             :      ulong   val_sz   is the number of bytes in the encoded value
      32             :      void  * val      points to the first byte of the encoded val
      33             : 
      34             :    The actual iteration process does not depend on these values.
      35             :    (E.g. caller can mangle these without impact the iteration.)
      36             : 
      37             :    There are also variables:
      38             :      ulong _csz    is the SWV encoded width of the pod header variables
      39             :      ulong _used   is the number of bytes used in the pod
      40             :      ulong _cursor is the next byte to process in the iteration
      41             :      ulong _stop   is one after the last byte to process in the iteration
      42             : 
      43             :    This macro is not robust. */
      44             : 
      45             : #define FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) \
      46   704327799 :   do {                                                                                        \
      47   704327799 :     ulong   _csz    = fd_ulong_svw_dec_sz( pod );                                             \
      48   704327799 :     ulong   _used   = fd_ulong_svw_dec_fixed( pod + _csz, _csz );                             \
      49   704327799 :     uchar * _cursor = (uchar *)(pod + _csz*3UL);                                              \
      50   704327799 :     uchar * _stop   = (uchar *)(pod + _used);                                                 \
      51  1984565784 :     while( _cursor<_stop ) {                                                                  \
      52  1303928016 :       pair     = _cursor;                                                   (void)pair;       \
      53  1303928016 :       ksz      = fd_ulong_svw_dec_sz( _cursor );                                              \
      54  1303928016 :       key_sz   = fd_ulong_svw_dec_fixed( _cursor, ksz ); _cursor += ksz;                      \
      55  1303928016 :       key      = (char *)_cursor;                        _cursor += key_sz; (void)key;        \
      56  1303928016 :       val_type = (int)_cursor[0];                        _cursor += 1;      (void)val_type;   \
      57  1303928016 :       vsz      = fd_ulong_svw_dec_sz( _cursor );                                              \
      58  1303928016 :       val_sz   = fd_ulong_svw_dec_fixed( _cursor, vsz ); _cursor += vsz;                      \
      59  1303928016 :       val      = (void *)_cursor;                        _cursor += val_sz; (void)val;        \
      60  1303928016 :       next     = _cursor;                                                   (void)next;       \
      61             : 
      62             : #define FD_POD_FOR_ALL_END \
      63  1303928016 :     }                      \
      64   704327799 :   } while(0)
      65             : 
      66             : fd_pod_info_t *
      67             : fd_pod_list( uchar const   * FD_RESTRICT pod,
      68      457380 :              fd_pod_info_t * FD_RESTRICT info ) {
      69      457380 :   if( FD_UNLIKELY( !pod ) ) return NULL;
      70             : 
      71      457380 :   ulong idx = 0UL;
      72             : 
      73      457380 :   uchar const * pair; uchar const * next;
      74      457380 :   ulong ksz; ulong key_sz; char const * key; int val_type;
      75      457380 :   ulong vsz; ulong val_sz; void const * val;
      76    16818084 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
      77             : 
      78    16818084 :     info[idx].key_sz   = key_sz;
      79    16818084 :     info[idx].key      = key;
      80    16818084 :     info[idx].val_type = val_type;
      81    16818084 :     info[idx].val_sz   = val_sz;
      82    16818084 :     info[idx].val      = val;
      83    16818084 :     info[idx].parent   = NULL;
      84    16818084 :     idx++;
      85             : 
      86    16818084 :   } FD_POD_FOR_ALL_END;
      87             : 
      88      457380 :   return info;
      89      457380 : }
      90             : 
      91             : ulong
      92           6 : fd_pod_cnt_subpod( uchar const * FD_RESTRICT pod ) {
      93           6 :   if( FD_UNLIKELY( !pod ) ) return 0UL;
      94             : 
      95           6 :   ulong cnt = 0UL;
      96             : 
      97           6 :   uchar const * pair; uchar const * next;
      98           6 :   ulong ksz; ulong key_sz; char const * key; int val_type;
      99           6 :   ulong vsz; ulong val_sz; void const * val;
     100          51 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     101          51 :     cnt += (ulong)(val_type==FD_POD_VAL_TYPE_SUBPOD);
     102          51 :   } FD_POD_FOR_ALL_END;
     103             : 
     104           6 :   return cnt;
     105           6 : }
     106             : 
     107             : ulong
     108   505290042 : fd_pod_cnt_recursive( uchar const * FD_RESTRICT pod ) {
     109   505290042 :   if( FD_UNLIKELY( !pod ) ) return 0UL;
     110             : 
     111   505290042 :   ulong cnt = 0UL;
     112             : 
     113   505290042 :   uchar const * pair; uchar const * next;
     114   505290042 :   ulong ksz; ulong key_sz; char const * key; int val_type;
     115   505290042 :   ulong vsz; ulong val_sz; void const * val;
     116  1078568358 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     117  1078568358 :     cnt++;
     118  1078568358 :     if( val_type==FD_POD_VAL_TYPE_SUBPOD ) cnt += fd_pod_cnt_recursive( (uchar *)val );
     119  1078568358 :   } FD_POD_FOR_ALL_END;
     120             : 
     121   505290042 :   return cnt;
     122   505290042 : }
     123             : 
     124             : static fd_pod_info_t *
     125             : fd_pod_list_recursive_node( fd_pod_info_t * FD_RESTRICT parent,
     126             :                             uchar const   * FD_RESTRICT pod,
     127      728898 :                             fd_pod_info_t * FD_RESTRICT info ) {
     128             : 
     129      728898 :   uchar const * pair; uchar const * next;
     130      728898 :   ulong ksz; ulong key_sz; char const * key; int val_type;
     131      728898 :   ulong vsz; ulong val_sz; void const * val;
     132     1533312 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     133             : 
     134     1533312 :     info->key_sz   = key_sz;
     135     1533312 :     info->key      = key;
     136     1533312 :     info->val_type = val_type;
     137     1533312 :     info->val_sz   = val_sz;
     138     1533312 :     info->val      = val;
     139     1533312 :     info->parent   = parent;
     140     1533312 :     info++;
     141             : 
     142     1533312 :     if( val_type==FD_POD_VAL_TYPE_SUBPOD ) info = fd_pod_list_recursive_node( info-1, (uchar *)val, info );
     143             : 
     144     1533312 :   } FD_POD_FOR_ALL_END;
     145             : 
     146      728898 :   return info;
     147      728898 : }
     148             : 
     149             : fd_pod_info_t *
     150             : fd_pod_list_recursive( uchar const   * FD_RESTRICT pod,
     151       26769 :                        fd_pod_info_t * FD_RESTRICT info ) {
     152       26769 :   if( FD_UNLIKELY( !pod ) ) return info;
     153       26769 :   fd_pod_list_recursive_node( NULL, pod, info );
     154       26769 :   return info;
     155       26769 : }
     156             : 
     157             : int
     158             : fd_pod_query( uchar const   * FD_RESTRICT pod,
     159             :               char const    * FD_RESTRICT path,
     160    23594316 :               fd_pod_info_t * FD_RESTRICT opt_info ) {
     161    23594316 :   if( FD_UNLIKELY( (!pod) | (!path) ) ) return FD_POD_ERR_INVAL;
     162             : 
     163    23594316 :   ulong prefix_len; char delim; char const * suffix;
     164    23594316 :   FD_POD_PATH_SPLIT( path, prefix_len, delim, suffix );
     165             : 
     166    23594316 :   uchar const * pair; uchar const * next;
     167    23594316 :   ulong ksz; ulong key_sz; char const * key; int val_type;
     168    23594316 :   ulong vsz; ulong val_sz; void const * val;
     169   481516119 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     170             : 
     171   481516119 :     if( ((key_sz-1UL)==prefix_len) && !memcmp( key, path, prefix_len ) ) { /* Found leading key in pod */
     172             : 
     173    22449147 :       if( !delim ) { /* Path was a single key, return it */
     174             : 
     175    17060859 :         if( opt_info ) {
     176    17060859 :           opt_info->key_sz   = key_sz;
     177    17060859 :           opt_info->key      = key;
     178    17060859 :           opt_info->val_type = val_type;
     179    17060859 :           opt_info->val_sz   = val_sz;
     180    17060859 :           opt_info->val      = val;
     181    17060859 :           opt_info->parent   = NULL;
     182    17060859 :         }
     183    17060859 :         return FD_POD_SUCCESS;
     184             : 
     185    17060859 :       } else { /* Path had a suffix.  Recurse into the subpod */
     186             : 
     187     5388288 :         if( FD_UNLIKELY( val_type!=FD_POD_VAL_TYPE_SUBPOD ) ) return FD_POD_ERR_TYPE;
     188     5056332 :         return fd_pod_query( (uchar const *)val, suffix, opt_info );
     189             : 
     190     5388288 :       }
     191    22449147 :     }
     192             : 
     193   481516119 :   } FD_POD_FOR_ALL_END;
     194             : 
     195     1145169 :   return FD_POD_ERR_RESOLVE;
     196    23594316 : }
     197             : 
     198             : char const *
     199          18 : fd_pod_strerror( int err ) {
     200          18 :   switch( err ) {
     201           3 :   case FD_POD_SUCCESS:     return "success";
     202           3 :   case FD_POD_ERR_INVAL:   return "bad input args";
     203           3 :   case FD_POD_ERR_TYPE:    return "path contained an unexpected type key";
     204           3 :   case FD_POD_ERR_RESOLVE: return "path did not resolve to a key";
     205           3 :   case FD_POD_ERR_FULL:    return "pod too full";
     206           3 :   default: break;
     207          18 :   }
     208           3 :   return "unknown";
     209          18 : }
     210             : 
     211             : ulong
     212             : fd_pod_resize( uchar * pod,
     213       49689 :                ulong   new_max ) {
     214       49689 :   if( FD_UNLIKELY( !pod ) ) return 0UL;
     215             : 
     216       49689 :   ulong csz    = fd_ulong_svw_dec_sz( pod );
     217             : //ulong max    = fd_ulong_svw_dec_fixed( pod,           csz );
     218       49689 :   ulong used   = fd_ulong_svw_dec_fixed( pod + csz,     csz ); if( FD_UNLIKELY( used>new_max ) ) return 0UL;
     219       49689 :   ulong cnt    = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     220       49689 :   ulong bdy_sz = used - csz*3UL;
     221             : 
     222       49689 :   ulong new_csz;
     223       49689 :   ulong new_used;
     224       49689 :   for(;;) {
     225       49689 :     new_csz    = fd_ulong_svw_enc_sz( new_max );
     226       49689 :     new_used   = new_csz*3UL + bdy_sz;
     227       49689 :     if( FD_LIKELY( new_used<=new_max ) ) break;
     228             :     /* Resized header was too large ... try a smaller new_max */
     229           0 :     new_max--;
     230           0 :   }
     231             : 
     232       49689 :   memmove( pod + new_csz*3UL, pod + csz*3UL, bdy_sz );
     233       49689 :   fd_ulong_svw_enc_fixed( pod,               new_csz, new_max  );
     234       49689 :   fd_ulong_svw_enc_fixed( pod + new_csz,     new_csz, new_used );
     235       49689 :   fd_ulong_svw_enc_fixed( pod + new_csz*2UL, new_csz, cnt      );
     236       49689 :   return new_max;
     237       49689 : }
     238             : 
     239             : ulong
     240             : fd_pod_compact( uchar * pod,
     241   167777721 :                 int     full ) {
     242   167777721 :   if( FD_UNLIKELY( !pod ) ) return 0UL;
     243             : 
     244             :   /* Compact the body */
     245             : 
     246   167777721 :   ulong   csz = fd_ulong_svw_dec_sz( pod );
     247   167777721 :   ulong   max = fd_ulong_svw_dec_fixed( pod,           csz );
     248   167777721 :   ulong   cnt = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     249   167777721 :   uchar * bdy = pod + csz*3UL;
     250             : 
     251   167777721 :   uchar * pair; uchar * next = bdy;
     252   167777721 :   ulong ksz; ulong key_sz; char const * key; int val_type;
     253   167777721 :   ulong vsz; ulong val_sz; void * val;
     254   358351737 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     255             : 
     256             :     /* Compact the pair */
     257             : 
     258   358351737 :     ulong new_key_sz = strlen( key ) + 1UL; /* <=key_sz */
     259   358351737 :     ulong new_ksz    = fd_ulong_svw_enc_sz( new_key_sz );
     260             : 
     261   358351737 :     ulong new_val_sz;
     262   358351737 :     if( val_type==FD_POD_VAL_TYPE_SUBPOD ) {
     263   167548605 :       uchar * subpod = (uchar *)val;
     264   167548605 :       fd_pod_compact( subpod, 1 /*full*/ ); /* Yes, do not pass through the user provided value of full */
     265   167548605 :       new_val_sz = fd_pod_max( subpod ); /* ==fd_pod_used at this point */
     266   167548605 :     } else if( val_type==FD_POD_VAL_TYPE_CSTR ) {
     267     1537479 :       new_val_sz = val_sz ? (strlen( (char *)val )+1UL) : 0UL;
     268    21487932 :     } else {
     269    21487932 :       new_val_sz = val_sz;
     270    21487932 :     }
     271             :     /* Note: new_val_sz<=val_sz */
     272   358351737 :     ulong new_vsz = fd_ulong_svw_enc_sz( new_val_sz );
     273             : 
     274   358351737 :     uchar * new_cursor = pair;
     275   358351737 :     fd_ulong_svw_enc_fixed( new_cursor, new_ksz, new_key_sz ); new_cursor += new_ksz;
     276   358351737 :     memmove( new_cursor, key, new_key_sz );                    new_cursor += new_key_sz;
     277   358351737 :     new_cursor[0] = (uchar)val_type;                           new_cursor += 1;
     278   358351737 :     fd_ulong_svw_enc_fixed( new_cursor, new_vsz, new_val_sz ); new_cursor += new_vsz;
     279   358351737 :     memmove( new_cursor, val, new_val_sz );                    new_cursor += new_val_sz;
     280             : 
     281             :     /* Compact the trailing space */
     282             : 
     283   358351737 :     ulong rem = (ulong)(_stop-next);
     284   358351737 :     memmove( new_cursor, next, rem );
     285             : 
     286             :     /* Update the iterator internals */
     287             : 
     288   358351737 :     next    = new_cursor;
     289   358351737 :     _cursor = new_cursor;
     290   358351737 :     _stop   = new_cursor + rem;
     291   358351737 :     _used   = (ulong)(_stop - pod);
     292   358351737 :   } FD_POD_FOR_ALL_END;
     293             : 
     294   167777721 :   ulong new_bdy_sz = (ulong)(next - bdy);
     295             : 
     296             :   /* Compact the header and (if full) trailing padding */
     297             : 
     298   167777721 :   ulong new_csz;
     299   167777721 :   ulong new_max;
     300   167777721 :   ulong new_used;
     301             : 
     302   167777721 :   if( !full ) {
     303      204282 :     new_csz  = fd_ulong_svw_enc_sz( max ); /* <=csz */
     304      204282 :     new_max  = max;
     305      204282 :     new_used = new_csz*3UL + new_bdy_sz;
     306   167573439 :   } else {
     307   167573439 :     new_csz = 1UL;
     308   244922970 :     for(;;) {
     309   244922970 :       /**/  new_max  = new_csz*3UL + new_bdy_sz;
     310   244922970 :       ulong test_csz = fd_ulong_svw_enc_sz( new_max );
     311   244922970 :       if( FD_LIKELY( test_csz==new_csz ) ) break;
     312    77349531 :       new_csz = test_csz;
     313    77349531 :     }
     314   167573439 :     new_used = new_max;
     315   167573439 :   }
     316             : 
     317   167777721 :   memmove( pod + new_csz*3UL, bdy, new_bdy_sz );
     318   167777721 :   fd_ulong_svw_enc_fixed( pod,               new_csz, new_max  );
     319   167777721 :   fd_ulong_svw_enc_fixed( pod + new_csz,     new_csz, new_used );
     320   167777721 :   fd_ulong_svw_enc_fixed( pod + new_csz*2UL, new_csz, cnt      );
     321             : 
     322   167777721 :   return new_max;
     323   167777721 : }
     324             : 
     325             : int
     326         984 : fd_cstr_to_pod_val_type( char const * cstr ) {
     327         984 :   if( FD_UNLIKELY( !cstr ) ) return FD_POD_ERR_INVAL;
     328         981 :   if( !fd_cstr_casecmp( cstr, "subpod"  ) ) return FD_POD_VAL_TYPE_SUBPOD;
     329         975 :   if( !fd_cstr_casecmp( cstr, "buf"     ) ) return FD_POD_VAL_TYPE_BUF;
     330         969 :   if( !fd_cstr_casecmp( cstr, "cstr"    ) ) return FD_POD_VAL_TYPE_CSTR;
     331         948 :   if( !fd_cstr_casecmp( cstr, "char"    ) ) return FD_POD_VAL_TYPE_CHAR;
     332         933 :   if( !fd_cstr_casecmp( cstr, "schar"   ) ) return FD_POD_VAL_TYPE_SCHAR;
     333         918 :   if( !fd_cstr_casecmp( cstr, "short"   ) ) return FD_POD_VAL_TYPE_SHORT;
     334         903 :   if( !fd_cstr_casecmp( cstr, "int"     ) ) return FD_POD_VAL_TYPE_INT;
     335         888 :   if( !fd_cstr_casecmp( cstr, "long"    ) ) return FD_POD_VAL_TYPE_LONG;
     336         873 :   if( !fd_cstr_casecmp( cstr, "int128"  ) ) return FD_POD_VAL_TYPE_INT128;
     337         867 :   if( !fd_cstr_casecmp( cstr, "uchar"   ) ) return FD_POD_VAL_TYPE_UCHAR;
     338         852 :   if( !fd_cstr_casecmp( cstr, "ushort"  ) ) return FD_POD_VAL_TYPE_USHORT;
     339         837 :   if( !fd_cstr_casecmp( cstr, "uint"    ) ) return FD_POD_VAL_TYPE_UINT;
     340         822 :   if( !fd_cstr_casecmp( cstr, "ulong"   ) ) return FD_POD_VAL_TYPE_ULONG;
     341         807 :   if( !fd_cstr_casecmp( cstr, "uint128" ) ) return FD_POD_VAL_TYPE_UINT128;
     342         801 :   if( !fd_cstr_casecmp( cstr, "float"   ) ) return FD_POD_VAL_TYPE_FLOAT;
     343         783 :   if( !fd_cstr_casecmp( cstr, "double"  ) ) return FD_POD_VAL_TYPE_DOUBLE;
     344             :   /* FIXME: ADD FD_CSTR_NCASECMP */
     345         777 :   if( !strncmp( cstr, "user", 4UL ) || !strncmp( cstr, "USER", 4UL ) || !strncmp( cstr, "User", 4UL ) ) {
     346         774 :     int val_type = fd_cstr_to_int( cstr+4UL );
     347         774 :     if( FD_LIKELY( ((0<=val_type) & (val_type<=255)) ) ) return val_type;
     348         774 :   }
     349           9 :   return FD_POD_ERR_INVAL;
     350         777 : }
     351             : 
     352             : char *
     353             : fd_pod_val_type_to_cstr( int    val_type,
     354         921 :                          char * cstr ) {
     355         921 :   if( FD_UNLIKELY( !cstr ) ) return NULL;
     356         921 :   switch( val_type ) {
     357          69 :   case FD_POD_VAL_TYPE_SUBPOD:  return strcpy( cstr, "subpod"  );
     358           9 :   case FD_POD_VAL_TYPE_BUF:     return strcpy( cstr, "buf"     );
     359          21 :   case FD_POD_VAL_TYPE_CSTR:    return strcpy( cstr, "cstr"    );
     360           9 :   case FD_POD_VAL_TYPE_CHAR:    return strcpy( cstr, "char"    );
     361           9 :   case FD_POD_VAL_TYPE_SCHAR:   return strcpy( cstr, "schar"   );
     362           9 :   case FD_POD_VAL_TYPE_SHORT:   return strcpy( cstr, "short"   );
     363           9 :   case FD_POD_VAL_TYPE_INT:     return strcpy( cstr, "int"     );
     364           9 :   case FD_POD_VAL_TYPE_LONG:    return strcpy( cstr, "long"    );
     365           3 :   case FD_POD_VAL_TYPE_INT128:  return strcpy( cstr, "int128"  );
     366           9 :   case FD_POD_VAL_TYPE_UCHAR:   return strcpy( cstr, "uchar"   );
     367           9 :   case FD_POD_VAL_TYPE_USHORT:  return strcpy( cstr, "ushort"  );
     368           9 :   case FD_POD_VAL_TYPE_UINT:    return strcpy( cstr, "uint"    );
     369           6 :   case FD_POD_VAL_TYPE_ULONG:   return strcpy( cstr, "ulong"   );
     370           3 :   case FD_POD_VAL_TYPE_UINT128: return strcpy( cstr, "uint128" );
     371          15 :   case FD_POD_VAL_TYPE_FLOAT:   return strcpy( cstr, "float"   );
     372           3 :   case FD_POD_VAL_TYPE_DOUBLE:  return strcpy( cstr, "double"  );
     373         720 :   default: break;
     374         921 :   }
     375         720 :   if( FD_UNLIKELY( !((0<=val_type) & (val_type<=255)) ) ) return NULL;
     376         720 :   return fd_cstr_printf( cstr, FD_POD_VAL_TYPE_CSTR_MAX, NULL, "user%i", val_type );
     377         720 : }
     378             : 
     379             : /* fd_pod_subpod_grow increases the amount of space for key-val pairs
     380             :    in the deepest nested subpod on the subpod path by needed bytes.
     381             :    This operation can impact the location of items all the subpods along
     382             :    the path.  On success, returns FD_POD_SUCCESS.  On failure, returns
     383             :    FD_POD_ERR* */
     384             : 
     385             : struct fd_pod_subpod_path;
     386             : typedef struct fd_pod_subpod_path fd_pod_subpod_path_t;
     387             : 
     388             : struct fd_pod_subpod_path {
     389             :   uchar *                pod;    /* Points to the subpod val of subpod key-val pair */
     390             :   fd_pod_subpod_path_t * parent; /* Points to the subpod that contains the subpod (NULL if this subpod is in the root pod) */
     391             : };
     392             : 
     393             : static int
     394             : fd_pod_subpod_grow( fd_pod_subpod_path_t * node,
     395    26830953 :                     ulong                  needed ) { /* How much more space is needed for key-val pairs (i.e. in the body) */
     396    26830953 :   if( FD_UNLIKELY( !needed ) ) return FD_POD_SUCCESS; /* Don't need anything */
     397             : 
     398    26830953 :   fd_pod_subpod_path_t * parent = node->parent;
     399    26830953 :   if( FD_UNLIKELY( !parent ) ) return FD_POD_ERR_FULL; /* Can't grow the root pod */
     400             : 
     401             :   /* Compute how much larger the parent's val footprint needs to be,
     402             :      accounting for the possibility that this pod's header might need to
     403             :      expand and/or the parent's val_sz encoding might also need to
     404             :      expand. */
     405             : 
     406    26824929 :   uchar * pod = node->pod;
     407             : 
     408    26824929 :   ulong vsz    = fd_ulong_svw_dec_tail_sz( pod );
     409    26824929 :   ulong val_sz = fd_ulong_svw_dec_fixed( pod - vsz, vsz );
     410             : 
     411    26824929 :   ulong csz    = fd_ulong_svw_dec_sz( pod );
     412    26824929 :   ulong max    = fd_ulong_svw_dec_fixed( pod,           csz ); /* <=val_sz */
     413    26824929 :   ulong used   = fd_ulong_svw_dec_fixed( pod + csz,     csz );
     414    26824929 :   ulong cnt    = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     415    26824929 :   ulong bdy_sz = used - csz*3UL;
     416             : 
     417    26824929 :   ulong new_bdy_max = max + needed - csz*3UL;
     418    26824929 :   ulong new_csz     = csz;
     419    26824929 :   ulong new_max;
     420    28728198 :   for(;;) {
     421    28728198 :     new_max = new_csz*3UL + new_bdy_max;
     422    28728198 :     ulong test_csz = fd_ulong_svw_enc_sz( new_max );
     423    28728198 :     if( FD_LIKELY( test_csz==new_csz ) ) break;
     424     1903269 :     new_csz = test_csz;
     425     1903269 :   }
     426    26824929 :   ulong new_used = new_csz*3UL + bdy_sz;
     427             : 
     428    26824929 :   if( new_max<=val_sz ) { /* Can grow this pod without touching our parent */
     429             : 
     430             :     /* Repack the pod */
     431             : 
     432           0 :     uchar * cursor = pod + new_max;
     433             : 
     434           0 :     cursor -= new_bdy_max; memmove( cursor, pod + 3UL*csz, bdy_sz );
     435           0 :     cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, cnt      );
     436           0 :     cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, new_used );
     437           0 :     cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, new_max  );
     438             : 
     439           0 :     return FD_POD_SUCCESS;
     440           0 :   }
     441             : 
     442             :   /* fd_ulong_max below is to eliminate a very rare edge cases at a
     443             :      minuscule temporary cost in packing efficiency (compact can handle
     444             :      it).  The edge case is the ideal new_val_footprint is at most the
     445             :      current val_footprint but the current val_sz can't be encoded in
     446             :      the ideal new_val_footprint.  In this case, we'd need to repack the
     447             :      rest of the pod to use a smaller val sz but we don't have enough
     448             :      info to do that here (which would actually be correcting for a
     449             :      previous packing inefficiency).  We avoid the edge case by not
     450             :      letting new_vsz be less than vsz (such that the current val_sz is
     451             :      always encodable in the new format).  In short, this defers cleanup
     452             :      of the preexisting packing inefficiency to compact and does not
     453             :      make it worse. */
     454             : 
     455    26824929 :   ulong new_vsz = fd_ulong_max( fd_ulong_svw_enc_sz( new_max ), vsz );
     456             : 
     457    26824929 :   ulong     val_footprint = vsz     + val_sz;
     458    26824929 :   ulong new_val_footprint = new_vsz + new_max;
     459    26824929 :   if( new_val_footprint<=val_footprint ) { /* Can grow this pod without growing our parent but have to recode val_sz */
     460             : 
     461             :     /* Repack the pod */
     462             : 
     463           0 :     uchar * cursor = pod - vsz + new_val_footprint;
     464             : 
     465           0 :     cursor -= new_bdy_max; memmove( cursor, pod + 3UL*csz, bdy_sz );
     466           0 :     cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, cnt      );
     467           0 :     cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, new_used );
     468           0 :     cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, new_max  );
     469           0 :     cursor -= new_vsz;     fd_ulong_svw_enc_fixed( cursor, new_vsz, val_sz   );
     470             : 
     471           0 :     return FD_POD_SUCCESS;
     472           0 :   }
     473             : 
     474             :   /* Have to repack the parent to grow this pod */
     475             : 
     476    26824929 :   uchar * parent_pod    = parent->pod;
     477    26824929 :   ulong   parent_csz    = fd_ulong_svw_dec_sz( parent_pod );
     478    26824929 :   ulong   parent_max    = fd_ulong_svw_dec_fixed( parent_pod,              parent_csz );
     479    26824929 :   ulong   parent_used   = fd_ulong_svw_dec_fixed( parent_pod + parent_csz, parent_csz );
     480    26824929 :   ulong   parent_avail  = parent_max - parent_used;
     481    26824929 :   ulong   parent_needed = new_val_footprint - val_footprint;
     482    26824929 :   if( parent_avail < parent_needed ) { /* Need to grow the parent (and maybe their parent and ...) to grow this pod */
     483             : 
     484    22489659 :     int err = fd_pod_subpod_grow( parent, parent_needed-parent_avail );
     485    22489659 :     if( FD_UNLIKELY( err ) ) return err;
     486             : 
     487             :     /* Determine where the parent and this pod ended up after growth */
     488             : 
     489    22451028 :     ulong pod_off = (ulong)(pod - parent_pod) - parent_csz*3UL; /* Original offset pod relative to the parent body */
     490             : 
     491    22451028 :     parent_pod   = parent->pod;
     492    22451028 :     parent_csz   = fd_ulong_svw_dec_sz( parent_pod );
     493             :   //parent_max   = fd_ulong_svw_dec_fixed( parent_pod,              parent_csz );
     494    22451028 :     parent_used  = fd_ulong_svw_dec_fixed( parent_pod + parent_csz, parent_csz );
     495             :   //parent_avail = parent_max - parent_used;
     496             : 
     497    22451028 :     pod = parent_pod + parent_csz*3UL + pod_off;
     498    22451028 :   }
     499             : 
     500             :   /* At this point, our parent has enough room to accommodate growing
     501             :      this pod.  Repack the parent pod and grow this pod. */
     502             : 
     503    26786298 :   uchar * parent_next = pod         + max;
     504    26786298 :   uchar * parent_stop = parent_pod  + parent_used;
     505    26786298 :   uchar * cursor      = parent_stop + parent_needed;
     506    26786298 :   ulong   rem         = (ulong)(parent_stop - parent_next);
     507             : 
     508    26786298 :   cursor -= rem;         memmove( cursor, parent_next, rem );
     509    26786298 :   cursor -= new_bdy_max; memmove( cursor, pod + csz*3UL, bdy_sz );
     510    26786298 :   cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, cnt      );
     511    26786298 :   cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, new_used );
     512    26786298 :   cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, new_max  ); node->pod = cursor;
     513    26786298 :   cursor -= new_vsz;     fd_ulong_svw_enc_fixed( cursor, new_vsz, new_max  );
     514             : 
     515    26786298 :   fd_ulong_svw_enc_fixed( parent_pod + parent_csz, parent_csz, parent_used + parent_needed );
     516             : 
     517    26786298 :   return FD_POD_SUCCESS;
     518    26824929 : }
     519             : 
     520             : static uchar *
     521             : fd_pod_private_alloc_node( fd_pod_subpod_path_t * FD_RESTRICT parent,
     522             :                            uchar                * FD_RESTRICT pod,
     523             :                            char const           * FD_RESTRICT path,
     524             :                            int                                new_val_type,
     525     5364348 :                            ulong                              new_val_sz ) {
     526     5364348 :   fd_pod_subpod_path_t node[1];
     527     5364348 :   node->pod    = pod;
     528     5364348 :   node->parent = parent;
     529             : 
     530     5364348 :   ulong prefix_len; char delim; char const * suffix;
     531     5364348 :   FD_POD_PATH_SPLIT( path, prefix_len, delim, suffix );
     532             : 
     533     5364348 :   uchar * pair; uchar * next;
     534     5364348 :   ulong ksz; ulong key_sz; char * key; int val_type;
     535     5364348 :   ulong vsz; ulong val_sz; void * val;
     536    41088012 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     537             : 
     538    41088012 :     if( ((key_sz-1UL)==prefix_len) && !memcmp( key, path, prefix_len ) ) { /* Found leading key in pod */
     539             : 
     540      708435 :       if( !delim ) { /* Path was a single key.  Fail as key already in pod. */
     541             : 
     542       31842 :         return NULL;
     543             : 
     544      676593 :       } else { /* Path had a suffix.  Recurse into the subpod */
     545             : 
     546      676593 :         if( FD_UNLIKELY( val_type!=FD_POD_VAL_TYPE_SUBPOD ) ) return NULL;
     547      355629 :         return fd_pod_private_alloc_node( node, (uchar *)val, suffix, new_val_type, new_val_sz );
     548             : 
     549      676593 :       }
     550      708435 :     }
     551    41088012 :   } FD_POD_FOR_ALL_END;
     552             : 
     553             :   /* Leading key not found in pod. */
     554             : 
     555             :   /* Extract the pod header */
     556             : 
     557     4655913 :   ulong csz   = fd_ulong_svw_dec_sz( pod );
     558     4655913 :   ulong max   = fd_ulong_svw_dec_fixed( pod,       csz );
     559     4655913 :   ulong used  = fd_ulong_svw_dec_fixed( pod + csz, csz );
     560     4655913 :   ulong avail = max - used;
     561             : 
     562     4655913 :   if( delim ) { /* Path had a suffix. */
     563             : 
     564             :     /* Compute the amount of space needed in this pod to hold the
     565             :        subpod.  FIXME: IDEALLY WE'D DO A "DRESS REHEARSAL" TO FIGURE OUT
     566             :        TIGHTLY THE MAX AMOUNT FOR THE REST OF THE ALLOC AND SAVE TIME
     567             :        FROM HAVING TO GROW PODS REPEATEDLY FOR A DEEPLY NESTED PATH.
     568             :        THIS WOULD ALSO PREVENT ANY MODIFICATIONS BEING MADE TO THE POD
     569             :        UNLESS THE ALLOC IS SUCCESSFUL (AS IT IS, THIS MIGHT LEAVE SOME
     570             :        PATH THAT TERMINATES ON AN EMPTY POD, WHICH IS THEORETICALLY FINE
     571             :        BUT POTENTIALLY NOT THE MOST DESIRABLE PRACTICALLY). */
     572             : 
     573     4095153 :     ulong subpod_max = FD_POD_FOOTPRINT_MIN;
     574             : 
     575     4095153 :     char const * subpod_key    = path; /* Does not include terminating '\0', potentially has additional keys following. */
     576     4095153 :     ulong        subpod_key_sz = prefix_len + 1UL;
     577     4095153 :     ulong        subpod_ksz    = fd_ulong_svw_enc_sz( subpod_key_sz );
     578     4095153 :     ulong        subpod_val_sz = fd_pod_footprint( subpod_max );
     579     4095153 :     ulong        subpod_vsz    = fd_ulong_svw_enc_sz( subpod_val_sz );
     580             : 
     581     4095153 :     ulong needed = subpod_ksz + subpod_key_sz + 1UL /* subpod_val_type */ + subpod_vsz + subpod_val_sz;
     582             : 
     583             :     /* Expand the pod if necessary */
     584             : 
     585     4095153 :     if( needed > avail ) {
     586     3815574 :       int err = fd_pod_subpod_grow( node, needed-avail );
     587     3815574 :       if( FD_UNLIKELY( err ) ) return NULL;
     588     3810636 :       pod   = node->pod;
     589     3810636 :       csz   = fd_ulong_svw_dec_sz( pod );
     590             :     //max   = fd_ulong_svw_dec_fixed( pod,       csz );
     591     3810636 :       used  = fd_ulong_svw_dec_fixed( pod + csz, csz );
     592             :     //avail = max - used;
     593     3810636 :     }
     594             : 
     595             :     /* Insert the subpod */
     596             : 
     597     4090215 :     uchar * cursor = pod + used;
     598     4090215 :     fd_ulong_svw_enc_fixed( cursor, subpod_ksz, subpod_key_sz );      cursor += subpod_ksz;
     599     4090215 :     fd_memcpy( cursor, subpod_key, subpod_key_sz-1UL );
     600     4090215 :     cursor[subpod_key_sz-1UL] = '\0';                                 cursor += subpod_key_sz; /* Handle terminating '\0' */
     601     4090215 :     cursor[0                ] = (uchar)FD_POD_VAL_TYPE_SUBPOD;        cursor += 1;
     602     4090215 :     fd_ulong_svw_enc_fixed( cursor, subpod_vsz, subpod_val_sz );      cursor += subpod_vsz;
     603     4090215 :     uchar * subpod = fd_pod_join( fd_pod_new( cursor, subpod_max ) ); cursor += subpod_val_sz;
     604             : 
     605             :     /* Update the pod header */
     606             : 
     607     4090215 :     ulong cnt = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     608     4090215 :     fd_ulong_svw_enc_fixed( pod + csz,     csz, (ulong)(cursor-pod) ); /* used */
     609     4090215 :     fd_ulong_svw_enc_fixed( pod + csz*2UL, csz, cnt + 1UL );
     610             : 
     611             :     /* Recurse into the subpod */
     612             : 
     613     4090215 :     return fd_pod_private_alloc_node( node, subpod, suffix, new_val_type, new_val_sz );
     614     4095153 :   }
     615             : 
     616             :   /* Path was a single key */
     617             : 
     618             :   /* Compute how much space we need in the pod for this val. */
     619             : 
     620      560760 :   char const * new_key    = path;
     621      560760 :   ulong        new_key_sz = prefix_len + 1UL;
     622      560760 :   ulong        new_ksz    = fd_ulong_svw_enc_sz( new_key_sz );
     623      560760 :   ulong        new_vsz    = fd_ulong_svw_enc_sz( new_val_sz );
     624             : 
     625      560760 :   ulong needed = new_ksz + new_key_sz + 1UL /* new_val_type */ + new_vsz + new_val_sz;
     626             : 
     627             :   /* Expand the pod if necessary */
     628             : 
     629      560760 :   if( needed > avail ) {
     630      525720 :     int err = fd_pod_subpod_grow( node, needed-avail );
     631      525720 :     if( FD_UNLIKELY( err ) ) return NULL;
     632      524634 :     pod  = node->pod;
     633      524634 :     csz  = fd_ulong_svw_dec_sz( pod );
     634             :   //max  = fd_ulong_svw_dec_fixed( pod,       csz );
     635      524634 :     used = fd_ulong_svw_dec_fixed( pod + csz, csz );
     636      524634 :   }
     637             : 
     638             :   /* Allocate the val */
     639             : 
     640      559674 :   uchar * cursor = pod + used;
     641      559674 :   fd_ulong_svw_enc_fixed( cursor, new_ksz, new_key_sz ); cursor += new_ksz;
     642      559674 :   fd_memcpy( cursor, new_key, new_key_sz );              cursor += new_key_sz;
     643      559674 :   cursor[0] = (uchar)new_val_type;                       cursor += 1;
     644      559674 :   fd_ulong_svw_enc_fixed( cursor, new_vsz, new_val_sz ); cursor += new_vsz;
     645      559674 :   uchar * new_val = cursor;                              cursor += new_val_sz;
     646             : 
     647             :   /* Update the pod header */
     648             : 
     649      559674 :   ulong cnt = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     650      559674 :   fd_ulong_svw_enc_fixed( pod + csz,     csz, (ulong)(cursor-pod) ); /* used */
     651      559674 :   fd_ulong_svw_enc_fixed( pod + csz*2UL, csz, cnt + 1UL );
     652             : 
     653      559674 :   return new_val;
     654      560760 : }
     655             : 
     656             : ulong
     657             : fd_pod_alloc( uchar      * FD_RESTRICT pod,
     658             :               char const * FD_RESTRICT path,
     659             :               int                      val_type,
     660      739077 :               ulong                    val_sz ) {
     661      739077 :   if( FD_UNLIKELY( (!pod) | (!path) | (!((0<=val_type) & (val_type<=255))) ) ) return 0UL;
     662             : 
     663      739077 :   uchar * val = fd_pod_private_alloc_node( NULL, pod, path, val_type, val_sz );
     664      739077 :   if( FD_UNLIKELY( !val ) ) {
     665      179427 :     fd_pod_compact( pod, 0 /*partial*/ );
     666      179427 :     val = fd_pod_private_alloc_node( NULL, pod, path, val_type, val_sz );
     667      179427 :     if( FD_UNLIKELY( !val ) ) return 0UL;
     668      179427 :   }
     669             : 
     670      559674 :   return (ulong)(val-pod);
     671      739077 : }
     672             : 
     673             : int
     674             : fd_pod_remove( uchar      * FD_RESTRICT pod,
     675     1115088 :                char const * FD_RESTRICT path ) {
     676     1115088 :   if( FD_UNLIKELY( (!pod) | (!path) ) ) return FD_POD_ERR_INVAL;
     677             : 
     678     1115088 :   ulong prefix_len; char delim; char const * suffix;
     679     1115088 :   FD_POD_PATH_SPLIT( path, prefix_len, delim, suffix );
     680             : 
     681     1115088 :   uchar * pair; uchar * next;
     682     1115088 :   ulong ksz; ulong key_sz; char const * key; int val_type;
     683     1115088 :   ulong vsz; ulong val_sz; void * val;
     684    30380142 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     685             : 
     686    30380142 :     if( ((key_sz-1UL)==prefix_len) && !memcmp( key, path, prefix_len ) ) { /* Found leading key in pod */
     687             : 
     688      532449 :       if( !delim ) { /* Path was a single key, delete it */
     689             : 
     690       17049 :         ulong footprint = (ulong)( next - pair);
     691       17049 :         ulong rem       = (ulong)(_stop - next);
     692       17049 :         memmove( pair, next, rem );
     693       17049 :         ulong cnt = fd_ulong_svw_dec_fixed( pod + _csz*2UL, _csz );
     694       17049 :         fd_ulong_svw_enc_fixed( pod + _csz,     _csz, _used - footprint ); /* upd used */
     695       17049 :         fd_ulong_svw_enc_fixed( pod + _csz*2UL, _csz, cnt - 1UL         ); /* upd cnt */
     696       17049 :         return FD_POD_SUCCESS;
     697             : 
     698      515400 :       } else { /* Path had a suffix.  Recurse into the subpod */
     699             : 
     700      515400 :         if( FD_UNLIKELY( val_type!=FD_POD_VAL_TYPE_SUBPOD ) ) return FD_POD_ERR_TYPE;
     701      343959 :         return fd_pod_remove( (uchar *)val, suffix );
     702             : 
     703      515400 :       }
     704      532449 :     }
     705             : 
     706    30380142 :   } FD_POD_FOR_ALL_END;
     707             : 
     708      582639 :   return FD_POD_ERR_RESOLVE;
     709     1115088 : }
     710             : 
     711             : #undef FD_POD_FOR_ALL_END
     712             : #undef FD_POD_FOR_ALL_BEGIN
     713             : #undef FD_POD_PATH_SPLIT
     714             : 

Generated by: LCOV version 1.14