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 : }
|