1#ifndef JEMALLOC_INTERNAL_EMITTER_H
2#define JEMALLOC_INTERNAL_EMITTER_H
3
4#include "jemalloc/internal/ql.h"
5
6typedef enum emitter_output_e emitter_output_t;
7enum emitter_output_e {
8	emitter_output_json,
9	emitter_output_table
10};
11
12typedef enum emitter_justify_e emitter_justify_t;
13enum 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
20typedef enum emitter_type_e emitter_type_t;
21enum 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
37typedef struct emitter_col_s emitter_col_t;
38struct 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		uint32_t uint32_t_val;
49		uint64_t uint64_val;
50		uint64_t uint64_t_val;
51		size_t size_val;
52		ssize_t ssize_val;
53		const char *str_val;
54	};
55
56	/* Filled in by initialization. */
57	ql_elm(emitter_col_t) link;
58};
59
60typedef struct emitter_row_s emitter_row_t;
61struct emitter_row_s {
62	ql_head(emitter_col_t) cols;
63};
64
65typedef struct emitter_s emitter_t;
66struct emitter_s {
67	emitter_output_t output;
68	/* The output information. */
69	void (*write_cb)(void *, const char *);
70	void *cbopaque;
71	int nesting_depth;
72	/* True if we've already emitted a value at the given depth. */
73	bool item_at_depth;
74	/* True if we emitted a key and will emit corresponding value next. */
75	bool emitted_key;
76};
77
78/* Internal convenience function.  Write to the emitter the given string. */
79JEMALLOC_FORMAT_PRINTF(2, 3)
80static inline void
81emitter_printf(emitter_t *emitter, const char *format, ...) {
82	va_list ap;
83
84	va_start(ap, format);
85	malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
86	va_end(ap);
87}
88
89static inline const char * JEMALLOC_FORMAT_ARG(3)
90emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
91    emitter_justify_t justify, int width) {
92	size_t written;
93	fmt_specifier++;
94	if (justify == emitter_justify_none) {
95		written = malloc_snprintf(out_fmt, out_size,
96		    "%%%s", fmt_specifier);
97	} else if (justify == emitter_justify_left) {
98		written = malloc_snprintf(out_fmt, out_size,
99		    "%%-%d%s", width, fmt_specifier);
100	} else {
101		written = malloc_snprintf(out_fmt, out_size,
102		    "%%%d%s", width, fmt_specifier);
103	}
104	/* Only happens in case of bad format string, which *we* choose. */
105	assert(written <  out_size);
106	return out_fmt;
107}
108
109/*
110 * Internal.  Emit the given value type in the relevant encoding (so that the
111 * bool true gets mapped to json "true", but the string "true" gets mapped to
112 * json "\"true\"", for instance.
113 *
114 * Width is ignored if justify is emitter_justify_none.
115 */
116static inline void
117emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
118    emitter_type_t value_type, const void *value) {
119	size_t str_written;
120#define BUF_SIZE 256
121#define FMT_SIZE 10
122	/*
123	 * We dynamically generate a format string to emit, to let us use the
124	 * snprintf machinery.  This is kinda hacky, but gets the job done
125	 * quickly without having to think about the various snprintf edge
126	 * cases.
127	 */
128	char fmt[FMT_SIZE];
129	char buf[BUF_SIZE];
130
131#define EMIT_SIMPLE(type, format)					\
132	emitter_printf(emitter,						\
133	    emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width),	\
134	    *(const type *)value);
135
136	switch (value_type) {
137	case emitter_type_bool:
138		emitter_printf(emitter,
139		    emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width),
140		    *(const bool *)value ?  "true" : "false");
141		break;
142	case emitter_type_int:
143		EMIT_SIMPLE(int, "%d")
144		break;
145	case emitter_type_unsigned:
146		EMIT_SIMPLE(unsigned, "%u")
147		break;
148	case emitter_type_ssize:
149		EMIT_SIMPLE(ssize_t, "%zd")
150		break;
151	case emitter_type_size:
152		EMIT_SIMPLE(size_t, "%zu")
153		break;
154	case emitter_type_string:
155		str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
156		    *(const char *const *)value);
157		/*
158		 * We control the strings we output; we shouldn't get anything
159		 * anywhere near the fmt size.
160		 */
161		assert(str_written < BUF_SIZE);
162		emitter_printf(emitter,
163		    emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width), buf);
164		break;
165	case emitter_type_uint32:
166		EMIT_SIMPLE(uint32_t, "%" FMTu32)
167		break;
168	case emitter_type_uint64:
169		EMIT_SIMPLE(uint64_t, "%" FMTu64)
170		break;
171	case emitter_type_title:
172		EMIT_SIMPLE(char *const, "%s");
173		break;
174	default:
175		unreachable();
176	}
177#undef BUF_SIZE
178#undef FMT_SIZE
179}
180
181
182/* Internal functions.  In json mode, tracks nesting state. */
183static inline void
184emitter_nest_inc(emitter_t *emitter) {
185	emitter->nesting_depth++;
186	emitter->item_at_depth = false;
187}
188
189static inline void
190emitter_nest_dec(emitter_t *emitter) {
191	emitter->nesting_depth--;
192	emitter->item_at_depth = true;
193}
194
195static inline void
196emitter_indent(emitter_t *emitter) {
197	int amount = emitter->nesting_depth;
198	const char *indent_str;
199	if (emitter->output == emitter_output_json) {
200		indent_str = "\t";
201	} else {
202		amount *= 2;
203		indent_str = " ";
204	}
205	for (int i = 0; i < amount; i++) {
206		emitter_printf(emitter, "%s", indent_str);
207	}
208}
209
210static inline void
211emitter_json_key_prefix(emitter_t *emitter) {
212	if (emitter->emitted_key) {
213		emitter->emitted_key = false;
214		return;
215	}
216	emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
217	emitter_indent(emitter);
218}
219
220/******************************************************************************/
221/* Public functions for emitter_t. */
222
223static inline void
224emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
225    void (*write_cb)(void *, const char *), void *cbopaque) {
226	emitter->output = emitter_output;
227	emitter->write_cb = write_cb;
228	emitter->cbopaque = cbopaque;
229	emitter->item_at_depth = false;
230	emitter->emitted_key = false;
231	emitter->nesting_depth = 0;
232}
233
234/******************************************************************************/
235/* JSON public API. */
236
237/*
238 * Emits a key (e.g. as appears in an object). The next json entity emitted will
239 * be the corresponding value.
240 */
241static inline void
242emitter_json_key(emitter_t *emitter, const char *json_key) {
243	if (emitter->output == emitter_output_json) {
244		emitter_json_key_prefix(emitter);
245		emitter_printf(emitter, "\"%s\": ", json_key);
246		emitter->emitted_key = true;
247	}
248}
249
250static inline void
251emitter_json_value(emitter_t *emitter, emitter_type_t value_type,
252    const void *value) {
253	if (emitter->output == emitter_output_json) {
254		emitter_json_key_prefix(emitter);
255		emitter_print_value(emitter, emitter_justify_none, -1,
256		    value_type, value);
257		emitter->item_at_depth = true;
258	}
259}
260
261/* Shorthand for calling emitter_json_key and then emitter_json_value. */
262static inline void
263emitter_json_kv(emitter_t *emitter, const char *json_key,
264    emitter_type_t value_type, const void *value) {
265	emitter_json_key(emitter, json_key);
266	emitter_json_value(emitter, value_type, value);
267}
268
269static inline void
270emitter_json_array_begin(emitter_t *emitter) {
271	if (emitter->output == emitter_output_json) {
272		emitter_json_key_prefix(emitter);
273		emitter_printf(emitter, "[");
274		emitter_nest_inc(emitter);
275	}
276}
277
278/* Shorthand for calling emitter_json_key and then emitter_json_array_begin. */
279static inline void
280emitter_json_array_kv_begin(emitter_t *emitter, const char *json_key) {
281	emitter_json_key(emitter, json_key);
282	emitter_json_array_begin(emitter);
283}
284
285static inline void
286emitter_json_array_end(emitter_t *emitter) {
287	if (emitter->output == emitter_output_json) {
288		assert(emitter->nesting_depth > 0);
289		emitter_nest_dec(emitter);
290		emitter_printf(emitter, "\n");
291		emitter_indent(emitter);
292		emitter_printf(emitter, "]");
293	}
294}
295
296static inline void
297emitter_json_object_begin(emitter_t *emitter) {
298	if (emitter->output == emitter_output_json) {
299		emitter_json_key_prefix(emitter);
300		emitter_printf(emitter, "{");
301		emitter_nest_inc(emitter);
302	}
303}
304
305/* Shorthand for calling emitter_json_key and then emitter_json_object_begin. */
306static inline void
307emitter_json_object_kv_begin(emitter_t *emitter, const char *json_key) {
308	emitter_json_key(emitter, json_key);
309	emitter_json_object_begin(emitter);
310}
311
312static inline void
313emitter_json_object_end(emitter_t *emitter) {
314	if (emitter->output == emitter_output_json) {
315		assert(emitter->nesting_depth > 0);
316		emitter_nest_dec(emitter);
317		emitter_printf(emitter, "\n");
318		emitter_indent(emitter);
319		emitter_printf(emitter, "}");
320	}
321}
322
323
324/******************************************************************************/
325/* Table public API. */
326
327static inline void
328emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
329	if (emitter->output == emitter_output_table) {
330		emitter_indent(emitter);
331		emitter_printf(emitter, "%s\n", table_key);
332		emitter_nest_inc(emitter);
333	}
334}
335
336static inline void
337emitter_table_dict_end(emitter_t *emitter) {
338	if (emitter->output == emitter_output_table) {
339		emitter_nest_dec(emitter);
340	}
341}
342
343static inline void
344emitter_table_kv_note(emitter_t *emitter, const char *table_key,
345    emitter_type_t value_type, const void *value,
346    const char *table_note_key, emitter_type_t table_note_value_type,
347    const void *table_note_value) {
348	if (emitter->output == emitter_output_table) {
349		emitter_indent(emitter);
350		emitter_printf(emitter, "%s: ", table_key);
351		emitter_print_value(emitter, emitter_justify_none, -1,
352		    value_type, value);
353		if (table_note_key != NULL) {
354			emitter_printf(emitter, " (%s: ", table_note_key);
355			emitter_print_value(emitter, emitter_justify_none, -1,
356			    table_note_value_type, table_note_value);
357			emitter_printf(emitter, ")");
358		}
359		emitter_printf(emitter, "\n");
360	}
361	emitter->item_at_depth = true;
362}
363
364static inline void
365emitter_table_kv(emitter_t *emitter, const char *table_key,
366    emitter_type_t value_type, const void *value) {
367	emitter_table_kv_note(emitter, table_key, value_type, value, NULL,
368	    emitter_type_bool, NULL);
369}
370
371
372/* Write to the emitter the given string, but only in table mode. */
373JEMALLOC_FORMAT_PRINTF(2, 3)
374static inline void
375emitter_table_printf(emitter_t *emitter, const char *format, ...) {
376	if (emitter->output == emitter_output_table) {
377		va_list ap;
378		va_start(ap, format);
379		malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
380		va_end(ap);
381	}
382}
383
384static inline void
385emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
386	if (emitter->output != emitter_output_table) {
387		return;
388	}
389	emitter_col_t *col;
390	ql_foreach(col, &row->cols, link) {
391		emitter_print_value(emitter, col->justify, col->width,
392		    col->type, (const void *)&col->bool_val);
393	}
394	emitter_table_printf(emitter, "\n");
395}
396
397static inline void
398emitter_row_init(emitter_row_t *row) {
399	ql_new(&row->cols);
400}
401
402static inline void
403emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
404	ql_elm_new(col, link);
405	ql_tail_insert(&row->cols, col, link);
406}
407
408
409/******************************************************************************/
410/*
411 * Generalized public API. Emits using either JSON or table, according to
412 * settings in the emitter_t. */
413
414/*
415 * Note emits a different kv pair as well, but only in table mode.  Omits the
416 * note if table_note_key is NULL.
417 */
418static inline void
419emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
420    emitter_type_t value_type, const void *value,
421    const char *table_note_key, emitter_type_t table_note_value_type,
422    const void *table_note_value) {
423	if (emitter->output == emitter_output_json) {
424		emitter_json_key(emitter, json_key);
425		emitter_json_value(emitter, value_type, value);
426	} else {
427		emitter_table_kv_note(emitter, table_key, value_type, value,
428		    table_note_key, table_note_value_type, table_note_value);
429	}
430	emitter->item_at_depth = true;
431}
432
433static inline void
434emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
435    emitter_type_t value_type, const void *value) {
436	emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
437	    emitter_type_bool, NULL);
438}
439
440static inline void
441emitter_dict_begin(emitter_t *emitter, const char *json_key,
442    const char *table_header) {
443	if (emitter->output == emitter_output_json) {
444		emitter_json_key(emitter, json_key);
445		emitter_json_object_begin(emitter);
446	} else {
447		emitter_table_dict_begin(emitter, table_header);
448	}
449}
450
451static inline void
452emitter_dict_end(emitter_t *emitter) {
453	if (emitter->output == emitter_output_json) {
454		emitter_json_object_end(emitter);
455	} else {
456		emitter_table_dict_end(emitter);
457	}
458}
459
460static inline void
461emitter_begin(emitter_t *emitter) {
462	if (emitter->output == emitter_output_json) {
463		assert(emitter->nesting_depth == 0);
464		emitter_printf(emitter, "{");
465		emitter_nest_inc(emitter);
466	} else {
467		/*
468		 * This guarantees that we always call write_cb at least once.
469		 * This is useful if some invariant is established by each call
470		 * to write_cb, but doesn't hold initially: e.g., some buffer
471		 * holds a null-terminated string.
472		 */
473		emitter_printf(emitter, "%s", "");
474	}
475}
476
477static inline void
478emitter_end(emitter_t *emitter) {
479	if (emitter->output == emitter_output_json) {
480		assert(emitter->nesting_depth == 1);
481		emitter_nest_dec(emitter);
482		emitter_printf(emitter, "\n}\n");
483	}
484}
485
486#endif /* JEMALLOC_INTERNAL_EMITTER_H */
487