]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/jemalloc/include/jemalloc/internal/emitter.h
MFV r345988:
[FreeBSD/FreeBSD.git] / contrib / jemalloc / include / jemalloc / internal / emitter.h
1 #ifndef JEMALLOC_INTERNAL_EMITTER_H
2 #define JEMALLOC_INTERNAL_EMITTER_H
3
4 #include "jemalloc/internal/ql.h"
5
6 typedef enum emitter_output_e emitter_output_t;
7 enum emitter_output_e {
8         emitter_output_json,
9         emitter_output_table
10 };
11
12 typedef enum emitter_justify_e emitter_justify_t;
13 enum emitter_justify_e {
14         emitter_justify_left,
15         emitter_justify_right,
16         /* Not for users; just to pass to internal functions. */
17         emitter_justify_none
18 };
19
20 typedef enum emitter_type_e emitter_type_t;
21 enum emitter_type_e {
22         emitter_type_bool,
23         emitter_type_int,
24         emitter_type_unsigned,
25         emitter_type_uint32,
26         emitter_type_uint64,
27         emitter_type_size,
28         emitter_type_ssize,
29         emitter_type_string,
30         /*
31          * A title is a column title in a table; it's just a string, but it's
32          * not quoted.
33          */
34         emitter_type_title,
35 };
36
37 typedef struct emitter_col_s emitter_col_t;
38 struct emitter_col_s {
39         /* Filled in by the user. */
40         emitter_justify_t justify;
41         int width;
42         emitter_type_t type;
43         union {
44                 bool bool_val;
45                 int int_val;
46                 unsigned unsigned_val;
47                 uint32_t uint32_val;
48                 uint64_t uint64_val;
49                 size_t size_val;
50                 ssize_t ssize_val;
51                 const char *str_val;
52         };
53
54         /* Filled in by initialization. */
55         ql_elm(emitter_col_t) link;
56 };
57
58 typedef struct emitter_row_s emitter_row_t;
59 struct emitter_row_s {
60         ql_head(emitter_col_t) cols;
61 };
62
63 static inline void
64 emitter_row_init(emitter_row_t *row) {
65         ql_new(&row->cols);
66 }
67
68 static inline void
69 emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
70         ql_elm_new(col, link);
71         ql_tail_insert(&row->cols, col, link);
72 }
73
74 typedef struct emitter_s emitter_t;
75 struct emitter_s {
76         emitter_output_t output;
77         /* The output information. */
78         void (*write_cb)(void *, const char *);
79         void *cbopaque;
80         int nesting_depth;
81         /* True if we've already emitted a value at the given depth. */
82         bool item_at_depth;
83 };
84
85 static inline void
86 emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
87     void (*write_cb)(void *, const char *), void *cbopaque) {
88         emitter->output = emitter_output;
89         emitter->write_cb = write_cb;
90         emitter->cbopaque = cbopaque;
91         emitter->item_at_depth = false;
92         emitter->nesting_depth = 0;
93 }
94
95 /* Internal convenience function.  Write to the emitter the given string. */
96 JEMALLOC_FORMAT_PRINTF(2, 3)
97 static inline void
98 emitter_printf(emitter_t *emitter, const char *format, ...) {
99         va_list ap;
100
101         va_start(ap, format);
102         malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
103         va_end(ap);
104 }
105
106 /* Write to the emitter the given string, but only in table mode. */
107 JEMALLOC_FORMAT_PRINTF(2, 3)
108 static inline void
109 emitter_table_printf(emitter_t *emitter, const char *format, ...) {
110         if (emitter->output == emitter_output_table) {
111                 va_list ap;
112                 va_start(ap, format);
113                 malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
114                 va_end(ap);
115         }
116 }
117
118 static inline void
119 emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
120     emitter_justify_t justify, int width) {
121         size_t written;
122         if (justify == emitter_justify_none) {
123                 written = malloc_snprintf(out_fmt, out_size,
124                     "%%%s", fmt_specifier);
125         } else if (justify == emitter_justify_left) {
126                 written = malloc_snprintf(out_fmt, out_size,
127                     "%%-%d%s", width, fmt_specifier);
128         } else {
129                 written = malloc_snprintf(out_fmt, out_size,
130                     "%%%d%s", width, fmt_specifier);
131         }
132         /* Only happens in case of bad format string, which *we* choose. */
133         assert(written <  out_size);
134 }
135
136 /*
137  * Internal.  Emit the given value type in the relevant encoding (so that the
138  * bool true gets mapped to json "true", but the string "true" gets mapped to
139  * json "\"true\"", for instance.
140  *
141  * Width is ignored if justify is emitter_justify_none.
142  */
143 static inline void
144 emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
145     emitter_type_t value_type, const void *value) {
146         size_t str_written;
147 #define BUF_SIZE 256
148 #define FMT_SIZE 10
149         /*
150          * We dynamically generate a format string to emit, to let us use the
151          * snprintf machinery.  This is kinda hacky, but gets the job done
152          * quickly without having to think about the various snprintf edge
153          * cases.
154          */
155         char fmt[FMT_SIZE];
156         char buf[BUF_SIZE];
157
158 #define EMIT_SIMPLE(type, format)                                       \
159         emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width);         \
160         emitter_printf(emitter, fmt, *(const type *)value);             \
161
162         switch (value_type) {
163         case emitter_type_bool:
164                 emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
165                 emitter_printf(emitter, fmt, *(const bool *)value ?
166                     "true" : "false");
167                 break;
168         case emitter_type_int:
169                 EMIT_SIMPLE(int, "d")
170                 break;
171         case emitter_type_unsigned:
172                 EMIT_SIMPLE(unsigned, "u")
173                 break;
174         case emitter_type_ssize:
175                 EMIT_SIMPLE(ssize_t, "zd")
176                 break;
177         case emitter_type_size:
178                 EMIT_SIMPLE(size_t, "zu")
179                 break;
180         case emitter_type_string:
181                 str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
182                     *(const char *const *)value);
183                 /*
184                  * We control the strings we output; we shouldn't get anything
185                  * anywhere near the fmt size.
186                  */
187                 assert(str_written < BUF_SIZE);
188                 emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
189                 emitter_printf(emitter, fmt, buf);
190                 break;
191         case emitter_type_uint32:
192                 EMIT_SIMPLE(uint32_t, FMTu32)
193                 break;
194         case emitter_type_uint64:
195                 EMIT_SIMPLE(uint64_t, FMTu64)
196                 break;
197         case emitter_type_title:
198                 EMIT_SIMPLE(char *const, "s");
199                 break;
200         default:
201                 unreachable();
202         }
203 #undef BUF_SIZE
204 #undef FMT_SIZE
205 }
206
207
208 /* Internal functions.  In json mode, tracks nesting state. */
209 static inline void
210 emitter_nest_inc(emitter_t *emitter) {
211         emitter->nesting_depth++;
212         emitter->item_at_depth = false;
213 }
214
215 static inline void
216 emitter_nest_dec(emitter_t *emitter) {
217         emitter->nesting_depth--;
218         emitter->item_at_depth = true;
219 }
220
221 static inline void
222 emitter_indent(emitter_t *emitter) {
223         int amount = emitter->nesting_depth;
224         const char *indent_str;
225         if (emitter->output == emitter_output_json) {
226                 indent_str = "\t";
227         } else {
228                 amount *= 2;
229                 indent_str = " ";
230         }
231         for (int i = 0; i < amount; i++) {
232                 emitter_printf(emitter, "%s", indent_str);
233         }
234 }
235
236 static inline void
237 emitter_json_key_prefix(emitter_t *emitter) {
238         emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
239         emitter_indent(emitter);
240 }
241
242 static inline void
243 emitter_begin(emitter_t *emitter) {
244         if (emitter->output == emitter_output_json) {
245                 assert(emitter->nesting_depth == 0);
246                 emitter_printf(emitter, "{");
247                 emitter_nest_inc(emitter);
248         } else {
249                 // tabular init
250                 emitter_printf(emitter, "%s", "");
251         }
252 }
253
254 static inline void
255 emitter_end(emitter_t *emitter) {
256         if (emitter->output == emitter_output_json) {
257                 assert(emitter->nesting_depth == 1);
258                 emitter_nest_dec(emitter);
259                 emitter_printf(emitter, "\n}\n");
260         }
261 }
262
263 /*
264  * Note emits a different kv pair as well, but only in table mode.  Omits the
265  * note if table_note_key is NULL.
266  */
267 static inline void
268 emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
269     emitter_type_t value_type, const void *value,
270     const char *table_note_key, emitter_type_t table_note_value_type,
271     const void *table_note_value) {
272         if (emitter->output == emitter_output_json) {
273                 assert(emitter->nesting_depth > 0);
274                 emitter_json_key_prefix(emitter);
275                 emitter_printf(emitter, "\"%s\": ", json_key);
276                 emitter_print_value(emitter, emitter_justify_none, -1,
277                     value_type, value);
278         } else {
279                 emitter_indent(emitter);
280                 emitter_printf(emitter, "%s: ", table_key);
281                 emitter_print_value(emitter, emitter_justify_none, -1,
282                     value_type, value);
283                 if (table_note_key != NULL) {
284                         emitter_printf(emitter, " (%s: ", table_note_key);
285                         emitter_print_value(emitter, emitter_justify_none, -1,
286                             table_note_value_type, table_note_value);
287                         emitter_printf(emitter, ")");
288                 }
289                 emitter_printf(emitter, "\n");
290         }
291         emitter->item_at_depth = true;
292 }
293
294 static inline void
295 emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
296     emitter_type_t value_type, const void *value) {
297         emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
298             emitter_type_bool, NULL);
299 }
300
301 static inline void
302 emitter_json_kv(emitter_t *emitter, const char *json_key,
303     emitter_type_t value_type, const void *value) {
304         if (emitter->output == emitter_output_json) {
305                 emitter_kv(emitter, json_key, NULL, value_type, value);
306         }
307 }
308
309 static inline void
310 emitter_table_kv(emitter_t *emitter, const char *table_key,
311     emitter_type_t value_type, const void *value) {
312         if (emitter->output == emitter_output_table) {
313                 emitter_kv(emitter, NULL, table_key, value_type, value);
314         }
315 }
316
317 static inline void
318 emitter_dict_begin(emitter_t *emitter, const char *json_key,
319     const char *table_header) {
320         if (emitter->output == emitter_output_json) {
321                 emitter_json_key_prefix(emitter);
322                 emitter_printf(emitter, "\"%s\": {", json_key);
323                 emitter_nest_inc(emitter);
324         } else {
325                 emitter_indent(emitter);
326                 emitter_printf(emitter, "%s\n", table_header);
327                 emitter_nest_inc(emitter);
328         }
329 }
330
331 static inline void
332 emitter_dict_end(emitter_t *emitter) {
333         if (emitter->output == emitter_output_json) {
334                 assert(emitter->nesting_depth > 0);
335                 emitter_nest_dec(emitter);
336                 emitter_printf(emitter, "\n");
337                 emitter_indent(emitter);
338                 emitter_printf(emitter, "}");
339         } else {
340                 emitter_nest_dec(emitter);
341         }
342 }
343
344 static inline void
345 emitter_json_dict_begin(emitter_t *emitter, const char *json_key) {
346         if (emitter->output == emitter_output_json) {
347                 emitter_dict_begin(emitter, json_key, NULL);
348         }
349 }
350
351 static inline void
352 emitter_json_dict_end(emitter_t *emitter) {
353         if (emitter->output == emitter_output_json) {
354                 emitter_dict_end(emitter);
355         }
356 }
357
358 static inline void
359 emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
360         if (emitter->output == emitter_output_table) {
361                 emitter_dict_begin(emitter, NULL, table_key);
362         }
363 }
364
365 static inline void
366 emitter_table_dict_end(emitter_t *emitter) {
367         if (emitter->output == emitter_output_table) {
368                 emitter_dict_end(emitter);
369         }
370 }
371
372 static inline void
373 emitter_json_arr_begin(emitter_t *emitter, const char *json_key) {
374         if (emitter->output == emitter_output_json) {
375                 emitter_json_key_prefix(emitter);
376                 emitter_printf(emitter, "\"%s\": [", json_key);
377                 emitter_nest_inc(emitter);
378         }
379 }
380
381 static inline void
382 emitter_json_arr_end(emitter_t *emitter) {
383         if (emitter->output == emitter_output_json) {
384                 assert(emitter->nesting_depth > 0);
385                 emitter_nest_dec(emitter);
386                 emitter_printf(emitter, "\n");
387                 emitter_indent(emitter);
388                 emitter_printf(emitter, "]");
389         }
390 }
391
392 static inline void
393 emitter_json_arr_obj_begin(emitter_t *emitter) {
394         if (emitter->output == emitter_output_json) {
395                 emitter_json_key_prefix(emitter);
396                 emitter_printf(emitter, "{");
397                 emitter_nest_inc(emitter);
398         }
399 }
400
401 static inline void
402 emitter_json_arr_obj_end(emitter_t *emitter) {
403         if (emitter->output == emitter_output_json) {
404                 assert(emitter->nesting_depth > 0);
405                 emitter_nest_dec(emitter);
406                 emitter_printf(emitter, "\n");
407                 emitter_indent(emitter);
408                 emitter_printf(emitter, "}");
409         }
410 }
411
412 static inline void
413 emitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,
414     const void *value) {
415         if (emitter->output == emitter_output_json) {
416                 emitter_json_key_prefix(emitter);
417                 emitter_print_value(emitter, emitter_justify_none, -1,
418                     value_type, value);
419         }
420 }
421
422 static inline void
423 emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
424         if (emitter->output != emitter_output_table) {
425                 return;
426         }
427         emitter_col_t *col;
428         ql_foreach(col, &row->cols, link) {
429                 emitter_print_value(emitter, col->justify, col->width,
430                     col->type, (const void *)&col->bool_val);
431         }
432         emitter_table_printf(emitter, "\n");
433 }
434
435 #endif /* JEMALLOC_INTERNAL_EMITTER_H */