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

Generated by: LCOV version 1.14