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