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