LCOV - code coverage report
Current view: top level - flamenco/types - fd_types_yaml.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 112 183 61.2 %
Date: 2025-07-01 05:00:49 Functions: 4 7 57.1 %

          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        3114 : #define STATE_OBJECT_BEGIN (2)  /* Writing object, ==0 elems so far */
      17        3732 : #define STATE_OBJECT       (3)  /* Writing object,  >0 elems so far */
      18         519 : #define STATE_ARRAY_BEGIN  (4)  /* Writing array,  ==0 elems so far */
      19        1428 : #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             : fd_flamenco_yaml_t *
      66          63 : fd_flamenco_yaml_new( void * mem ) {
      67             : 
      68          63 :   if( FD_UNLIKELY( !mem ) ) {
      69           0 :     FD_LOG_WARNING(( "NULL mem" ));
      70           0 :     return NULL;
      71           0 :   }
      72             : 
      73          63 :   fd_flamenco_yaml_t * yaml = (fd_flamenco_yaml_t *)mem;
      74          63 :   memset( yaml,         0,   sizeof(*yaml)        );
      75          63 :   memset( yaml->indent, ' ', sizeof(yaml->indent) );
      76          63 :   return (fd_flamenco_yaml_t *)mem;
      77          63 : }
      78             : 
      79             : void *
      80          24 : fd_flamenco_yaml_delete( fd_flamenco_yaml_t * yaml ) {
      81          24 :   return yaml;
      82          24 : }
      83             : 
      84             : fd_flamenco_yaml_t *
      85             : fd_flamenco_yaml_init( fd_flamenco_yaml_t * self,
      86          63 :                        void *               _file ) {
      87             : 
      88          63 :   if( FD_UNLIKELY( !self ) ) {
      89           0 :     FD_LOG_WARNING(( "NULL self" ));
      90           0 :     return NULL;
      91           0 :   }
      92          63 :   if( FD_UNLIKELY( !_file ) ) {
      93           0 :     FD_LOG_WARNING(( "NULL file" ));
      94           0 :     return NULL;
      95           0 :   }
      96             : 
      97          63 :   self->file = _file;
      98             : 
      99          63 :   return self;
     100          63 : }
     101             : 
     102             : void *
     103           0 : fd_flamenco_yaml_file( fd_flamenco_yaml_t * self ) {
     104           0 :   return self->file;
     105           0 : }
     106             : 
     107             : /* fd_flamenco_yaml_walk iteratively serializes YAML while keeping
     108             :    minimal state.
     109             : 
     110             :    Throughout this function, serialization state is illustrated using
     111             :    code comments.  The '$' symbol symbolizes the current stream cursor. */
     112             : 
     113             : void
     114             : fd_flamenco_yaml_walk( void *       _self,
     115             :                        void const * arg,
     116             :                        char const * name,
     117             :                        int          type,
     118             :                        char const * type_name,
     119        5283 :                        uint         level ) {
     120        5283 :   (void)type_name;
     121             : 
     122        5283 :   if( level>=FD_FLAMENCO_YAML_MAX_INDENT-1 ) {
     123           0 :     FD_LOG_WARNING(( "indent level %u exceeds max %lu",
     124           0 :                      level, FD_FLAMENCO_YAML_MAX_INDENT ));
     125           0 :     return;
     126           0 :   }
     127             : 
     128        5283 :   if( type == FD_FLAMENCO_TYPE_ENUM_DISC ) {
     129             :     /* Don't do anything with this */
     130         159 :     return;
     131         159 :   }
     132             : 
     133        5124 :   fd_flamenco_yaml_t * self = (fd_flamenco_yaml_t *)_self;
     134        5124 :   FILE *               file = self->file;
     135             : 
     136             :   /* On entry, there are either two cursor states:
     137             : 
     138             :      At the beginning of a line, if there is at least one predecessor
     139             :      in the current collection:
     140             : 
     141             :        ...
     142             :        object:
     143             :          - foo
     144             :          - bar
     145             :        $
     146             :        ...
     147             : 
     148             :      Or, at the beginning of the line, if we are serializing the first
     149             :      element.  We handle this as a special case, because we don't know
     150             :      whether the subsequent content can be printed inline, or needs a
     151             :      new line.
     152             : 
     153             :        ...
     154             :        object: $
     155             :        ...
     156             : 
     157             :      For example, an empty array is the following:
     158             : 
     159             :        ...
     160             :        object: []
     161             :        ...                                                            */
     162             : 
     163             :   /* Check if we are at the beginning of a collection */
     164        5124 :   if( (self->stack[ level ] & 1)==0 ) {
     165             : 
     166             :     /* Collection is empty -- print inline */
     167        1278 :     if( fd_flamenco_type_is_collection_end( type ) ) {
     168           6 :       if( name )
     169           0 :         fprintf( file, "%s: ", name );
     170             : 
     171           6 :       switch( type ) {
     172           0 :       case FD_FLAMENCO_TYPE_MAP_END:
     173           0 :       case FD_FLAMENCO_TYPE_ENUM_END:
     174           0 :         fprintf( file, "{}\n" );
     175           0 :         break;
     176           6 :       case FD_FLAMENCO_TYPE_ARR_END:
     177           6 :         fprintf( file, "[]\n" );
     178           6 :         break;
     179           6 :       }
     180             : 
     181           6 :       return;
     182           6 :     }
     183             : 
     184             :     /* Check if we should split off into a separate line */
     185        1272 :     int split = 0;
     186        1272 :     switch( self->stack[ level ] ) {
     187        1038 :     case STATE_OBJECT_BEGIN:
     188             :       /* Objects nested in objects go on a separate line:
     189             : 
     190             :            ...
     191             :            a:          <---
     192             :              b:        <---
     193             :                c: {}
     194             :            ...                                                        */
     195             : 
     196        1038 :       split = ( level>1 )
     197        1038 :            && ( ( (self->stack[ level-1 ])==STATE_OBJECT ) );
     198        1038 :       break;
     199         171 :     case STATE_ARRAY_BEGIN:
     200             :       /* Arrays nested in arrays or objects go on a separate line:
     201             : 
     202             :            ...              |      ...
     203             :            -         <---   |      a:          <---
     204             :              -       <---   |        - a: 3
     205             :                - []         |          b: 4
     206             :            ...              |      ...                                */
     207             : 
     208         171 :       split = ( level>1 )
     209         171 :            && ( ( (self->stack[ level-1 ])==STATE_OBJECT )
     210         162 :               | ( (self->stack[ level-1 ])==STATE_ARRAY  ) );
     211         171 :       break;
     212        1272 :     }
     213             : 
     214        1272 :     if( split ) {
     215         585 :       fprintf( file, "\n" );
     216         585 :       fwrite( self->indent, 2, (ulong)level-1, file );
     217         585 :     }
     218             : 
     219        3846 :   } else {
     220             : 
     221             :     /* Nothing to do if collection ends, but at least one item printed */
     222             : 
     223        3846 :     if( fd_flamenco_type_is_collection_end( type ) )
     224        1209 :       return;
     225             : 
     226             :     /* We are at the beginning of a line.
     227             : 
     228             :        Indent according to current level.
     229             :        If just started an object or array, inhibit indent. */
     230             : 
     231        2637 :     long indent = (long)level-1L;
     232        2637 :     fwrite( self->indent, 2, (ulong)indent, file );
     233             : 
     234        2637 :   }
     235             : 
     236             :   /* Print node tag */
     237        3909 :   switch( self->stack[ level ] ) {
     238        1038 :   case STATE_OBJECT_BEGIN:
     239        2580 :   case STATE_OBJECT:
     240        2580 :     fprintf( file, "%s: ", name );
     241        2580 :     break;
     242             : 
     243         171 :   case STATE_ARRAY_BEGIN:
     244        1266 :   case STATE_ARRAY:
     245        1266 :     fprintf( file, "- " );
     246        1266 :     break;
     247        3909 :   }
     248             : 
     249             :   /* Print node value */
     250        3909 :   switch( type ) {
     251         879 :   case FD_FLAMENCO_TYPE_MAP:
     252        1038 :   case FD_FLAMENCO_TYPE_ENUM:
     253        1038 :     self->stack[ level+1 ] = STATE_OBJECT_BEGIN;
     254        1038 :     break;
     255         177 :   case FD_FLAMENCO_TYPE_ARR:
     256         177 :     self->stack[ level+1 ] = STATE_ARRAY_BEGIN;
     257         177 :     break;
     258             : 
     259           6 :   case FD_FLAMENCO_TYPE_NULL:
     260           6 :     fprintf( file, "null\n" );
     261           6 :     break;
     262           6 :   case FD_FLAMENCO_TYPE_BOOL:
     263           6 :     fprintf( file, "%s\n", (*(uchar const *)arg) ? "true" : "false" );
     264           6 :     break;
     265         552 :   case FD_FLAMENCO_TYPE_UCHAR:
     266         552 :     fprintf( file, "%u\n", *(uchar const *)arg );
     267         552 :     break;
     268           0 :   case FD_FLAMENCO_TYPE_SCHAR:
     269           0 :     fprintf( file, "%d\n", *(schar const *)arg );
     270           0 :     break;
     271         168 :   case FD_FLAMENCO_TYPE_USHORT:
     272         168 :     fprintf( file, "%u\n", *(ushort const *)arg );
     273         168 :     break;
     274           0 :   case FD_FLAMENCO_TYPE_SSHORT:
     275           0 :     fprintf( file, "%d\n", *(short const *)arg );
     276           0 :     break;
     277         147 :   case FD_FLAMENCO_TYPE_UINT:
     278         147 :     fprintf( file, "%u\n", *(uint const *)arg );
     279         147 :     break;
     280           0 :   case FD_FLAMENCO_TYPE_SINT:
     281           0 :     fprintf( file, "%d\n", *(int const *)arg );
     282           0 :     break;
     283        1482 :   case FD_FLAMENCO_TYPE_ULONG:
     284        1482 :     fprintf( file, "%lu\n", *(ulong const *)arg );
     285        1482 :     break;
     286           6 :   case FD_FLAMENCO_TYPE_SLONG:
     287           6 :     fprintf( file, "%ld\n", *(long const *)arg );
     288           6 :     break;
     289           0 : # if FD_HAS_INT128
     290           0 :   case FD_FLAMENCO_TYPE_UINT128:
     291           0 :   case FD_FLAMENCO_TYPE_SINT128: {
     292           0 :     uint128 v = *(uint128 const *)arg;
     293           0 :     fprintf( file, "%s: 0x%016lx%016lx\n", name,
     294           0 :               (ulong)(v>>64), (ulong)v );
     295           0 :     break;
     296           0 :   }
     297           0 : # endif
     298           0 :   case FD_FLAMENCO_TYPE_FLOAT:
     299           0 :     fprintf( file, "%f\n", (double)( *(float const *)arg ) );
     300           0 :     break;
     301           0 :   case FD_FLAMENCO_TYPE_DOUBLE:
     302           0 :     fprintf( file, "%f\n", *(double const *)arg );
     303           0 :     break;
     304         279 :   case FD_FLAMENCO_TYPE_HASH256: {
     305         279 :     char buf[ FD_BASE58_ENCODED_32_SZ ];
     306         279 :     fd_base58_encode_32( arg, NULL, buf );
     307         279 :     fprintf( file, "'%s'\n", buf );
     308         279 :     break;
     309           0 :   }
     310           0 :   case FD_FLAMENCO_TYPE_HASH1024:
     311           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 ) );
     312           0 :     break;
     313           0 :   case FD_FLAMENCO_TYPE_HASH16384:
     314             :     /* FIXME: This currently truncates the hash */
     315           0 :     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 ) );
     316           0 :     break;
     317          48 :   case FD_FLAMENCO_TYPE_SIG512: {
     318          48 :     char buf[ FD_BASE58_ENCODED_64_SZ ];
     319          48 :     fd_base58_encode_64( arg, NULL, buf );
     320          48 :     fprintf( file, "'%s'\n", buf );
     321          48 :     break;
     322           0 :   }
     323           0 :   case FD_FLAMENCO_TYPE_CSTR:
     324           0 :     fprintf( file, "'%s'\n", (char const *)arg );
     325           0 :     break;
     326           0 :   case FD_FLAMENCO_TYPE_ENUM_DISC:
     327           0 :     break;
     328           0 :   default:
     329           0 :     FD_LOG_CRIT(( "unknown type %#x", (uint)type ));
     330           0 :     break;
     331        3909 :   }
     332             : 
     333             :   /* Remember that we processed an element in the current level */
     334        3909 :   self->stack[ level ] |= 1;
     335        3909 : }
     336             : 
     337             : static fd_flamenco_yaml_t * g_yaml = NULL;
     338             : 
     339             : fd_flamenco_yaml_t *
     340           0 : fd_get_types_yaml(void) {
     341           0 :   if (NULL != g_yaml)
     342           0 :     return g_yaml;
     343           0 :   g_yaml = fd_flamenco_yaml_init( fd_flamenco_yaml_new(malloc( fd_flamenco_yaml_footprint() ) ), stdout );
     344           0 :   return g_yaml;
     345           0 : }
     346             : 
     347             : void
     348           0 : fd_flush_yaml_dump(void) {
     349           0 :   if (NULL != g_yaml)
     350           0 :     fflush(g_yaml->file);
     351           0 : }

Generated by: LCOV version 1.14