10ef50b4eSJason Evans #ifndef JEMALLOC_INTERNAL_EMITTER_H
20ef50b4eSJason Evans #define JEMALLOC_INTERNAL_EMITTER_H
30ef50b4eSJason Evans 
40ef50b4eSJason Evans #include "jemalloc/internal/ql.h"
50ef50b4eSJason Evans 
60ef50b4eSJason Evans typedef enum emitter_output_e emitter_output_t;
70ef50b4eSJason Evans enum emitter_output_e {
80ef50b4eSJason Evans 	emitter_output_json,
90ef50b4eSJason Evans 	emitter_output_table
100ef50b4eSJason Evans };
110ef50b4eSJason Evans 
120ef50b4eSJason Evans typedef enum emitter_justify_e emitter_justify_t;
130ef50b4eSJason Evans enum emitter_justify_e {
140ef50b4eSJason Evans 	emitter_justify_left,
150ef50b4eSJason Evans 	emitter_justify_right,
160ef50b4eSJason Evans 	/* Not for users; just to pass to internal functions. */
170ef50b4eSJason Evans 	emitter_justify_none
180ef50b4eSJason Evans };
190ef50b4eSJason Evans 
200ef50b4eSJason Evans typedef enum emitter_type_e emitter_type_t;
210ef50b4eSJason Evans enum emitter_type_e {
220ef50b4eSJason Evans 	emitter_type_bool,
230ef50b4eSJason Evans 	emitter_type_int,
240ef50b4eSJason Evans 	emitter_type_unsigned,
250ef50b4eSJason Evans 	emitter_type_uint32,
260ef50b4eSJason Evans 	emitter_type_uint64,
270ef50b4eSJason Evans 	emitter_type_size,
280ef50b4eSJason Evans 	emitter_type_ssize,
290ef50b4eSJason Evans 	emitter_type_string,
300ef50b4eSJason Evans 	/*
310ef50b4eSJason Evans 	 * A title is a column title in a table; it's just a string, but it's
320ef50b4eSJason Evans 	 * not quoted.
330ef50b4eSJason Evans 	 */
340ef50b4eSJason Evans 	emitter_type_title,
350ef50b4eSJason Evans };
360ef50b4eSJason Evans 
370ef50b4eSJason Evans typedef struct emitter_col_s emitter_col_t;
380ef50b4eSJason Evans struct emitter_col_s {
390ef50b4eSJason Evans 	/* Filled in by the user. */
400ef50b4eSJason Evans 	emitter_justify_t justify;
410ef50b4eSJason Evans 	int width;
420ef50b4eSJason Evans 	emitter_type_t type;
430ef50b4eSJason Evans 	union {
440ef50b4eSJason Evans 		bool bool_val;
450ef50b4eSJason Evans 		int int_val;
460ef50b4eSJason Evans 		unsigned unsigned_val;
470ef50b4eSJason Evans 		uint32_t uint32_val;
48c5ad8142SEric van Gyzen 		uint32_t uint32_t_val;
490ef50b4eSJason Evans 		uint64_t uint64_val;
50c5ad8142SEric van Gyzen 		uint64_t uint64_t_val;
510ef50b4eSJason Evans 		size_t size_val;
520ef50b4eSJason Evans 		ssize_t ssize_val;
530ef50b4eSJason Evans 		const char *str_val;
540ef50b4eSJason Evans 	};
550ef50b4eSJason Evans 
560ef50b4eSJason Evans 	/* Filled in by initialization. */
570ef50b4eSJason Evans 	ql_elm(emitter_col_t) link;
580ef50b4eSJason Evans };
590ef50b4eSJason Evans 
600ef50b4eSJason Evans typedef struct emitter_row_s emitter_row_t;
610ef50b4eSJason Evans struct emitter_row_s {
620ef50b4eSJason Evans 	ql_head(emitter_col_t) cols;
630ef50b4eSJason Evans };
640ef50b4eSJason Evans 
650ef50b4eSJason Evans typedef struct emitter_s emitter_t;
660ef50b4eSJason Evans struct emitter_s {
670ef50b4eSJason Evans 	emitter_output_t output;
680ef50b4eSJason Evans 	/* The output information. */
690ef50b4eSJason Evans 	void (*write_cb)(void *, const char *);
700ef50b4eSJason Evans 	void *cbopaque;
710ef50b4eSJason Evans 	int nesting_depth;
720ef50b4eSJason Evans 	/* True if we've already emitted a value at the given depth. */
730ef50b4eSJason Evans 	bool item_at_depth;
74c5ad8142SEric van Gyzen 	/* True if we emitted a key and will emit corresponding value next. */
75c5ad8142SEric van Gyzen 	bool emitted_key;
760ef50b4eSJason Evans };
770ef50b4eSJason Evans 
780ef50b4eSJason Evans /* Internal convenience function.  Write to the emitter the given string. */
790ef50b4eSJason Evans JEMALLOC_FORMAT_PRINTF(2, 3)
800ef50b4eSJason Evans static inline void
emitter_printf(emitter_t * emitter,const char * format,...)810ef50b4eSJason Evans emitter_printf(emitter_t *emitter, const char *format, ...) {
820ef50b4eSJason Evans 	va_list ap;
830ef50b4eSJason Evans 
840ef50b4eSJason Evans 	va_start(ap, format);
850ef50b4eSJason Evans 	malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
860ef50b4eSJason Evans 	va_end(ap);
870ef50b4eSJason Evans }
880ef50b4eSJason Evans 
89c5ad8142SEric van Gyzen static inline const char * JEMALLOC_FORMAT_ARG(3)
emitter_gen_fmt(char * out_fmt,size_t out_size,const char * fmt_specifier,emitter_justify_t justify,int width)900ef50b4eSJason Evans emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
910ef50b4eSJason Evans     emitter_justify_t justify, int width) {
920ef50b4eSJason Evans 	size_t written;
93c5ad8142SEric van Gyzen 	fmt_specifier++;
940ef50b4eSJason Evans 	if (justify == emitter_justify_none) {
950ef50b4eSJason Evans 		written = malloc_snprintf(out_fmt, out_size,
960ef50b4eSJason Evans 		    "%%%s", fmt_specifier);
970ef50b4eSJason Evans 	} else if (justify == emitter_justify_left) {
980ef50b4eSJason Evans 		written = malloc_snprintf(out_fmt, out_size,
990ef50b4eSJason Evans 		    "%%-%d%s", width, fmt_specifier);
1000ef50b4eSJason Evans 	} else {
1010ef50b4eSJason Evans 		written = malloc_snprintf(out_fmt, out_size,
1020ef50b4eSJason Evans 		    "%%%d%s", width, fmt_specifier);
1030ef50b4eSJason Evans 	}
1040ef50b4eSJason Evans 	/* Only happens in case of bad format string, which *we* choose. */
1050ef50b4eSJason Evans 	assert(written <  out_size);
106c5ad8142SEric van Gyzen 	return out_fmt;
1070ef50b4eSJason Evans }
1080ef50b4eSJason Evans 
1090ef50b4eSJason Evans /*
1100ef50b4eSJason Evans  * Internal.  Emit the given value type in the relevant encoding (so that the
1110ef50b4eSJason Evans  * bool true gets mapped to json "true", but the string "true" gets mapped to
1120ef50b4eSJason Evans  * json "\"true\"", for instance.
1130ef50b4eSJason Evans  *
1140ef50b4eSJason Evans  * Width is ignored if justify is emitter_justify_none.
1150ef50b4eSJason Evans  */
1160ef50b4eSJason Evans static inline void
emitter_print_value(emitter_t * emitter,emitter_justify_t justify,int width,emitter_type_t value_type,const void * value)1170ef50b4eSJason Evans emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
1180ef50b4eSJason Evans     emitter_type_t value_type, const void *value) {
1190ef50b4eSJason Evans 	size_t str_written;
1200ef50b4eSJason Evans #define BUF_SIZE 256
1210ef50b4eSJason Evans #define FMT_SIZE 10
1220ef50b4eSJason Evans 	/*
1230ef50b4eSJason Evans 	 * We dynamically generate a format string to emit, to let us use the
1240ef50b4eSJason Evans 	 * snprintf machinery.  This is kinda hacky, but gets the job done
1250ef50b4eSJason Evans 	 * quickly without having to think about the various snprintf edge
1260ef50b4eSJason Evans 	 * cases.
1270ef50b4eSJason Evans 	 */
1280ef50b4eSJason Evans 	char fmt[FMT_SIZE];
1290ef50b4eSJason Evans 	char buf[BUF_SIZE];
1300ef50b4eSJason Evans 
1310ef50b4eSJason Evans #define EMIT_SIMPLE(type, format)					\
132c5ad8142SEric van Gyzen 	emitter_printf(emitter,						\
133c5ad8142SEric van Gyzen 	    emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width),	\
134c5ad8142SEric van Gyzen 	    *(const type *)value);
1350ef50b4eSJason Evans 
1360ef50b4eSJason Evans 	switch (value_type) {
1370ef50b4eSJason Evans 	case emitter_type_bool:
138c5ad8142SEric van Gyzen 		emitter_printf(emitter,
139c5ad8142SEric van Gyzen 		    emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width),
140c5ad8142SEric van Gyzen 		    *(const bool *)value ?  "true" : "false");
1410ef50b4eSJason Evans 		break;
1420ef50b4eSJason Evans 	case emitter_type_int:
143c5ad8142SEric van Gyzen 		EMIT_SIMPLE(int, "%d")
1440ef50b4eSJason Evans 		break;
1450ef50b4eSJason Evans 	case emitter_type_unsigned:
146c5ad8142SEric van Gyzen 		EMIT_SIMPLE(unsigned, "%u")
1470ef50b4eSJason Evans 		break;
1480ef50b4eSJason Evans 	case emitter_type_ssize:
149c5ad8142SEric van Gyzen 		EMIT_SIMPLE(ssize_t, "%zd")
1500ef50b4eSJason Evans 		break;
1510ef50b4eSJason Evans 	case emitter_type_size:
152c5ad8142SEric van Gyzen 		EMIT_SIMPLE(size_t, "%zu")
1530ef50b4eSJason Evans 		break;
1540ef50b4eSJason Evans 	case emitter_type_string:
1550ef50b4eSJason Evans 		str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
1560ef50b4eSJason Evans 		    *(const char *const *)value);
1570ef50b4eSJason Evans 		/*
1580ef50b4eSJason Evans 		 * We control the strings we output; we shouldn't get anything
1590ef50b4eSJason Evans 		 * anywhere near the fmt size.
1600ef50b4eSJason Evans 		 */
1610ef50b4eSJason Evans 		assert(str_written < BUF_SIZE);
162c5ad8142SEric van Gyzen 		emitter_printf(emitter,
163c5ad8142SEric van Gyzen 		    emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width), buf);
1640ef50b4eSJason Evans 		break;
1650ef50b4eSJason Evans 	case emitter_type_uint32:
166c5ad8142SEric van Gyzen 		EMIT_SIMPLE(uint32_t, "%" FMTu32)
1670ef50b4eSJason Evans 		break;
1680ef50b4eSJason Evans 	case emitter_type_uint64:
169c5ad8142SEric van Gyzen 		EMIT_SIMPLE(uint64_t, "%" FMTu64)
1700ef50b4eSJason Evans 		break;
1710ef50b4eSJason Evans 	case emitter_type_title:
172c5ad8142SEric van Gyzen 		EMIT_SIMPLE(char *const, "%s");
1730ef50b4eSJason Evans 		break;
1740ef50b4eSJason Evans 	default:
1750ef50b4eSJason Evans 		unreachable();
1760ef50b4eSJason Evans 	}
1770ef50b4eSJason Evans #undef BUF_SIZE
1780ef50b4eSJason Evans #undef FMT_SIZE
1790ef50b4eSJason Evans }
1800ef50b4eSJason Evans 
1810ef50b4eSJason Evans 
1820ef50b4eSJason Evans /* Internal functions.  In json mode, tracks nesting state. */
1830ef50b4eSJason Evans static inline void
emitter_nest_inc(emitter_t * emitter)1840ef50b4eSJason Evans emitter_nest_inc(emitter_t *emitter) {
1850ef50b4eSJason Evans 	emitter->nesting_depth++;
1860ef50b4eSJason Evans 	emitter->item_at_depth = false;
1870ef50b4eSJason Evans }
1880ef50b4eSJason Evans 
1890ef50b4eSJason Evans static inline void
emitter_nest_dec(emitter_t * emitter)1900ef50b4eSJason Evans emitter_nest_dec(emitter_t *emitter) {
1910ef50b4eSJason Evans 	emitter->nesting_depth--;
1920ef50b4eSJason Evans 	emitter->item_at_depth = true;
1930ef50b4eSJason Evans }
1940ef50b4eSJason Evans 
1950ef50b4eSJason Evans static inline void
emitter_indent(emitter_t * emitter)1960ef50b4eSJason Evans emitter_indent(emitter_t *emitter) {
1970ef50b4eSJason Evans 	int amount = emitter->nesting_depth;
1980ef50b4eSJason Evans 	const char *indent_str;
1990ef50b4eSJason Evans 	if (emitter->output == emitter_output_json) {
2000ef50b4eSJason Evans 		indent_str = "\t";
2010ef50b4eSJason Evans 	} else {
2020ef50b4eSJason Evans 		amount *= 2;
2030ef50b4eSJason Evans 		indent_str = " ";
2040ef50b4eSJason Evans 	}
2050ef50b4eSJason Evans 	for (int i = 0; i < amount; i++) {
2060ef50b4eSJason Evans 		emitter_printf(emitter, "%s", indent_str);
2070ef50b4eSJason Evans 	}
2080ef50b4eSJason Evans }
2090ef50b4eSJason Evans 
2100ef50b4eSJason Evans static inline void
emitter_json_key_prefix(emitter_t * emitter)2110ef50b4eSJason Evans emitter_json_key_prefix(emitter_t *emitter) {
212c5ad8142SEric van Gyzen 	if (emitter->emitted_key) {
213c5ad8142SEric van Gyzen 		emitter->emitted_key = false;
214c5ad8142SEric van Gyzen 		return;
215c5ad8142SEric van Gyzen 	}
2160ef50b4eSJason Evans 	emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
2170ef50b4eSJason Evans 	emitter_indent(emitter);
2180ef50b4eSJason Evans }
2190ef50b4eSJason Evans 
220c5ad8142SEric van Gyzen /******************************************************************************/
221c5ad8142SEric van Gyzen /* Public functions for emitter_t. */
222e1c167d0SJason Evans 
223e1c167d0SJason Evans static inline void
emitter_init(emitter_t * emitter,emitter_output_t emitter_output,void (* write_cb)(void *,const char *),void * cbopaque)224c5ad8142SEric van Gyzen emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
225c5ad8142SEric van Gyzen     void (*write_cb)(void *, const char *), void *cbopaque) {
226c5ad8142SEric van Gyzen 	emitter->output = emitter_output;
227c5ad8142SEric van Gyzen 	emitter->write_cb = write_cb;
228c5ad8142SEric van Gyzen 	emitter->cbopaque = cbopaque;
229c5ad8142SEric van Gyzen 	emitter->item_at_depth = false;
230c5ad8142SEric van Gyzen 	emitter->emitted_key = false;
231c5ad8142SEric van Gyzen 	emitter->nesting_depth = 0;
232e1c167d0SJason Evans }
233c5ad8142SEric van Gyzen 
234c5ad8142SEric van Gyzen /******************************************************************************/
235c5ad8142SEric van Gyzen /* JSON public API. */
236e1c167d0SJason Evans 
237f2cb2907SJason Evans /*
238c5ad8142SEric van Gyzen  * Emits a key (e.g. as appears in an object). The next json entity emitted will
239c5ad8142SEric van Gyzen  * be the corresponding value.
240f2cb2907SJason Evans  */
241e1c167d0SJason Evans static inline void
emitter_json_key(emitter_t * emitter,const char * json_key)242c5ad8142SEric van Gyzen emitter_json_key(emitter_t *emitter, const char *json_key) {
243c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
244c5ad8142SEric van Gyzen 		emitter_json_key_prefix(emitter);
245c5ad8142SEric van Gyzen 		emitter_printf(emitter, "\"%s\": ", json_key);
246c5ad8142SEric van Gyzen 		emitter->emitted_key = true;
247c5ad8142SEric van Gyzen 	}
248c5ad8142SEric van Gyzen }
249c5ad8142SEric van Gyzen 
250c5ad8142SEric van Gyzen static inline void
emitter_json_value(emitter_t * emitter,emitter_type_t value_type,const void * value)251c5ad8142SEric van Gyzen emitter_json_value(emitter_t *emitter, emitter_type_t value_type,
252c5ad8142SEric van Gyzen     const void *value) {
253c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
254c5ad8142SEric van Gyzen 		emitter_json_key_prefix(emitter);
255c5ad8142SEric van Gyzen 		emitter_print_value(emitter, emitter_justify_none, -1,
256c5ad8142SEric van Gyzen 		    value_type, value);
257c5ad8142SEric van Gyzen 		emitter->item_at_depth = true;
258c5ad8142SEric van Gyzen 	}
259c5ad8142SEric van Gyzen }
260c5ad8142SEric van Gyzen 
261c5ad8142SEric van Gyzen /* Shorthand for calling emitter_json_key and then emitter_json_value. */
262c5ad8142SEric van Gyzen static inline void
emitter_json_kv(emitter_t * emitter,const char * json_key,emitter_type_t value_type,const void * value)263c5ad8142SEric van Gyzen emitter_json_kv(emitter_t *emitter, const char *json_key,
264c5ad8142SEric van Gyzen     emitter_type_t value_type, const void *value) {
265c5ad8142SEric van Gyzen 	emitter_json_key(emitter, json_key);
266c5ad8142SEric van Gyzen 	emitter_json_value(emitter, value_type, value);
267c5ad8142SEric van Gyzen }
268c5ad8142SEric van Gyzen 
269c5ad8142SEric van Gyzen static inline void
emitter_json_array_begin(emitter_t * emitter)270c5ad8142SEric van Gyzen emitter_json_array_begin(emitter_t *emitter) {
271c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
272c5ad8142SEric van Gyzen 		emitter_json_key_prefix(emitter);
273c5ad8142SEric van Gyzen 		emitter_printf(emitter, "[");
274c5ad8142SEric van Gyzen 		emitter_nest_inc(emitter);
275c5ad8142SEric van Gyzen 	}
276c5ad8142SEric van Gyzen }
277c5ad8142SEric van Gyzen 
278c5ad8142SEric van Gyzen /* Shorthand for calling emitter_json_key and then emitter_json_array_begin. */
279c5ad8142SEric van Gyzen static inline void
emitter_json_array_kv_begin(emitter_t * emitter,const char * json_key)280c5ad8142SEric van Gyzen emitter_json_array_kv_begin(emitter_t *emitter, const char *json_key) {
281c5ad8142SEric van Gyzen 	emitter_json_key(emitter, json_key);
282c5ad8142SEric van Gyzen 	emitter_json_array_begin(emitter);
283c5ad8142SEric van Gyzen }
284c5ad8142SEric van Gyzen 
285c5ad8142SEric van Gyzen static inline void
emitter_json_array_end(emitter_t * emitter)286c5ad8142SEric van Gyzen emitter_json_array_end(emitter_t *emitter) {
287c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
288c5ad8142SEric van Gyzen 		assert(emitter->nesting_depth > 0);
289c5ad8142SEric van Gyzen 		emitter_nest_dec(emitter);
290c5ad8142SEric van Gyzen 		emitter_printf(emitter, "\n");
291c5ad8142SEric van Gyzen 		emitter_indent(emitter);
292c5ad8142SEric van Gyzen 		emitter_printf(emitter, "]");
293c5ad8142SEric van Gyzen 	}
294c5ad8142SEric van Gyzen }
295c5ad8142SEric van Gyzen 
296c5ad8142SEric van Gyzen static inline void
emitter_json_object_begin(emitter_t * emitter)297c5ad8142SEric van Gyzen emitter_json_object_begin(emitter_t *emitter) {
298c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
299c5ad8142SEric van Gyzen 		emitter_json_key_prefix(emitter);
300c5ad8142SEric van Gyzen 		emitter_printf(emitter, "{");
301c5ad8142SEric van Gyzen 		emitter_nest_inc(emitter);
302c5ad8142SEric van Gyzen 	}
303c5ad8142SEric van Gyzen }
304c5ad8142SEric van Gyzen 
305c5ad8142SEric van Gyzen /* Shorthand for calling emitter_json_key and then emitter_json_object_begin. */
306c5ad8142SEric van Gyzen static inline void
emitter_json_object_kv_begin(emitter_t * emitter,const char * json_key)307c5ad8142SEric van Gyzen emitter_json_object_kv_begin(emitter_t *emitter, const char *json_key) {
308c5ad8142SEric van Gyzen 	emitter_json_key(emitter, json_key);
309c5ad8142SEric van Gyzen 	emitter_json_object_begin(emitter);
310c5ad8142SEric van Gyzen }
311c5ad8142SEric van Gyzen 
312c5ad8142SEric van Gyzen static inline void
emitter_json_object_end(emitter_t * emitter)313c5ad8142SEric van Gyzen emitter_json_object_end(emitter_t *emitter) {
314c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
315c5ad8142SEric van Gyzen 		assert(emitter->nesting_depth > 0);
316c5ad8142SEric van Gyzen 		emitter_nest_dec(emitter);
317c5ad8142SEric van Gyzen 		emitter_printf(emitter, "\n");
318c5ad8142SEric van Gyzen 		emitter_indent(emitter);
319c5ad8142SEric van Gyzen 		emitter_printf(emitter, "}");
320c5ad8142SEric van Gyzen 	}
321c5ad8142SEric van Gyzen }
322c5ad8142SEric van Gyzen 
323c5ad8142SEric van Gyzen 
324c5ad8142SEric van Gyzen /******************************************************************************/
325c5ad8142SEric van Gyzen /* Table public API. */
326c5ad8142SEric van Gyzen 
327c5ad8142SEric van Gyzen static inline void
emitter_table_dict_begin(emitter_t * emitter,const char * table_key)328c5ad8142SEric van Gyzen emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
329c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_table) {
330c5ad8142SEric van Gyzen 		emitter_indent(emitter);
331c5ad8142SEric van Gyzen 		emitter_printf(emitter, "%s\n", table_key);
332c5ad8142SEric van Gyzen 		emitter_nest_inc(emitter);
333c5ad8142SEric van Gyzen 	}
334c5ad8142SEric van Gyzen }
335c5ad8142SEric van Gyzen 
336c5ad8142SEric van Gyzen static inline void
emitter_table_dict_end(emitter_t * emitter)337c5ad8142SEric van Gyzen emitter_table_dict_end(emitter_t *emitter) {
338c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_table) {
339c5ad8142SEric van Gyzen 		emitter_nest_dec(emitter);
340c5ad8142SEric van Gyzen 	}
341c5ad8142SEric van Gyzen }
342c5ad8142SEric van Gyzen 
343c5ad8142SEric van Gyzen static inline void
emitter_table_kv_note(emitter_t * emitter,const char * table_key,emitter_type_t value_type,const void * value,const char * table_note_key,emitter_type_t table_note_value_type,const void * table_note_value)344c5ad8142SEric van Gyzen emitter_table_kv_note(emitter_t *emitter, const char *table_key,
3450ef50b4eSJason Evans     emitter_type_t value_type, const void *value,
3460ef50b4eSJason Evans     const char *table_note_key, emitter_type_t table_note_value_type,
3470ef50b4eSJason Evans     const void *table_note_value) {
348c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_table) {
3490ef50b4eSJason Evans 		emitter_indent(emitter);
3500ef50b4eSJason Evans 		emitter_printf(emitter, "%s: ", table_key);
3510ef50b4eSJason Evans 		emitter_print_value(emitter, emitter_justify_none, -1,
3520ef50b4eSJason Evans 		    value_type, value);
3530ef50b4eSJason Evans 		if (table_note_key != NULL) {
3540ef50b4eSJason Evans 			emitter_printf(emitter, " (%s: ", table_note_key);
3550ef50b4eSJason Evans 			emitter_print_value(emitter, emitter_justify_none, -1,
3560ef50b4eSJason Evans 			    table_note_value_type, table_note_value);
3570ef50b4eSJason Evans 			emitter_printf(emitter, ")");
3580ef50b4eSJason Evans 		}
3590ef50b4eSJason Evans 		emitter_printf(emitter, "\n");
3600ef50b4eSJason Evans 	}
3610ef50b4eSJason Evans 	emitter->item_at_depth = true;
3620ef50b4eSJason Evans }
3630ef50b4eSJason Evans 
3640ef50b4eSJason Evans static inline void
emitter_table_kv(emitter_t * emitter,const char * table_key,emitter_type_t value_type,const void * value)365c5ad8142SEric van Gyzen emitter_table_kv(emitter_t *emitter, const char *table_key,
3660ef50b4eSJason Evans     emitter_type_t value_type, const void *value) {
367c5ad8142SEric van Gyzen 	emitter_table_kv_note(emitter, table_key, value_type, value, NULL,
3680ef50b4eSJason Evans 	    emitter_type_bool, NULL);
3690ef50b4eSJason Evans }
3700ef50b4eSJason Evans 
371f2cb2907SJason Evans 
372c5ad8142SEric van Gyzen /* Write to the emitter the given string, but only in table mode. */
373c5ad8142SEric van Gyzen JEMALLOC_FORMAT_PRINTF(2, 3)
374f2cb2907SJason Evans static inline void
emitter_table_printf(emitter_t * emitter,const char * format,...)375c5ad8142SEric van Gyzen emitter_table_printf(emitter_t *emitter, const char *format, ...) {
3760ef50b4eSJason Evans 	if (emitter->output == emitter_output_table) {
377c5ad8142SEric van Gyzen 		va_list ap;
378c5ad8142SEric van Gyzen 		va_start(ap, format);
379c5ad8142SEric van Gyzen 		malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
380c5ad8142SEric van Gyzen 		va_end(ap);
3810ef50b4eSJason Evans 	}
3820ef50b4eSJason Evans }
3830ef50b4eSJason Evans 
3840ef50b4eSJason Evans static inline void
emitter_table_row(emitter_t * emitter,emitter_row_t * row)3850ef50b4eSJason Evans emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
3860ef50b4eSJason Evans 	if (emitter->output != emitter_output_table) {
3870ef50b4eSJason Evans 		return;
3880ef50b4eSJason Evans 	}
3890ef50b4eSJason Evans 	emitter_col_t *col;
3900ef50b4eSJason Evans 	ql_foreach(col, &row->cols, link) {
3910ef50b4eSJason Evans 		emitter_print_value(emitter, col->justify, col->width,
3920ef50b4eSJason Evans 		    col->type, (const void *)&col->bool_val);
3930ef50b4eSJason Evans 	}
3940ef50b4eSJason Evans 	emitter_table_printf(emitter, "\n");
3950ef50b4eSJason Evans }
3960ef50b4eSJason Evans 
397c5ad8142SEric van Gyzen static inline void
emitter_row_init(emitter_row_t * row)398c5ad8142SEric van Gyzen emitter_row_init(emitter_row_t *row) {
399c5ad8142SEric van Gyzen 	ql_new(&row->cols);
400c5ad8142SEric van Gyzen }
401c5ad8142SEric van Gyzen 
402c5ad8142SEric van Gyzen static inline void
emitter_col_init(emitter_col_t * col,emitter_row_t * row)403c5ad8142SEric van Gyzen emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
404c5ad8142SEric van Gyzen 	ql_elm_new(col, link);
405c5ad8142SEric van Gyzen 	ql_tail_insert(&row->cols, col, link);
406c5ad8142SEric van Gyzen }
407c5ad8142SEric van Gyzen 
408c5ad8142SEric van Gyzen 
409c5ad8142SEric van Gyzen /******************************************************************************/
410c5ad8142SEric van Gyzen /*
411c5ad8142SEric van Gyzen  * Generalized public API. Emits using either JSON or table, according to
412c5ad8142SEric van Gyzen  * settings in the emitter_t. */
413c5ad8142SEric van Gyzen 
414c5ad8142SEric van Gyzen /*
415c5ad8142SEric van Gyzen  * Note emits a different kv pair as well, but only in table mode.  Omits the
416c5ad8142SEric van Gyzen  * note if table_note_key is NULL.
417c5ad8142SEric van Gyzen  */
418c5ad8142SEric van Gyzen static inline void
emitter_kv_note(emitter_t * emitter,const char * json_key,const char * table_key,emitter_type_t value_type,const void * value,const char * table_note_key,emitter_type_t table_note_value_type,const void * table_note_value)419c5ad8142SEric van Gyzen emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
420c5ad8142SEric van Gyzen     emitter_type_t value_type, const void *value,
421c5ad8142SEric van Gyzen     const char *table_note_key, emitter_type_t table_note_value_type,
422c5ad8142SEric van Gyzen     const void *table_note_value) {
423c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
424c5ad8142SEric van Gyzen 		emitter_json_key(emitter, json_key);
425c5ad8142SEric van Gyzen 		emitter_json_value(emitter, value_type, value);
426c5ad8142SEric van Gyzen 	} else {
427c5ad8142SEric van Gyzen 		emitter_table_kv_note(emitter, table_key, value_type, value,
428c5ad8142SEric van Gyzen 		    table_note_key, table_note_value_type, table_note_value);
429c5ad8142SEric van Gyzen 	}
430c5ad8142SEric van Gyzen 	emitter->item_at_depth = true;
431c5ad8142SEric van Gyzen }
432c5ad8142SEric van Gyzen 
433c5ad8142SEric van Gyzen static inline void
emitter_kv(emitter_t * emitter,const char * json_key,const char * table_key,emitter_type_t value_type,const void * value)434c5ad8142SEric van Gyzen emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
435c5ad8142SEric van Gyzen     emitter_type_t value_type, const void *value) {
436c5ad8142SEric van Gyzen 	emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
437c5ad8142SEric van Gyzen 	    emitter_type_bool, NULL);
438c5ad8142SEric van Gyzen }
439c5ad8142SEric van Gyzen 
440c5ad8142SEric van Gyzen static inline void
emitter_dict_begin(emitter_t * emitter,const char * json_key,const char * table_header)441c5ad8142SEric van Gyzen emitter_dict_begin(emitter_t *emitter, const char *json_key,
442c5ad8142SEric van Gyzen     const char *table_header) {
443c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
444c5ad8142SEric van Gyzen 		emitter_json_key(emitter, json_key);
445c5ad8142SEric van Gyzen 		emitter_json_object_begin(emitter);
446c5ad8142SEric van Gyzen 	} else {
447c5ad8142SEric van Gyzen 		emitter_table_dict_begin(emitter, table_header);
448c5ad8142SEric van Gyzen 	}
449c5ad8142SEric van Gyzen }
450c5ad8142SEric van Gyzen 
451c5ad8142SEric van Gyzen static inline void
emitter_dict_end(emitter_t * emitter)452c5ad8142SEric van Gyzen emitter_dict_end(emitter_t *emitter) {
453c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
454c5ad8142SEric van Gyzen 		emitter_json_object_end(emitter);
455c5ad8142SEric van Gyzen 	} else {
456c5ad8142SEric van Gyzen 		emitter_table_dict_end(emitter);
457c5ad8142SEric van Gyzen 	}
458c5ad8142SEric van Gyzen }
459c5ad8142SEric van Gyzen 
460c5ad8142SEric van Gyzen static inline void
emitter_begin(emitter_t * emitter)461c5ad8142SEric van Gyzen emitter_begin(emitter_t *emitter) {
462c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
463c5ad8142SEric van Gyzen 		assert(emitter->nesting_depth == 0);
464c5ad8142SEric van Gyzen 		emitter_printf(emitter, "{");
465c5ad8142SEric van Gyzen 		emitter_nest_inc(emitter);
466c5ad8142SEric van Gyzen 	} else {
467c5ad8142SEric van Gyzen 		/*
468c5ad8142SEric van Gyzen 		 * This guarantees that we always call write_cb at least once.
469c5ad8142SEric van Gyzen 		 * This is useful if some invariant is established by each call
470c5ad8142SEric van Gyzen 		 * to write_cb, but doesn't hold initially: e.g., some buffer
471c5ad8142SEric van Gyzen 		 * holds a null-terminated string.
472c5ad8142SEric van Gyzen 		 */
473c5ad8142SEric van Gyzen 		emitter_printf(emitter, "%s", "");
474c5ad8142SEric van Gyzen 	}
475c5ad8142SEric van Gyzen }
476c5ad8142SEric van Gyzen 
477c5ad8142SEric van Gyzen static inline void
emitter_end(emitter_t * emitter)478c5ad8142SEric van Gyzen emitter_end(emitter_t *emitter) {
479c5ad8142SEric van Gyzen 	if (emitter->output == emitter_output_json) {
480c5ad8142SEric van Gyzen 		assert(emitter->nesting_depth == 1);
481c5ad8142SEric van Gyzen 		emitter_nest_dec(emitter);
482c5ad8142SEric van Gyzen 		emitter_printf(emitter, "\n}\n");
483c5ad8142SEric van Gyzen 	}
484c5ad8142SEric van Gyzen }
485c5ad8142SEric van Gyzen 
4860ef50b4eSJason Evans #endif /* JEMALLOC_INTERNAL_EMITTER_H */
487