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

Generated by: LCOV version 1.14