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: 2025-01-08 12:08:44 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    30075294 : #define FD_POD_PATH_SPLIT( path, prefix_len, delim, suffix ) do { \
      11    30075294 :     suffix = path;                                                \
      12   145743486 :     for(;;) {                                                     \
      13   145743486 :       delim = suffix[0];                                          \
      14   145743486 :       if( FD_UNLIKELY( (delim=='.') | (!delim) ) ) break;         \
      15   145743486 :       suffix++;                                                   \
      16   115668192 :     }                                                             \
      17    30075294 :     prefix_len = (ulong)(suffix - path);                          \
      18    30075294 :     suffix++;                                                     \
      19    30075294 :   } 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   704329413 :   do {                                                                                        \
      47   704329413 :     ulong   _csz    = fd_ulong_svw_dec_sz( pod );                                             \
      48   704329413 :     ulong   _used   = fd_ulong_svw_dec_fixed( pod + _csz, _csz );                             \
      49   704329413 :     uchar * _cursor = (uchar *)(pod + _csz*3UL);                                              \
      50   704329413 :     uchar * _stop   = (uchar *)(pod + _used);                                                 \
      51  1984614354 :     while( _cursor<_stop ) {                                                                  \
      52  1303976028 :       pair     = _cursor;                                                   (void)pair;       \
      53  1303976028 :       ksz      = fd_ulong_svw_dec_sz( _cursor );                                              \
      54  1303976028 :       key_sz   = fd_ulong_svw_dec_fixed( _cursor, ksz ); _cursor += ksz;                      \
      55  1303976028 :       key      = (char *)_cursor;                        _cursor += key_sz; (void)key;        \
      56  1303976028 :       val_type = (int)_cursor[0];                        _cursor += 1;      (void)val_type;   \
      57  1303976028 :       vsz      = fd_ulong_svw_dec_sz( _cursor );                                              \
      58  1303976028 :       val_sz   = fd_ulong_svw_dec_fixed( _cursor, vsz ); _cursor += vsz;                      \
      59  1303976028 :       val      = (void *)_cursor;                        _cursor += val_sz; (void)val;        \
      60  1303976028 :       next     = _cursor;                                                   (void)next;       \
      61             : 
      62             : #define FD_POD_FOR_ALL_END \
      63  1303976028 :     }                      \
      64   704329413 :   } 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    23595249 :               fd_pod_info_t * FD_RESTRICT opt_info ) {
     161    23595249 :   if( FD_UNLIKELY( (!pod) | (!path) ) ) return FD_POD_ERR_INVAL;
     162             : 
     163    23595249 :   ulong prefix_len; char delim; char const * suffix;
     164    23595249 :   FD_POD_PATH_SPLIT( path, prefix_len, delim, suffix );
     165             : 
     166    23595249 :   uchar const * pair; uchar const * next;
     167    23595249 :   ulong ksz; ulong key_sz; char const * key; int val_type;
     168    23595249 :   ulong vsz; ulong val_sz; void const * val;
     169   481547109 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     170             : 
     171   481547109 :     if( ((key_sz-1UL)==prefix_len) && !memcmp( key, path, prefix_len ) ) { /* Found leading key in pod */
     172             : 
     173    22449888 :       if( !delim ) { /* Path was a single key, return it */
     174             : 
     175    17061027 :         if( opt_info ) {
     176    17061027 :           opt_info->key_sz   = key_sz;
     177    17061027 :           opt_info->key      = key;
     178    17061027 :           opt_info->val_type = val_type;
     179    17061027 :           opt_info->val_sz   = val_sz;
     180    17061027 :           opt_info->val      = val;
     181    17061027 :           opt_info->parent   = NULL;
     182    17061027 :         }
     183    17061027 :         return FD_POD_SUCCESS;
     184             : 
     185    17061027 :       } else { /* Path had a suffix.  Recurse into the subpod */
     186             : 
     187     5388861 :         if( FD_UNLIKELY( val_type!=FD_POD_VAL_TYPE_SUBPOD ) ) return FD_POD_ERR_TYPE;
     188     5056905 :         return fd_pod_query( (uchar const *)val, suffix, opt_info );
     189             : 
     190     5388861 :       }
     191    22449888 :     }
     192             : 
     193   481547109 :   } FD_POD_FOR_ALL_END;
     194             : 
     195     1145361 :   return FD_POD_ERR_RESOLVE;
     196    23595249 : }
     197             : 
     198             : char const *
     199          30 : fd_pod_strerror( int err ) {
     200          30 :   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          15 :   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          30 :   }
     208           3 :   return "unknown";
     209          30 : }
     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   167777793 :                 int     full ) {
     242   167777793 :   if( FD_UNLIKELY( !pod ) ) return 0UL;
     243             : 
     244             :   /* Compact the body */
     245             : 
     246   167777793 :   ulong   csz = fd_ulong_svw_dec_sz( pod );
     247   167777793 :   ulong   max = fd_ulong_svw_dec_fixed( pod,           csz );
     248   167777793 :   ulong   cnt = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     249   167777793 :   uchar * bdy = pod + csz*3UL;
     250             : 
     251   167777793 :   uchar * pair; uchar * next = bdy;
     252   167777793 :   ulong ksz; ulong key_sz; char const * key; int val_type;
     253   167777793 :   ulong vsz; ulong val_sz; void * val;
     254   358351959 :   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   358351959 :     ulong new_key_sz = strlen( key ) + 1UL; /* <=key_sz */
     259   358351959 :     ulong new_ksz    = fd_ulong_svw_enc_sz( new_key_sz );
     260             : 
     261   358351959 :     ulong new_val_sz;
     262   358351959 :     if( val_type==FD_POD_VAL_TYPE_SUBPOD ) {
     263   167548671 :       uchar * subpod = (uchar *)val;
     264   167548671 :       fd_pod_compact( subpod, 1 /*full*/ ); /* Yes, do not pass through the user provided value of full */
     265   167548671 :       new_val_sz = fd_pod_max( subpod ); /* ==fd_pod_used at this point */
     266   167548671 :     } else if( val_type==FD_POD_VAL_TYPE_CSTR ) {
     267     1537491 :       new_val_sz = val_sz ? (strlen( (char *)val )+1UL) : 0UL;
     268    21488004 :     } else {
     269    21488004 :       new_val_sz = val_sz;
     270    21488004 :     }
     271             :     /* Note: new_val_sz<=val_sz */
     272   358351959 :     ulong new_vsz = fd_ulong_svw_enc_sz( new_val_sz );
     273             : 
     274   358351959 :     uchar * new_cursor = pair;
     275   358351959 :     fd_ulong_svw_enc_fixed( new_cursor, new_ksz, new_key_sz ); new_cursor += new_ksz;
     276   358351959 :     memmove( new_cursor, key, new_key_sz );                    new_cursor += new_key_sz;
     277   358351959 :     new_cursor[0] = (uchar)val_type;                           new_cursor += 1;
     278   358351959 :     fd_ulong_svw_enc_fixed( new_cursor, new_vsz, new_val_sz ); new_cursor += new_vsz;
     279   358351959 :     memmove( new_cursor, val, new_val_sz );                    new_cursor += new_val_sz;
     280             : 
     281             :     /* Compact the trailing space */
     282             : 
     283   358351959 :     ulong rem = (ulong)(_stop-next);
     284   358351959 :     memmove( new_cursor, next, rem );
     285             : 
     286             :     /* Update the iterator internals */
     287             : 
     288   358351959 :     next    = new_cursor;
     289   358351959 :     _cursor = new_cursor;
     290   358351959 :     _stop   = new_cursor + rem;
     291   358351959 :     _used   = (ulong)(_stop - pod);
     292   358351959 :   } FD_POD_FOR_ALL_END;
     293             : 
     294   167777793 :   ulong new_bdy_sz = (ulong)(next - bdy);
     295             : 
     296             :   /* Compact the header and (if full) trailing padding */
     297             : 
     298   167777793 :   ulong new_csz;
     299   167777793 :   ulong new_max;
     300   167777793 :   ulong new_used;
     301             : 
     302   167777793 :   if( !full ) {
     303      204288 :     new_csz  = fd_ulong_svw_enc_sz( max ); /* <=csz */
     304      204288 :     new_max  = max;
     305      204288 :     new_used = new_csz*3UL + new_bdy_sz;
     306   167573505 :   } else {
     307   167573505 :     new_csz = 1UL;
     308   244923036 :     for(;;) {
     309   244923036 :       /**/  new_max  = new_csz*3UL + new_bdy_sz;
     310   244923036 :       ulong test_csz = fd_ulong_svw_enc_sz( new_max );
     311   244923036 :       if( FD_LIKELY( test_csz==new_csz ) ) break;
     312    77349531 :       new_csz = test_csz;
     313    77349531 :     }
     314   167573505 :     new_used = new_max;
     315   167573505 :   }
     316             : 
     317   167777793 :   memmove( pod + new_csz*3UL, bdy, new_bdy_sz );
     318   167777793 :   fd_ulong_svw_enc_fixed( pod,               new_csz, new_max  );
     319   167777793 :   fd_ulong_svw_enc_fixed( pod + new_csz,     new_csz, new_used );
     320   167777793 :   fd_ulong_svw_enc_fixed( pod + new_csz*2UL, new_csz, cnt      );
     321             : 
     322   167777793 :   return new_max;
     323   167777793 : }
     324             : 
     325             : int
     326        1008 : fd_cstr_to_pod_val_type( char const * cstr ) {
     327        1008 :   if( FD_UNLIKELY( !cstr ) ) return FD_POD_ERR_INVAL;
     328        1005 :   if( !fd_cstr_casecmp( cstr, "subpod"  ) ) return FD_POD_VAL_TYPE_SUBPOD;
     329         999 :   if( !fd_cstr_casecmp( cstr, "buf"     ) ) return FD_POD_VAL_TYPE_BUF;
     330         993 :   if( !fd_cstr_casecmp( cstr, "cstr"    ) ) return FD_POD_VAL_TYPE_CSTR;
     331         963 :   if( !fd_cstr_casecmp( cstr, "char"    ) ) return FD_POD_VAL_TYPE_CHAR;
     332         948 :   if( !fd_cstr_casecmp( cstr, "schar"   ) ) return FD_POD_VAL_TYPE_SCHAR;
     333         933 :   if( !fd_cstr_casecmp( cstr, "short"   ) ) return FD_POD_VAL_TYPE_SHORT;
     334         918 :   if( !fd_cstr_casecmp( cstr, "int"     ) ) return FD_POD_VAL_TYPE_INT;
     335         897 :   if( !fd_cstr_casecmp( cstr, "long"    ) ) return FD_POD_VAL_TYPE_LONG;
     336         882 :   if( !fd_cstr_casecmp( cstr, "int128"  ) ) return FD_POD_VAL_TYPE_INT128;
     337         876 :   if( !fd_cstr_casecmp( cstr, "uchar"   ) ) return FD_POD_VAL_TYPE_UCHAR;
     338         861 :   if( !fd_cstr_casecmp( cstr, "ushort"  ) ) return FD_POD_VAL_TYPE_USHORT;
     339         846 :   if( !fd_cstr_casecmp( cstr, "uint"    ) ) return FD_POD_VAL_TYPE_UINT;
     340         831 :   if( !fd_cstr_casecmp( cstr, "ulong"   ) ) return FD_POD_VAL_TYPE_ULONG;
     341         816 :   if( !fd_cstr_casecmp( cstr, "uint128" ) ) return FD_POD_VAL_TYPE_UINT128;
     342         810 :   if( !fd_cstr_casecmp( cstr, "float"   ) ) return FD_POD_VAL_TYPE_FLOAT;
     343         792 :   if( !fd_cstr_casecmp( cstr, "double"  ) ) return FD_POD_VAL_TYPE_DOUBLE;
     344             :   /* FIXME: ADD FD_CSTR_NCASECMP */
     345         786 :   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          18 :   return FD_POD_ERR_INVAL;
     350         786 : }
     351             : 
     352             : char *
     353             : fd_pod_val_type_to_cstr( int    val_type,
     354         930 :                          char * cstr ) {
     355         930 :   if( FD_UNLIKELY( !cstr ) ) return NULL;
     356         930 :   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          30 :   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         930 :   }
     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    26831358 :                     ulong                  needed ) { /* How much more space is needed for key-val pairs (i.e. in the body) */
     396    26831358 :   if( FD_UNLIKELY( !needed ) ) return FD_POD_SUCCESS; /* Don't need anything */
     397             : 
     398    26831358 :   fd_pod_subpod_path_t * parent = node->parent;
     399    26831358 :   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    26825334 :   uchar * pod = node->pod;
     407             : 
     408    26825334 :   ulong vsz    = fd_ulong_svw_dec_tail_sz( pod );
     409    26825334 :   ulong val_sz = fd_ulong_svw_dec_fixed( pod - vsz, vsz );
     410             : 
     411    26825334 :   ulong csz    = fd_ulong_svw_dec_sz( pod );
     412    26825334 :   ulong max    = fd_ulong_svw_dec_fixed( pod,           csz ); /* <=val_sz */
     413    26825334 :   ulong used   = fd_ulong_svw_dec_fixed( pod + csz,     csz );
     414    26825334 :   ulong cnt    = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     415    26825334 :   ulong bdy_sz = used - csz*3UL;
     416             : 
     417    26825334 :   ulong new_bdy_max = max + needed - csz*3UL;
     418    26825334 :   ulong new_csz     = csz;
     419    26825334 :   ulong new_max;
     420    28728603 :   for(;;) {
     421    28728603 :     new_max = new_csz*3UL + new_bdy_max;
     422    28728603 :     ulong test_csz = fd_ulong_svw_enc_sz( new_max );
     423    28728603 :     if( FD_LIKELY( test_csz==new_csz ) ) break;
     424     1903269 :     new_csz = test_csz;
     425     1903269 :   }
     426    26825334 :   ulong new_used = new_csz*3UL + bdy_sz;
     427             : 
     428    26825334 :   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    26825334 :   ulong new_vsz = fd_ulong_max( fd_ulong_svw_enc_sz( new_max ), vsz );
     456             : 
     457    26825334 :   ulong     val_footprint = vsz     + val_sz;
     458    26825334 :   ulong new_val_footprint = new_vsz + new_max;
     459    26825334 :   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    26825334 :   uchar * parent_pod    = parent->pod;
     477    26825334 :   ulong   parent_csz    = fd_ulong_svw_dec_sz( parent_pod );
     478    26825334 :   ulong   parent_max    = fd_ulong_svw_dec_fixed( parent_pod,              parent_csz );
     479    26825334 :   ulong   parent_used   = fd_ulong_svw_dec_fixed( parent_pod + parent_csz, parent_csz );
     480    26825334 :   ulong   parent_avail  = parent_max - parent_used;
     481    26825334 :   ulong   parent_needed = new_val_footprint - val_footprint;
     482    26825334 :   if( parent_avail < parent_needed ) { /* Need to grow the parent (and maybe their parent and ...) to grow this pod */
     483             : 
     484    22489818 :     int err = fd_pod_subpod_grow( parent, parent_needed-parent_avail );
     485    22489818 :     if( FD_UNLIKELY( err ) ) return err;
     486             : 
     487             :     /* Determine where the parent and this pod ended up after growth */
     488             : 
     489    22451187 :     ulong pod_off = (ulong)(pod - parent_pod) - parent_csz*3UL; /* Original offset pod relative to the parent body */
     490             : 
     491    22451187 :     parent_pod   = parent->pod;
     492    22451187 :     parent_csz   = fd_ulong_svw_dec_sz( parent_pod );
     493             :   //parent_max   = fd_ulong_svw_dec_fixed( parent_pod,              parent_csz );
     494    22451187 :     parent_used  = fd_ulong_svw_dec_fixed( parent_pod + parent_csz, parent_csz );
     495             :   //parent_avail = parent_max - parent_used;
     496             : 
     497    22451187 :     pod = parent_pod + parent_csz*3UL + pod_off;
     498    22451187 :   }
     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    26786703 :   uchar * parent_next = pod         + max;
     504    26786703 :   uchar * parent_stop = parent_pod  + parent_used;
     505    26786703 :   uchar * cursor      = parent_stop + parent_needed;
     506    26786703 :   ulong   rem         = (ulong)(parent_stop - parent_next);
     507             : 
     508    26786703 :   cursor -= rem;         memmove( cursor, parent_next, rem );
     509    26786703 :   cursor -= new_bdy_max; memmove( cursor, pod + csz*3UL, bdy_sz );
     510    26786703 :   cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, cnt      );
     511    26786703 :   cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, new_used );
     512    26786703 :   cursor -= new_csz;     fd_ulong_svw_enc_fixed( cursor, new_csz, new_max  ); node->pod = cursor;
     513    26786703 :   cursor -= new_vsz;     fd_ulong_svw_enc_fixed( cursor, new_vsz, new_max  );
     514             : 
     515    26786703 :   fd_ulong_svw_enc_fixed( parent_pod + parent_csz, parent_csz, parent_used + parent_needed );
     516             : 
     517    26786703 :   return FD_POD_SUCCESS;
     518    26825334 : }
     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     5364855 :                            ulong                              new_val_sz ) {
     526     5364855 :   fd_pod_subpod_path_t node[1];
     527     5364855 :   node->pod    = pod;
     528     5364855 :   node->parent = parent;
     529             : 
     530     5364855 :   ulong prefix_len; char delim; char const * suffix;
     531     5364855 :   FD_POD_PATH_SPLIT( path, prefix_len, delim, suffix );
     532             : 
     533     5364855 :   uchar * pair; uchar * next;
     534     5364855 :   ulong ksz; ulong key_sz; char * key; int val_type;
     535     5364855 :   ulong vsz; ulong val_sz; void * val;
     536    41100939 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     537             : 
     538    41100939 :     if( ((key_sz-1UL)==prefix_len) && !memcmp( key, path, prefix_len ) ) { /* Found leading key in pod */
     539             : 
     540      708696 :       if( !delim ) { /* Path was a single key.  Fail as key already in pod. */
     541             : 
     542       31854 :         return NULL;
     543             : 
     544      676842 :       } else { /* Path had a suffix.  Recurse into the subpod */
     545             : 
     546      676842 :         if( FD_UNLIKELY( val_type!=FD_POD_VAL_TYPE_SUBPOD ) ) return NULL;
     547      355878 :         return fd_pod_private_alloc_node( node, (uchar *)val, suffix, new_val_type, new_val_sz );
     548             : 
     549      676842 :       }
     550      708696 :     }
     551    41100939 :   } FD_POD_FOR_ALL_END;
     552             : 
     553             :   /* Leading key not found in pod. */
     554             : 
     555             :   /* Extract the pod header */
     556             : 
     557     4656159 :   ulong csz   = fd_ulong_svw_dec_sz( pod );
     558     4656159 :   ulong max   = fd_ulong_svw_dec_fixed( pod,       csz );
     559     4656159 :   ulong used  = fd_ulong_svw_dec_fixed( pod + csz, csz );
     560     4656159 :   ulong avail = max - used;
     561             : 
     562     4656159 :   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     4095231 :     ulong subpod_max = FD_POD_FOOTPRINT_MIN;
     574             : 
     575     4095231 :     char const * subpod_key    = path; /* Does not include terminating '\0', potentially has additional keys following. */
     576     4095231 :     ulong        subpod_key_sz = prefix_len + 1UL;
     577     4095231 :     ulong        subpod_ksz    = fd_ulong_svw_enc_sz( subpod_key_sz );
     578     4095231 :     ulong        subpod_val_sz = fd_pod_footprint( subpod_max );
     579     4095231 :     ulong        subpod_vsz    = fd_ulong_svw_enc_sz( subpod_val_sz );
     580             : 
     581     4095231 :     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     4095231 :     if( needed > avail ) {
     586     3815652 :       int err = fd_pod_subpod_grow( node, needed-avail );
     587     3815652 :       if( FD_UNLIKELY( err ) ) return NULL;
     588     3810714 :       pod   = node->pod;
     589     3810714 :       csz   = fd_ulong_svw_dec_sz( pod );
     590             :     //max   = fd_ulong_svw_dec_fixed( pod,       csz );
     591     3810714 :       used  = fd_ulong_svw_dec_fixed( pod + csz, csz );
     592             :     //avail = max - used;
     593     3810714 :     }
     594             : 
     595             :     /* Insert the subpod */
     596             : 
     597     4090293 :     uchar * cursor = pod + used;
     598     4090293 :     fd_ulong_svw_enc_fixed( cursor, subpod_ksz, subpod_key_sz );      cursor += subpod_ksz;
     599     4090293 :     fd_memcpy( cursor, subpod_key, subpod_key_sz-1UL );
     600     4090293 :     cursor[subpod_key_sz-1UL] = '\0';                                 cursor += subpod_key_sz; /* Handle terminating '\0' */
     601     4090293 :     cursor[0                ] = (uchar)FD_POD_VAL_TYPE_SUBPOD;        cursor += 1;
     602     4090293 :     fd_ulong_svw_enc_fixed( cursor, subpod_vsz, subpod_val_sz );      cursor += subpod_vsz;
     603     4090293 :     uchar * subpod = fd_pod_join( fd_pod_new( cursor, subpod_max ) ); cursor += subpod_val_sz;
     604             : 
     605             :     /* Update the pod header */
     606             : 
     607     4090293 :     ulong cnt = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     608     4090293 :     fd_ulong_svw_enc_fixed( pod + csz,     csz, (ulong)(cursor-pod) ); /* used */
     609     4090293 :     fd_ulong_svw_enc_fixed( pod + csz*2UL, csz, cnt + 1UL );
     610             : 
     611             :     /* Recurse into the subpod */
     612             : 
     613     4090293 :     return fd_pod_private_alloc_node( node, subpod, suffix, new_val_type, new_val_sz );
     614     4095231 :   }
     615             : 
     616             :   /* Path was a single key */
     617             : 
     618             :   /* Compute how much space we need in the pod for this val. */
     619             : 
     620      560928 :   char const * new_key    = path;
     621      560928 :   ulong        new_key_sz = prefix_len + 1UL;
     622      560928 :   ulong        new_ksz    = fd_ulong_svw_enc_sz( new_key_sz );
     623      560928 :   ulong        new_vsz    = fd_ulong_svw_enc_sz( new_val_sz );
     624             : 
     625      560928 :   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      560928 :   if( needed > avail ) {
     630      525888 :     int err = fd_pod_subpod_grow( node, needed-avail );
     631      525888 :     if( FD_UNLIKELY( err ) ) return NULL;
     632      524802 :     pod  = node->pod;
     633      524802 :     csz  = fd_ulong_svw_dec_sz( pod );
     634             :   //max  = fd_ulong_svw_dec_fixed( pod,       csz );
     635      524802 :     used = fd_ulong_svw_dec_fixed( pod + csz, csz );
     636      524802 :   }
     637             : 
     638             :   /* Allocate the val */
     639             : 
     640      559842 :   uchar * cursor = pod + used;
     641      559842 :   fd_ulong_svw_enc_fixed( cursor, new_ksz, new_key_sz ); cursor += new_ksz;
     642      559842 :   fd_memcpy( cursor, new_key, new_key_sz );              cursor += new_key_sz;
     643      559842 :   cursor[0] = (uchar)new_val_type;                       cursor += 1;
     644      559842 :   fd_ulong_svw_enc_fixed( cursor, new_vsz, new_val_sz ); cursor += new_vsz;
     645      559842 :   uchar * new_val = cursor;                              cursor += new_val_sz;
     646             : 
     647             :   /* Update the pod header */
     648             : 
     649      559842 :   ulong cnt = fd_ulong_svw_dec_fixed( pod + csz*2UL, csz );
     650      559842 :   fd_ulong_svw_enc_fixed( pod + csz,     csz, (ulong)(cursor-pod) ); /* used */
     651      559842 :   fd_ulong_svw_enc_fixed( pod + csz*2UL, csz, cnt + 1UL );
     652             : 
     653      559842 :   return new_val;
     654      560928 : }
     655             : 
     656             : ulong
     657             : fd_pod_alloc( uchar      * FD_RESTRICT pod,
     658             :               char const * FD_RESTRICT path,
     659             :               int                      val_type,
     660      739251 :               ulong                    val_sz ) {
     661      739251 :   if( FD_UNLIKELY( (!pod) | (!path) | (!((0<=val_type) & (val_type<=255))) ) ) return 0UL;
     662             : 
     663      739251 :   uchar * val = fd_pod_private_alloc_node( NULL, pod, path, val_type, val_sz );
     664      739251 :   if( FD_UNLIKELY( !val ) ) {
     665      179433 :     fd_pod_compact( pod, 0 /*partial*/ );
     666      179433 :     val = fd_pod_private_alloc_node( NULL, pod, path, val_type, val_sz );
     667      179433 :     if( FD_UNLIKELY( !val ) ) return 0UL;
     668      179433 :   }
     669             : 
     670      559842 :   return (ulong)(val-pod);
     671      739251 : }
     672             : 
     673             : int
     674             : fd_pod_remove( uchar      * FD_RESTRICT pod,
     675     1115190 :                char const * FD_RESTRICT path ) {
     676     1115190 :   if( FD_UNLIKELY( (!pod) | (!path) ) ) return FD_POD_ERR_INVAL;
     677             : 
     678     1115190 :   ulong prefix_len; char delim; char const * suffix;
     679     1115190 :   FD_POD_PATH_SPLIT( path, prefix_len, delim, suffix );
     680             : 
     681     1115190 :   uchar * pair; uchar * next;
     682     1115190 :   ulong ksz; ulong key_sz; char const * key; int val_type;
     683     1115190 :   ulong vsz; ulong val_sz; void * val;
     684    30385629 :   FD_POD_FOR_ALL_BEGIN( pod, pair, next, ksz, key_sz, key, val_type, vsz, val_sz, val ) {
     685             : 
     686    30385629 :     if( ((key_sz-1UL)==prefix_len) && !memcmp( key, path, prefix_len ) ) { /* Found leading key in pod */
     687             : 
     688      532503 :       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      515454 :       } else { /* Path had a suffix.  Recurse into the subpod */
     699             : 
     700      515454 :         if( FD_UNLIKELY( val_type!=FD_POD_VAL_TYPE_SUBPOD ) ) return FD_POD_ERR_TYPE;
     701      344013 :         return fd_pod_remove( (uchar *)val, suffix );
     702             : 
     703      515454 :       }
     704      532503 :     }
     705             : 
     706    30385629 :   } FD_POD_FOR_ALL_END;
     707             : 
     708      582687 :   return FD_POD_ERR_RESOLVE;
     709     1115190 : }
     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