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