LCOV - code coverage report
Current view: top level - flamenco/types - fd_types_yaml.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 129 189 68.3 %
Date: 2025-03-20 12:08:36 Functions: 6 9 66.7 %

          Line data    Source code
       1             : #include "fd_types_yaml.h"
       2             : #include "fd_types_meta.h"
       3             : #include "../../ballet/base58/fd_base58.h"
       4             : 
       5             : #include <ctype.h>
       6             : #include <stdio.h>
       7             : #include <stdlib.h>
       8             : 
       9             : #define FD_FLAMENCO_YAML_INDENT_BUFSZ (2UL*FD_FLAMENCO_YAML_MAX_INDENT+1UL)
      10             : 
      11             : /* STATE_{...} identify the state of the YAML writer.  This is used
      12             :    because assembling the YAML stream requires different combinations
      13             :    of print operations depending on the sequence of AST nodes. */
      14             : 
      15             : #define STATE_NULL         (0)  /* Sentinel value */
      16      146958 : #define STATE_OBJECT_BEGIN (2)  /* Writing object, ==0 elems so far */
      17      254814 : #define STATE_OBJECT       (3)  /* Writing object,  >0 elems so far */
      18         249 : #define STATE_ARRAY_BEGIN  (4)  /* Writing array,  ==0 elems so far */
      19         993 : #define STATE_ARRAY        (5)  /* Writing array,   >0 elems so far */
      20             : #define STATE_OPTION_BEGIN (6)  /* Writing nullable, waiting for elem */
      21             : 
      22             : /* fd_flamenco_yaml provides methods for converting a bincode-like AST of
      23             :    nodes into a YAML text stream.
      24             : 
      25             :    indent is a string containing the prefix suitable for the current
      26             :    indent level.  indent_stack[ i ] is the number of chars in indent
      27             :    level i.
      28             : 
      29             :    For example, the following structure
      30             : 
      31             :      my_object:
      32             :        key0: 34
      33             :        key1:
      34             :        - 128
      35             :        - 129
      36             :        key2: true
      37             :        key3: null
      38             : 
      39             :    Results in the following walk:
      40             : 
      41             :      [LEVEL] [TYPE]  [NAME]    [VALUE]
      42             :           0  MAP
      43             :           1  MAP     my_object
      44             :           2  SINT    key0       34
      45             :           2  ARR     key1
      46             :           3  SINT              128
      47             :           3  SINT              129
      48             :           3  ARR_END
      49             :           2  BOOL    key2      true
      50             :           2  MAP_END
      51             :           2  OPT     key3
      52             :           3  OPT_END
      53             :           1  MAP_END
      54             :           0  MAP_END
      55             : 
      56             :    After the start node of a collection types (arrays, maps, options),
      57             :    the walk level may increment.  The subsequent nodes in this
      58             :    incremented level then belong to the collection.  The last node in
      59             :    the incremented level is always the collection's corresponding end
      60             :    node.
      61             : 
      62             :    Finally, we support option types.  During walk, these are presented
      63             :    as a separate  */
      64             : 
      65             : struct fd_flamenco_yaml {
      66             :   void * file;   /* (FILE *) or platform equivalent */
      67             : 
      68             :   int  stack [ FD_FLAMENCO_YAML_MAX_INDENT   ];
      69             :   char indent[ FD_FLAMENCO_YAML_INDENT_BUFSZ ];
      70             : };
      71             : 
      72             : 
      73             : ulong
      74          57 : fd_flamenco_yaml_align( void ) {
      75          57 :   return alignof(fd_flamenco_yaml_t);
      76          57 : }
      77             : 
      78             : ulong
      79          57 : fd_flamenco_yaml_footprint( void ) {
      80          57 :   return sizeof(fd_flamenco_yaml_t);
      81          57 : }
      82             : 
      83             : fd_flamenco_yaml_t *
      84          57 : fd_flamenco_yaml_new( void * mem ) {
      85             : 
      86          57 :   if( FD_UNLIKELY( !mem ) ) {
      87           0 :     FD_LOG_WARNING(( "NULL mem" ));
      88           0 :     return NULL;
      89           0 :   }
      90             : 
      91          57 :   fd_flamenco_yaml_t * yaml = (fd_flamenco_yaml_t *)mem;
      92          57 :   memset( yaml,         0,   sizeof(*yaml)        );
      93          57 :   memset( yaml->indent, ' ', sizeof(yaml->indent) );
      94          57 :   return (fd_flamenco_yaml_t *)mem;
      95          57 : }
      96             : 
      97             : void *
      98          24 : fd_flamenco_yaml_delete( fd_flamenco_yaml_t * yaml ) {
      99          24 :   return yaml;
     100          24 : }
     101             : 
     102             : fd_flamenco_yaml_t *
     103             : fd_flamenco_yaml_init( fd_flamenco_yaml_t * self,
     104          57 :                        void *               _file ) {
     105             : 
     106          57 :   if( FD_UNLIKELY( !self ) ) {
     107           0 :     FD_LOG_WARNING(( "NULL self" ));
     108           0 :     return NULL;
     109           0 :   }
     110          57 :   if( FD_UNLIKELY( !_file ) ) {
     111           0 :     FD_LOG_WARNING(( "NULL file" ));
     112           0 :     return NULL;
     113           0 :   }
     114             : 
     115          57 :   self->file = _file;
     116             : 
     117          57 :   return self;
     118          57 : }
     119             : 
     120             : void *
     121           0 : fd_flamenco_yaml_file( fd_flamenco_yaml_t * self ) {
     122           0 :   return self->file;
     123           0 : }
     124             : 
     125             : /* fd_flamenco_yaml_walk iteratively serializes YAML while keeping
     126             :    minimal state.
     127             : 
     128             :    Throughout this function, serialization state is illustrated using
     129             :    code comments.  The '$' symbol symbolizes the current stream cursor. */
     130             : 
     131             : void
     132             : fd_flamenco_yaml_walk( void *       _self,
     133             :                        void const * arg,
     134             :                        char const * name,
     135             :                        int          type,
     136             :                        char const * type_name,
     137      256005 :                        uint         level ) {
     138             : 
     139      256005 :   (void)type_name;
     140             : 
     141      256005 :   if( level>=FD_FLAMENCO_YAML_MAX_INDENT-1 ) {
     142           0 :     FD_LOG_WARNING(( "indent level %u exceeds max %lu",
     143           0 :                      level, FD_FLAMENCO_YAML_MAX_INDENT ));
     144           0 :     return;
     145           0 :   }
     146             : 
     147      256005 :   if( type == FD_FLAMENCO_TYPE_ENUM_DISC ) {
     148             :     /* Don't do anything with this */
     149         150 :     return;
     150         150 :   }
     151             : 
     152      255855 :   fd_flamenco_yaml_t * self = (fd_flamenco_yaml_t *)_self;
     153      255855 :   FILE *               file = self->file;
     154             : 
     155             :   /* On entry, there are either two cursor states:
     156             : 
     157             :      At the beginning of a line, if there is at least one predecessor
     158             :      in the current collection:
     159             : 
     160             :        ...
     161             :        object:
     162             :          - foo
     163             :          - bar
     164             :        $
     165             :        ...
     166             : 
     167             :      Or, at the beginning of the line, if we are serializing the first
     168             :      element.  We handle this as a special case, because we don't know
     169             :      whether the subsequent content can be printed inline, or needs a
     170             :      new line.
     171             : 
     172             :        ...
     173             :        object: $
     174             :        ...
     175             : 
     176             :      For example, an empty array is the following:
     177             : 
     178             :        ...
     179             :        object: []
     180             :        ...                                                            */
     181             : 
     182             :   /* Check if we are at the beginning of a collection */
     183      255855 :   if( (self->stack[ level ] & 1)==0 ) {
     184             : 
     185             :     /* Collection is empty -- print inline */
     186       49134 :     if( fd_flamenco_type_is_collection_end( type ) ) {
     187          12 :       if( name )
     188           6 :         fprintf( file, "%s: ", name );
     189             : 
     190          12 :       switch( type ) {
     191           6 :       case FD_FLAMENCO_TYPE_MAP_END:
     192           6 :       case FD_FLAMENCO_TYPE_ENUM_END:
     193           6 :         fprintf( file, "{}\n" );
     194           6 :         break;
     195           6 :       case FD_FLAMENCO_TYPE_ARR_END:
     196           6 :         fprintf( file, "[]\n" );
     197           6 :         break;
     198          12 :       }
     199             : 
     200          12 :       return;
     201          12 :     }
     202             : 
     203             :     /* Check if we should split off into a separate line */
     204       49122 :     int split = 0;
     205       49122 :     switch( self->stack[ level ] ) {
     206       48984 :     case STATE_OBJECT_BEGIN:
     207             :       /* Objects nested in objects go on a separate line:
     208             : 
     209             :            ...
     210             :            a:          <---
     211             :              b:        <---
     212             :                c: {}
     213             :            ...                                                        */
     214             : 
     215       48984 :       split = ( level>1 )
     216       48984 :            && ( ( (self->stack[ level-1 ])==STATE_OBJECT ) );
     217       48984 :       break;
     218          81 :     case STATE_ARRAY_BEGIN:
     219             :       /* Arrays nested in arrays or objects go on a separate line:
     220             : 
     221             :            ...              |      ...
     222             :            -         <---   |      a:          <---
     223             :              -       <---   |        - a: 3
     224             :                - []         |          b: 4
     225             :            ...              |      ...                                */
     226             : 
     227          81 :       split = ( level>1 )
     228          81 :            && ( ( (self->stack[ level-1 ])==STATE_OBJECT )
     229          72 :               | ( (self->stack[ level-1 ])==STATE_ARRAY  ) );
     230          81 :       break;
     231       49122 :     }
     232             : 
     233       49122 :     if( split ) {
     234       48420 :       fprintf( file, "\n" );
     235       48420 :       fwrite( self->indent, 2, (ulong)level-1, file );
     236       48420 :     }
     237             : 
     238      206721 :   } else {
     239             : 
     240             :     /* Nothing to do if collection ends, but at least one item printed */
     241             : 
     242      206721 :     if( fd_flamenco_type_is_collection_end( type ) )
     243       49065 :       return;
     244             : 
     245             :     /* We are at the beginning of a line.
     246             : 
     247             :        Indent according to current level.
     248             :        If just started an object or array, inhibit indent. */
     249             : 
     250      157656 :     long indent = (long)level-1L;
     251      157656 :     fwrite( self->indent, 2, (ulong)indent, file );
     252             : 
     253      157656 :   }
     254             : 
     255             :   /* Print node tag */
     256      206778 :   switch( self->stack[ level ] ) {
     257       48984 :   case STATE_OBJECT_BEGIN:
     258      205800 :   case STATE_OBJECT:
     259      205800 :     fprintf( file, "%s: ", name );
     260      205800 :     break;
     261             : 
     262          81 :   case STATE_ARRAY_BEGIN:
     263         921 :   case STATE_ARRAY:
     264         921 :     fprintf( file, "- " );
     265         921 :     break;
     266      206778 :   }
     267             : 
     268             :   /* Print node value */
     269      206778 :   switch( type ) {
     270       48840 :   case FD_FLAMENCO_TYPE_MAP:
     271       48990 :   case FD_FLAMENCO_TYPE_ENUM:
     272       48990 :     self->stack[ level+1 ] = STATE_OBJECT_BEGIN;
     273       48990 :     break;
     274          87 :   case FD_FLAMENCO_TYPE_ARR:
     275          87 :     self->stack[ level+1 ] = STATE_ARRAY_BEGIN;
     276          87 :     break;
     277             : 
     278           6 :   case FD_FLAMENCO_TYPE_NULL:
     279           6 :     fprintf( file, "null\n" );
     280           6 :     break;
     281       15993 :   case FD_FLAMENCO_TYPE_BOOL:
     282       15993 :     fprintf( file, "%s\n", (*(uchar const *)arg) ? "true" : "false" );
     283       15993 :     break;
     284       16170 :   case FD_FLAMENCO_TYPE_UCHAR:
     285       16170 :     fprintf( file, "%u\n", *(uchar const *)arg );
     286       16170 :     break;
     287           0 :   case FD_FLAMENCO_TYPE_SCHAR:
     288           0 :     fprintf( file, "%d\n", *(schar const *)arg );
     289           0 :     break;
     290         159 :   case FD_FLAMENCO_TYPE_USHORT:
     291         159 :     fprintf( file, "%u\n", *(ushort const *)arg );
     292         159 :     break;
     293           0 :   case FD_FLAMENCO_TYPE_SSHORT:
     294           0 :     fprintf( file, "%d\n", *(short const *)arg );
     295           0 :     break;
     296         141 :   case FD_FLAMENCO_TYPE_UINT:
     297         141 :     fprintf( file, "%u\n", *(uint const *)arg );
     298         141 :     break;
     299           0 :   case FD_FLAMENCO_TYPE_SINT:
     300           0 :     fprintf( file, "%d\n", *(int const *)arg );
     301           0 :     break;
     302       65472 :   case FD_FLAMENCO_TYPE_ULONG:
     303       65472 :     fprintf( file, "%lu\n", *(ulong const *)arg );
     304       65472 :     break;
     305       13242 :   case FD_FLAMENCO_TYPE_SLONG:
     306       13242 :     fprintf( file, "%ld\n", *(long const *)arg );
     307       13242 :     break;
     308           0 : # if FD_HAS_INT128
     309           0 :   case FD_FLAMENCO_TYPE_UINT128:
     310           0 :   case FD_FLAMENCO_TYPE_SINT128: {
     311           0 :     uint128 v = *(uint128 const *)arg;
     312           0 :     fprintf( file, "%s: 0x%016lx%016lx\n", name,
     313           0 :               (ulong)(v>>64), (ulong)v );
     314           0 :     break;
     315           0 :   }
     316           0 : # endif
     317           0 :   case FD_FLAMENCO_TYPE_FLOAT:
     318           0 :     fprintf( file, "%f\n", (double)( *(float const *)arg ) );
     319           0 :     break;
     320           0 :   case FD_FLAMENCO_TYPE_DOUBLE:
     321           0 :     fprintf( file, "%f\n", *(double const *)arg );
     322           0 :     break;
     323       46383 :   case FD_FLAMENCO_TYPE_HASH256: {
     324       46383 :     char buf[ FD_BASE58_ENCODED_32_SZ ];
     325       46383 :     fd_base58_encode_32( arg, NULL, buf );
     326       46383 :     fprintf( file, "'%s'\n", buf );
     327       46383 :     break;
     328           0 :   }
     329           0 :   case FD_FLAMENCO_TYPE_HASH1024:
     330           0 :     fprintf( file, "'%s%s%s%s'\n", FD_BASE58_ENC_32_ALLOCA( arg ), FD_BASE58_ENC_32_ALLOCA( ((uchar *) arg)+32 ), FD_BASE58_ENC_32_ALLOCA( ((uchar *) arg)+64 ), FD_BASE58_ENC_32_ALLOCA( ((uchar *) arg)+96 ) );
     331           0 :     break;
     332           3 :   case FD_FLAMENCO_TYPE_HASH16384:
     333             :     /* FIXME: This currently truncates the hash */
     334           3 :     fprintf( file, "'%s%s%s%s (truncated)'\n", FD_BASE58_ENC_32_ALLOCA( arg ), FD_BASE58_ENC_32_ALLOCA( ((uchar *) arg)+32 ), FD_BASE58_ENC_32_ALLOCA( ((uchar *) arg)+64 ), FD_BASE58_ENC_32_ALLOCA( ((uchar *) arg)+96 ) );
     335           0 :     break;
     336          39 :   case FD_FLAMENCO_TYPE_SIG512: {
     337          39 :     char buf[ FD_BASE58_ENCODED_64_SZ ];
     338          39 :     fd_base58_encode_64( arg, NULL, buf );
     339          39 :     fprintf( file, "'%s'\n", buf );
     340          39 :     break;
     341           9 :   }
     342          93 :   case FD_FLAMENCO_TYPE_CSTR:
     343          93 :     fprintf( file, "'%s'\n", (char const *)arg );
     344          93 :     break;
     345           0 :   case FD_FLAMENCO_TYPE_ENUM_DISC:
     346           0 :     break;
     347           0 :   default:
     348           0 :     FD_LOG_CRIT(( "unknown type %#x", (uint)type ));
     349           0 :     break;
     350      206778 :   }
     351             : 
     352             :   /* Remember that we processed an element in the current level */
     353      206778 :   self->stack[ level ] |= 1;
     354      206778 : }
     355             : 
     356             : 
     357             : // (gdb) call fd_vote_state_walk(fd_get_types_yaml(), self, fd_flamenco_yaml_walk, 0, 0U)
     358             : // (gdb) call fd_flush_yaml_dump()
     359             : 
     360             : static fd_flamenco_yaml_t * g_yaml = NULL;
     361             : 
     362             : fd_flamenco_yaml_t *
     363           0 : fd_get_types_yaml(void) {
     364           0 :   if (NULL != g_yaml)
     365           0 :     return g_yaml;
     366           0 :   g_yaml = fd_flamenco_yaml_init( fd_flamenco_yaml_new(malloc( fd_flamenco_yaml_footprint() ) ), stdout );
     367           0 :   return g_yaml;
     368           0 : }
     369             : 
     370             : void
     371           0 : fd_flush_yaml_dump(void) {
     372           0 :   if (NULL != g_yaml)
     373           0 :     fflush(g_yaml->file);
     374           0 : }

Generated by: LCOV version 1.14