Blame jemalloc/include/jemalloc/internal/emitter.h

Packit 345191
#ifndef JEMALLOC_INTERNAL_EMITTER_H
Packit 345191
#define JEMALLOC_INTERNAL_EMITTER_H
Packit 345191
Packit 345191
#include "jemalloc/internal/ql.h"
Packit 345191
Packit 345191
typedef enum emitter_output_e emitter_output_t;
Packit 345191
enum emitter_output_e {
Packit 345191
	emitter_output_json,
Packit 345191
	emitter_output_table
Packit 345191
};
Packit 345191
Packit 345191
typedef enum emitter_justify_e emitter_justify_t;
Packit 345191
enum emitter_justify_e {
Packit 345191
	emitter_justify_left,
Packit 345191
	emitter_justify_right,
Packit 345191
	/* Not for users; just to pass to internal functions. */
Packit 345191
	emitter_justify_none
Packit 345191
};
Packit 345191
Packit 345191
typedef enum emitter_type_e emitter_type_t;
Packit 345191
enum emitter_type_e {
Packit 345191
	emitter_type_bool,
Packit 345191
	emitter_type_int,
Packit 345191
	emitter_type_unsigned,
Packit 345191
	emitter_type_uint32,
Packit 345191
	emitter_type_uint64,
Packit 345191
	emitter_type_size,
Packit 345191
	emitter_type_ssize,
Packit 345191
	emitter_type_string,
Packit 345191
	/*
Packit 345191
	 * A title is a column title in a table; it's just a string, but it's
Packit 345191
	 * not quoted.
Packit 345191
	 */
Packit 345191
	emitter_type_title,
Packit 345191
};
Packit 345191
Packit 345191
typedef struct emitter_col_s emitter_col_t;
Packit 345191
struct emitter_col_s {
Packit 345191
	/* Filled in by the user. */
Packit 345191
	emitter_justify_t justify;
Packit 345191
	int width;
Packit 345191
	emitter_type_t type;
Packit 345191
	union {
Packit 345191
		bool bool_val;
Packit 345191
		int int_val;
Packit 345191
		unsigned unsigned_val;
Packit 345191
		uint32_t uint32_val;
Packit 345191
		uint32_t uint32_t_val;
Packit 345191
		uint64_t uint64_val;
Packit 345191
		uint64_t uint64_t_val;
Packit 345191
		size_t size_val;
Packit 345191
		ssize_t ssize_val;
Packit 345191
		const char *str_val;
Packit 345191
	};
Packit 345191
Packit 345191
	/* Filled in by initialization. */
Packit 345191
	ql_elm(emitter_col_t) link;
Packit 345191
};
Packit 345191
Packit 345191
typedef struct emitter_row_s emitter_row_t;
Packit 345191
struct emitter_row_s {
Packit 345191
	ql_head(emitter_col_t) cols;
Packit 345191
};
Packit 345191
Packit 345191
typedef struct emitter_s emitter_t;
Packit 345191
struct emitter_s {
Packit 345191
	emitter_output_t output;
Packit 345191
	/* The output information. */
Packit 345191
	void (*write_cb)(void *, const char *);
Packit 345191
	void *cbopaque;
Packit 345191
	int nesting_depth;
Packit 345191
	/* True if we've already emitted a value at the given depth. */
Packit 345191
	bool item_at_depth;
Packit 345191
	/* True if we emitted a key and will emit corresponding value next. */
Packit 345191
	bool emitted_key;
Packit 345191
};
Packit 345191
Packit 345191
/* Internal convenience function.  Write to the emitter the given string. */
Packit 345191
JEMALLOC_FORMAT_PRINTF(2, 3)
Packit 345191
static inline void
Packit 345191
emitter_printf(emitter_t *emitter, const char *format, ...) {
Packit 345191
	va_list ap;
Packit 345191
Packit 345191
	va_start(ap, format);
Packit 345191
	malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
Packit 345191
	va_end(ap);
Packit 345191
}
Packit 345191
Packit 345191
static inline const char * JEMALLOC_FORMAT_ARG(3)
Packit 345191
emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
Packit 345191
    emitter_justify_t justify, int width) {
Packit 345191
	size_t written;
Packit 345191
	fmt_specifier++;
Packit 345191
	if (justify == emitter_justify_none) {
Packit 345191
		written = malloc_snprintf(out_fmt, out_size,
Packit 345191
		    "%%%s", fmt_specifier);
Packit 345191
	} else if (justify == emitter_justify_left) {
Packit 345191
		written = malloc_snprintf(out_fmt, out_size,
Packit 345191
		    "%%-%d%s", width, fmt_specifier);
Packit 345191
	} else {
Packit 345191
		written = malloc_snprintf(out_fmt, out_size,
Packit 345191
		    "%%%d%s", width, fmt_specifier);
Packit 345191
	}
Packit 345191
	/* Only happens in case of bad format string, which *we* choose. */
Packit 345191
	assert(written <  out_size);
Packit 345191
	return out_fmt;
Packit 345191
}
Packit 345191
Packit 345191
/*
Packit 345191
 * Internal.  Emit the given value type in the relevant encoding (so that the
Packit 345191
 * bool true gets mapped to json "true", but the string "true" gets mapped to
Packit 345191
 * json "\"true\"", for instance.
Packit 345191
 *
Packit 345191
 * Width is ignored if justify is emitter_justify_none.
Packit 345191
 */
Packit 345191
static inline void
Packit 345191
emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
Packit 345191
    emitter_type_t value_type, const void *value) {
Packit 345191
	size_t str_written;
Packit 345191
#define BUF_SIZE 256
Packit 345191
#define FMT_SIZE 10
Packit 345191
	/*
Packit 345191
	 * We dynamically generate a format string to emit, to let us use the
Packit 345191
	 * snprintf machinery.  This is kinda hacky, but gets the job done
Packit 345191
	 * quickly without having to think about the various snprintf edge
Packit 345191
	 * cases.
Packit 345191
	 */
Packit 345191
	char fmt[FMT_SIZE];
Packit 345191
	char buf[BUF_SIZE];
Packit 345191
Packit 345191
#define EMIT_SIMPLE(type, format)					\
Packit 345191
	emitter_printf(emitter,						\
Packit 345191
	    emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width),	\
Packit 345191
	    *(const type *)value);
Packit 345191
Packit 345191
	switch (value_type) {
Packit 345191
	case emitter_type_bool:
Packit 345191
		emitter_printf(emitter,
Packit 345191
		    emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width),
Packit 345191
		    *(const bool *)value ?  "true" : "false");
Packit 345191
		break;
Packit 345191
	case emitter_type_int:
Packit 345191
		EMIT_SIMPLE(int, "%d")
Packit 345191
		break;
Packit 345191
	case emitter_type_unsigned:
Packit 345191
		EMIT_SIMPLE(unsigned, "%u")
Packit 345191
		break;
Packit 345191
	case emitter_type_ssize:
Packit 345191
		EMIT_SIMPLE(ssize_t, "%zd")
Packit 345191
		break;
Packit 345191
	case emitter_type_size:
Packit 345191
		EMIT_SIMPLE(size_t, "%zu")
Packit 345191
		break;
Packit 345191
	case emitter_type_string:
Packit 345191
		str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
Packit 345191
		    *(const char *const *)value);
Packit 345191
		/*
Packit 345191
		 * We control the strings we output; we shouldn't get anything
Packit 345191
		 * anywhere near the fmt size.
Packit 345191
		 */
Packit 345191
		assert(str_written < BUF_SIZE);
Packit 345191
		emitter_printf(emitter,
Packit 345191
		    emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width), buf);
Packit 345191
		break;
Packit 345191
	case emitter_type_uint32:
Packit 345191
		EMIT_SIMPLE(uint32_t, "%" FMTu32)
Packit 345191
		break;
Packit 345191
	case emitter_type_uint64:
Packit 345191
		EMIT_SIMPLE(uint64_t, "%" FMTu64)
Packit 345191
		break;
Packit 345191
	case emitter_type_title:
Packit 345191
		EMIT_SIMPLE(char *const, "%s");
Packit 345191
		break;
Packit 345191
	default:
Packit 345191
		unreachable();
Packit 345191
	}
Packit 345191
#undef BUF_SIZE
Packit 345191
#undef FMT_SIZE
Packit 345191
}
Packit 345191
Packit 345191
Packit 345191
/* Internal functions.  In json mode, tracks nesting state. */
Packit 345191
static inline void
Packit 345191
emitter_nest_inc(emitter_t *emitter) {
Packit 345191
	emitter->nesting_depth++;
Packit 345191
	emitter->item_at_depth = false;
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_nest_dec(emitter_t *emitter) {
Packit 345191
	emitter->nesting_depth--;
Packit 345191
	emitter->item_at_depth = true;
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_indent(emitter_t *emitter) {
Packit 345191
	int amount = emitter->nesting_depth;
Packit 345191
	const char *indent_str;
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		indent_str = "\t";
Packit 345191
	} else {
Packit 345191
		amount *= 2;
Packit 345191
		indent_str = " ";
Packit 345191
	}
Packit 345191
	for (int i = 0; i < amount; i++) {
Packit 345191
		emitter_printf(emitter, "%s", indent_str);
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_json_key_prefix(emitter_t *emitter) {
Packit 345191
	if (emitter->emitted_key) {
Packit 345191
		emitter->emitted_key = false;
Packit 345191
		return;
Packit 345191
	}
Packit 345191
	emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
Packit 345191
	emitter_indent(emitter);
Packit 345191
}
Packit 345191
Packit 345191
/******************************************************************************/
Packit 345191
/* Public functions for emitter_t. */
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
Packit 345191
    void (*write_cb)(void *, const char *), void *cbopaque) {
Packit 345191
	emitter->output = emitter_output;
Packit 345191
	emitter->write_cb = write_cb;
Packit 345191
	emitter->cbopaque = cbopaque;
Packit 345191
	emitter->item_at_depth = false;
Packit 345191
	emitter->emitted_key = false;
Packit 345191
	emitter->nesting_depth = 0;
Packit 345191
}
Packit 345191
Packit 345191
/******************************************************************************/
Packit 345191
/* JSON public API. */
Packit 345191
Packit 345191
/*
Packit 345191
 * Emits a key (e.g. as appears in an object). The next json entity emitted will
Packit 345191
 * be the corresponding value.
Packit 345191
 */
Packit 345191
static inline void
Packit 345191
emitter_json_key(emitter_t *emitter, const char *json_key) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		emitter_json_key_prefix(emitter);
Packit 345191
		emitter_printf(emitter, "\"%s\": ", json_key);
Packit 345191
		emitter->emitted_key = true;
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_json_value(emitter_t *emitter, emitter_type_t value_type,
Packit 345191
    const void *value) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		emitter_json_key_prefix(emitter);
Packit 345191
		emitter_print_value(emitter, emitter_justify_none, -1,
Packit 345191
		    value_type, value);
Packit 345191
		emitter->item_at_depth = true;
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
/* Shorthand for calling emitter_json_key and then emitter_json_value. */
Packit 345191
static inline void
Packit 345191
emitter_json_kv(emitter_t *emitter, const char *json_key,
Packit 345191
    emitter_type_t value_type, const void *value) {
Packit 345191
	emitter_json_key(emitter, json_key);
Packit 345191
	emitter_json_value(emitter, value_type, value);
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_json_array_begin(emitter_t *emitter) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		emitter_json_key_prefix(emitter);
Packit 345191
		emitter_printf(emitter, "[");
Packit 345191
		emitter_nest_inc(emitter);
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
/* Shorthand for calling emitter_json_key and then emitter_json_array_begin. */
Packit 345191
static inline void
Packit 345191
emitter_json_array_kv_begin(emitter_t *emitter, const char *json_key) {
Packit 345191
	emitter_json_key(emitter, json_key);
Packit 345191
	emitter_json_array_begin(emitter);
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_json_array_end(emitter_t *emitter) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		assert(emitter->nesting_depth > 0);
Packit 345191
		emitter_nest_dec(emitter);
Packit 345191
		emitter_printf(emitter, "\n");
Packit 345191
		emitter_indent(emitter);
Packit 345191
		emitter_printf(emitter, "]");
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_json_object_begin(emitter_t *emitter) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		emitter_json_key_prefix(emitter);
Packit 345191
		emitter_printf(emitter, "{");
Packit 345191
		emitter_nest_inc(emitter);
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
/* Shorthand for calling emitter_json_key and then emitter_json_object_begin. */
Packit 345191
static inline void
Packit 345191
emitter_json_object_kv_begin(emitter_t *emitter, const char *json_key) {
Packit 345191
	emitter_json_key(emitter, json_key);
Packit 345191
	emitter_json_object_begin(emitter);
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_json_object_end(emitter_t *emitter) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		assert(emitter->nesting_depth > 0);
Packit 345191
		emitter_nest_dec(emitter);
Packit 345191
		emitter_printf(emitter, "\n");
Packit 345191
		emitter_indent(emitter);
Packit 345191
		emitter_printf(emitter, "}");
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
Packit 345191
/******************************************************************************/
Packit 345191
/* Table public API. */
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
Packit 345191
	if (emitter->output == emitter_output_table) {
Packit 345191
		emitter_indent(emitter);
Packit 345191
		emitter_printf(emitter, "%s\n", table_key);
Packit 345191
		emitter_nest_inc(emitter);
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_table_dict_end(emitter_t *emitter) {
Packit 345191
	if (emitter->output == emitter_output_table) {
Packit 345191
		emitter_nest_dec(emitter);
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_table_kv_note(emitter_t *emitter, const char *table_key,
Packit 345191
    emitter_type_t value_type, const void *value,
Packit 345191
    const char *table_note_key, emitter_type_t table_note_value_type,
Packit 345191
    const void *table_note_value) {
Packit 345191
	if (emitter->output == emitter_output_table) {
Packit 345191
		emitter_indent(emitter);
Packit 345191
		emitter_printf(emitter, "%s: ", table_key);
Packit 345191
		emitter_print_value(emitter, emitter_justify_none, -1,
Packit 345191
		    value_type, value);
Packit 345191
		if (table_note_key != NULL) {
Packit 345191
			emitter_printf(emitter, " (%s: ", table_note_key);
Packit 345191
			emitter_print_value(emitter, emitter_justify_none, -1,
Packit 345191
			    table_note_value_type, table_note_value);
Packit 345191
			emitter_printf(emitter, ")");
Packit 345191
		}
Packit 345191
		emitter_printf(emitter, "\n");
Packit 345191
	}
Packit 345191
	emitter->item_at_depth = true;
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_table_kv(emitter_t *emitter, const char *table_key,
Packit 345191
    emitter_type_t value_type, const void *value) {
Packit 345191
	emitter_table_kv_note(emitter, table_key, value_type, value, NULL,
Packit 345191
	    emitter_type_bool, NULL);
Packit 345191
}
Packit 345191
Packit 345191
Packit 345191
/* Write to the emitter the given string, but only in table mode. */
Packit 345191
JEMALLOC_FORMAT_PRINTF(2, 3)
Packit 345191
static inline void
Packit 345191
emitter_table_printf(emitter_t *emitter, const char *format, ...) {
Packit 345191
	if (emitter->output == emitter_output_table) {
Packit 345191
		va_list ap;
Packit 345191
		va_start(ap, format);
Packit 345191
		malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
Packit 345191
		va_end(ap);
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
Packit 345191
	if (emitter->output != emitter_output_table) {
Packit 345191
		return;
Packit 345191
	}
Packit 345191
	emitter_col_t *col;
Packit 345191
	ql_foreach(col, &row->cols, link) {
Packit 345191
		emitter_print_value(emitter, col->justify, col->width,
Packit 345191
		    col->type, (const void *)&col->bool_val);
Packit 345191
	}
Packit 345191
	emitter_table_printf(emitter, "\n");
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_row_init(emitter_row_t *row) {
Packit 345191
	ql_new(&row->cols);
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
Packit 345191
	ql_elm_new(col, link);
Packit 345191
	ql_tail_insert(&row->cols, col, link);
Packit 345191
}
Packit 345191
Packit 345191
Packit 345191
/******************************************************************************/
Packit 345191
/*
Packit 345191
 * Generalized public API. Emits using either JSON or table, according to
Packit 345191
 * settings in the emitter_t. */
Packit 345191
Packit 345191
/*
Packit 345191
 * Note emits a different kv pair as well, but only in table mode.  Omits the
Packit 345191
 * note if table_note_key is NULL.
Packit 345191
 */
Packit 345191
static inline void
Packit 345191
emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
Packit 345191
    emitter_type_t value_type, const void *value,
Packit 345191
    const char *table_note_key, emitter_type_t table_note_value_type,
Packit 345191
    const void *table_note_value) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		emitter_json_key(emitter, json_key);
Packit 345191
		emitter_json_value(emitter, value_type, value);
Packit 345191
	} else {
Packit 345191
		emitter_table_kv_note(emitter, table_key, value_type, value,
Packit 345191
		    table_note_key, table_note_value_type, table_note_value);
Packit 345191
	}
Packit 345191
	emitter->item_at_depth = true;
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
Packit 345191
    emitter_type_t value_type, const void *value) {
Packit 345191
	emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
Packit 345191
	    emitter_type_bool, NULL);
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_dict_begin(emitter_t *emitter, const char *json_key,
Packit 345191
    const char *table_header) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		emitter_json_key(emitter, json_key);
Packit 345191
		emitter_json_object_begin(emitter);
Packit 345191
	} else {
Packit 345191
		emitter_table_dict_begin(emitter, table_header);
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_dict_end(emitter_t *emitter) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		emitter_json_object_end(emitter);
Packit 345191
	} else {
Packit 345191
		emitter_table_dict_end(emitter);
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_begin(emitter_t *emitter) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		assert(emitter->nesting_depth == 0);
Packit 345191
		emitter_printf(emitter, "{");
Packit 345191
		emitter_nest_inc(emitter);
Packit 345191
	} else {
Packit 345191
		/*
Packit 345191
		 * This guarantees that we always call write_cb at least once.
Packit 345191
		 * This is useful if some invariant is established by each call
Packit 345191
		 * to write_cb, but doesn't hold initially: e.g., some buffer
Packit 345191
		 * holds a null-terminated string.
Packit 345191
		 */
Packit 345191
		emitter_printf(emitter, "%s", "");
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
static inline void
Packit 345191
emitter_end(emitter_t *emitter) {
Packit 345191
	if (emitter->output == emitter_output_json) {
Packit 345191
		assert(emitter->nesting_depth == 1);
Packit 345191
		emitter_nest_dec(emitter);
Packit 345191
		emitter_printf(emitter, "\n}\n");
Packit 345191
	}
Packit 345191
}
Packit 345191
Packit 345191
#endif /* JEMALLOC_INTERNAL_EMITTER_H */