LCOV - code coverage report
Current view: top level - flamenco/types - fd_types_yaml.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 121 186 65.1 %
Date: 2024-11-13 11:58:15 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        1584 : #define STATE_OBJECT_BEGIN (2)  /* Writing object, ==0 elems so far */
      17        1773 : #define STATE_OBJECT       (3)  /* Writing object,  >0 elems so far */
      18         213 : #define STATE_ARRAY_BEGIN  (4)  /* Writing array,  ==0 elems so far */
      19         573 : #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          51 : fd_flamenco_yaml_align( void ) {
      75          51 :   return alignof(fd_flamenco_yaml_t);
      76          51 : }
      77             : 
      78             : ulong
      79          51 : fd_flamenco_yaml_footprint( void ) {
      80          51 :   return sizeof(fd_flamenco_yaml_t);
      81          51 : }
      82             : 
      83             : fd_flamenco_yaml_t *
      84          51 : fd_flamenco_yaml_new( void * mem ) {
      85             : 
      86          51 :   if( FD_UNLIKELY( !mem ) ) {
      87           0 :     FD_LOG_WARNING(( "NULL mem" ));
      88           0 :     return NULL;
      89           0 :   }
      90             : 
      91          51 :   fd_flamenco_yaml_t * yaml = (fd_flamenco_yaml_t *)mem;
      92          51 :   memset( yaml,         0,   sizeof(*yaml)        );
      93          51 :   memset( yaml->indent, ' ', sizeof(yaml->indent) );
      94          51 :   return (fd_flamenco_yaml_t *)mem;
      95          51 : }
      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          51 :                        void *               _file ) {
     105             : 
     106          51 :   if( FD_UNLIKELY( !self ) ) {
     107           0 :     FD_LOG_WARNING(( "NULL self" ));
     108           0 :     return NULL;
     109           0 :   }
     110          51 :   if( FD_UNLIKELY( !_file ) ) {
     111           0 :     FD_LOG_WARNING(( "NULL file" ));
     112           0 :     return NULL;
     113           0 :   }
     114             : 
     115          51 :   self->file = _file;
     116             : 
     117          51 :   return self;
     118          51 : }
     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        2535 :                        uint         level ) {
     138             : 
     139        2535 :   (void)type_name;
     140             : 
     141        2535 :   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        2535 :   if( type == FD_FLAMENCO_TYPE_ENUM_DISC ) {
     148             :     /* Don't do anything with this */
     149         147 :     return;
     150         147 :   }
     151             : 
     152        2388 :   fd_flamenco_yaml_t * self = (fd_flamenco_yaml_t *)_self;
     153        2388 :   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        2388 :   if( (self->stack[ level ] & 1)==0 ) {
     184             : 
     185             :     /* Collection is empty -- print inline */
     186         654 :     if( fd_flamenco_type_is_collection_end( type ) ) {
     187           6 :       if( name )
     188           0 :         fprintf( file, "%s: ", name );
     189             : 
     190           6 :       switch( type ) {
     191           0 :       case FD_FLAMENCO_TYPE_MAP_END:
     192           0 :       case FD_FLAMENCO_TYPE_ENUM_END:
     193           0 :         fprintf( file, "{}\n" );
     194           0 :         break;
     195           6 :       case FD_FLAMENCO_TYPE_ARR_END:
     196           6 :         fprintf( file, "[]\n" );
     197           6 :         break;
     198           6 :       }
     199             : 
     200           6 :       return;
     201           6 :     }
     202             : 
     203             :     /* Check if we should split off into a separate line */
     204         648 :     int split = 0;
     205         648 :     switch( self->stack[ level ] ) {
     206         528 :     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         528 :       split = ( level>1 )
     216         528 :            && ( ( (self->stack[ level-1 ])==STATE_OBJECT ) );
     217         528 :       break;
     218          69 :     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          69 :       split = ( level>1 )
     228          69 :            && ( ( (self->stack[ level-1 ])==STATE_OBJECT )
     229          60 :               | ( (self->stack[ level-1 ])==STATE_ARRAY  ) );
     230          69 :       break;
     231         648 :     }
     232             : 
     233         648 :     if( split ) {
     234         366 :       fprintf( file, "\n" );
     235         366 :       fwrite( self->indent, 2, (ulong)level-1, file );
     236         366 :     }
     237             : 
     238        1734 :   } else {
     239             : 
     240             :     /* Nothing to do if collection ends, but at least one item printed */
     241             : 
     242        1734 :     if( fd_flamenco_type_is_collection_end( type ) )
     243         597 :       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        1137 :     long indent = (long)level-1L;
     251        1137 :     fwrite( self->indent, 2, (ulong)indent, file );
     252             : 
     253        1137 :   }
     254             : 
     255             :   /* Print node tag */
     256        1785 :   switch( self->stack[ level ] ) {
     257         528 :   case STATE_OBJECT_BEGIN:
     258        1221 :   case STATE_OBJECT:
     259        1221 :     fprintf( file, "%s: ", name );
     260        1221 :     break;
     261             : 
     262          69 :   case STATE_ARRAY_BEGIN:
     263         513 :   case STATE_ARRAY:
     264         513 :     fprintf( file, "- " );
     265         513 :     break;
     266        1785 :   }
     267             : 
     268             :   /* Print node value */
     269        1785 :   switch( type ) {
     270         381 :   case FD_FLAMENCO_TYPE_MAP:
     271         528 :   case FD_FLAMENCO_TYPE_ENUM:
     272         528 :     self->stack[ level+1 ] = STATE_OBJECT_BEGIN;
     273         528 :     break;
     274          75 :   case FD_FLAMENCO_TYPE_ARR:
     275          75 :     self->stack[ level+1 ] = STATE_ARRAY_BEGIN;
     276          75 :     break;
     277             : 
     278           6 :   case FD_FLAMENCO_TYPE_NULL:
     279           6 :     fprintf( file, "null\n" );
     280           6 :     break;
     281           3 :   case FD_FLAMENCO_TYPE_BOOL:
     282           3 :     fprintf( file, "%s\n", (*(uchar const *)arg) ? "true" : "false" );
     283           3 :     break;
     284          84 :   case FD_FLAMENCO_TYPE_UCHAR:
     285          84 :     fprintf( file, "%u\n", *(uchar const *)arg );
     286          84 :     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          48 :   case FD_FLAMENCO_TYPE_UINT:
     297          48 :     fprintf( file, "%u\n", *(uint const *)arg );
     298          48 :     break;
     299           0 :   case FD_FLAMENCO_TYPE_SINT:
     300           0 :     fprintf( file, "%d\n", *(int const *)arg );
     301           0 :     break;
     302         585 :   case FD_FLAMENCO_TYPE_ULONG:
     303         585 :     fprintf( file, "%lu\n", *(ulong const *)arg );
     304         585 :     break;
     305           9 :   case FD_FLAMENCO_TYPE_SLONG:
     306           9 :     fprintf( file, "%ld\n", *(long const *)arg );
     307           9 :     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         156 :   case FD_FLAMENCO_TYPE_HASH256: {
     324         156 :     char buf[ FD_BASE58_ENCODED_32_SZ ];
     325         156 :     fd_base58_encode_32( arg, NULL, buf );
     326         156 :     fprintf( file, "'%s'\n", buf );
     327         156 :     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          39 :   case FD_FLAMENCO_TYPE_SIG512: {
     333          39 :     char buf[ FD_BASE58_ENCODED_64_SZ ];
     334          39 :     fd_base58_encode_64( arg, NULL, buf );
     335          39 :     fprintf( file, "'%s'\n", buf );
     336          39 :     break;
     337           0 :   }
     338          93 :   case FD_FLAMENCO_TYPE_CSTR:
     339          93 :     fprintf( file, "'%s'\n", (char const *)arg );
     340          93 :     break;
     341           0 :   case FD_FLAMENCO_TYPE_ENUM_DISC:
     342           0 :     break;
     343           0 :   default:
     344           0 :     FD_LOG_CRIT(( "unknown type %#x", (uint)type ));
     345           0 :     break;
     346        1785 :   }
     347             : 
     348             :   /* Remember that we processed an element in the current level */
     349        1785 :   self->stack[ level ] |= 1;
     350        1785 : }
     351             : 
     352             : 
     353             : // (gdb) call fd_vote_state_walk(fd_get_types_yaml(), self, fd_flamenco_yaml_walk, 0, 0U)
     354             : // (gdb) call fd_flush_yaml_dump()
     355             : 
     356             : static fd_flamenco_yaml_t * g_yaml = NULL;
     357             : 
     358             : fd_flamenco_yaml_t *
     359           0 : fd_get_types_yaml(void) {
     360           0 :   if (NULL != g_yaml)
     361           0 :     return g_yaml;
     362           0 :   g_yaml = fd_flamenco_yaml_init( fd_flamenco_yaml_new(malloc( fd_flamenco_yaml_footprint() ) ), stdout );
     363           0 :   return g_yaml;
     364           0 : }
     365             : 
     366             : void
     367           0 : fd_flush_yaml_dump(void) {
     368           0 :   if (NULL != g_yaml)
     369           0 :     fflush(g_yaml->file);
     370           0 : }

Generated by: LCOV version 1.14