Introduce the emitter module.
The emitter can be used to produce structured json or tabular output. For now it has no uses; in subsequent commits, I'll begin transitioning stats printing code over.
This commit is contained in:
parent
e4f090e8df
commit
27a8fe6780
@ -168,6 +168,7 @@ TESTS_UNIT := \
|
|||||||
$(srcroot)test/unit/ckh.c \
|
$(srcroot)test/unit/ckh.c \
|
||||||
$(srcroot)test/unit/decay.c \
|
$(srcroot)test/unit/decay.c \
|
||||||
$(srcroot)test/unit/div.c \
|
$(srcroot)test/unit/div.c \
|
||||||
|
$(srcroot)test/unit/emitter.c \
|
||||||
$(srcroot)test/unit/extent_quantize.c \
|
$(srcroot)test/unit/extent_quantize.c \
|
||||||
$(srcroot)test/unit/fork.c \
|
$(srcroot)test/unit/fork.c \
|
||||||
$(srcroot)test/unit/hash.c \
|
$(srcroot)test/unit/hash.c \
|
||||||
|
381
include/jemalloc/internal/emitter.h
Normal file
381
include/jemalloc/internal/emitter.h
Normal file
@ -0,0 +1,381 @@
|
|||||||
|
#ifndef JEMALLOC_INTERNAL_EMITTER_H
|
||||||
|
#define JEMALLOC_INTERNAL_EMITTER_H
|
||||||
|
|
||||||
|
typedef enum emitter_output_e emitter_output_t;
|
||||||
|
enum emitter_output_e {
|
||||||
|
emitter_output_json,
|
||||||
|
emitter_output_table
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum emitter_justify_e emitter_justify_t;
|
||||||
|
enum emitter_justify_e {
|
||||||
|
emitter_justify_left,
|
||||||
|
emitter_justify_right,
|
||||||
|
/* Not for users; just to pass to internal functions. */
|
||||||
|
emitter_justify_none
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum emitter_type_e emitter_type_t;
|
||||||
|
enum emitter_type_e {
|
||||||
|
emitter_type_bool,
|
||||||
|
emitter_type_int,
|
||||||
|
emitter_type_unsigned,
|
||||||
|
emitter_type_uint32,
|
||||||
|
emitter_type_uint64,
|
||||||
|
emitter_type_size,
|
||||||
|
emitter_type_ssize,
|
||||||
|
emitter_type_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct emitter_s emitter_t;
|
||||||
|
struct emitter_s {
|
||||||
|
emitter_output_t output;
|
||||||
|
/* The output information. */
|
||||||
|
void (*write_cb)(void *, const char *);
|
||||||
|
void *cbopaque;
|
||||||
|
int nesting_depth;
|
||||||
|
/* True if we've already emitted a value at the given depth. */
|
||||||
|
bool item_at_depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
|
||||||
|
void (*write_cb)(void *, const char *), void *cbopaque) {
|
||||||
|
emitter->output = emitter_output;
|
||||||
|
emitter->write_cb = write_cb;
|
||||||
|
emitter->cbopaque = cbopaque;
|
||||||
|
emitter->item_at_depth = false;
|
||||||
|
emitter->nesting_depth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Internal convenience function. Write to the emitter the given string. */
|
||||||
|
JEMALLOC_FORMAT_PRINTF(2, 3)
|
||||||
|
static inline void
|
||||||
|
emitter_printf(emitter_t *emitter, const char *format, ...) {
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write to the emitter the given string, but only in table mode. */
|
||||||
|
JEMALLOC_FORMAT_PRINTF(2, 3)
|
||||||
|
static inline void
|
||||||
|
emitter_table_printf(emitter_t *emitter, const char *format, ...) {
|
||||||
|
if (emitter->output == emitter_output_table) {
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
|
||||||
|
emitter_justify_t justify, int width) {
|
||||||
|
size_t written;
|
||||||
|
if (justify == emitter_justify_none) {
|
||||||
|
written = malloc_snprintf(out_fmt, out_size,
|
||||||
|
"%%%s", fmt_specifier);
|
||||||
|
} else if (justify == emitter_justify_left) {
|
||||||
|
written = malloc_snprintf(out_fmt, out_size,
|
||||||
|
"%%-%d%s", width, fmt_specifier);
|
||||||
|
} else {
|
||||||
|
written = malloc_snprintf(out_fmt, out_size,
|
||||||
|
"%%%d%s", width, fmt_specifier);
|
||||||
|
}
|
||||||
|
/* Only happens in case of bad format string, which *we* choose. */
|
||||||
|
assert(written < out_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal. Emit the given value type in the relevant encoding (so that the
|
||||||
|
* bool true gets mapped to json "true", but the string "true" gets mapped to
|
||||||
|
* json "\"true\"", for instance.
|
||||||
|
*
|
||||||
|
* Width is ignored if justify is emitter_justify_none.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
|
||||||
|
emitter_type_t value_type, const void *value) {
|
||||||
|
size_t str_written;
|
||||||
|
#define BUF_SIZE 256
|
||||||
|
#define FMT_SIZE 10
|
||||||
|
/*
|
||||||
|
* We dynamically generate a format string to emit, to let us use the
|
||||||
|
* snprintf machinery. This is kinda hacky, but gets the job done
|
||||||
|
* quickly without having to think about the various snprintf edge
|
||||||
|
* cases.
|
||||||
|
*/
|
||||||
|
char fmt[FMT_SIZE];
|
||||||
|
char buf[BUF_SIZE];
|
||||||
|
|
||||||
|
#define EMIT_SIMPLE(type, format) \
|
||||||
|
emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width); \
|
||||||
|
emitter_printf(emitter, fmt, *(const type *)value); \
|
||||||
|
|
||||||
|
switch (value_type) {
|
||||||
|
case emitter_type_bool:
|
||||||
|
emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
|
||||||
|
emitter_printf(emitter, fmt, *(const bool *)value ?
|
||||||
|
"true" : "false");
|
||||||
|
break;
|
||||||
|
case emitter_type_int:
|
||||||
|
EMIT_SIMPLE(int, "d")
|
||||||
|
break;
|
||||||
|
case emitter_type_unsigned:
|
||||||
|
EMIT_SIMPLE(unsigned, "u")
|
||||||
|
break;
|
||||||
|
case emitter_type_ssize:
|
||||||
|
EMIT_SIMPLE(ssize_t, "zd")
|
||||||
|
break;
|
||||||
|
case emitter_type_size:
|
||||||
|
EMIT_SIMPLE(size_t, "zu")
|
||||||
|
break;
|
||||||
|
case emitter_type_string:
|
||||||
|
str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
|
||||||
|
*(const char *const *)value);
|
||||||
|
/*
|
||||||
|
* We control the strings we output; we shouldn't get anything
|
||||||
|
* anywhere near the fmt size.
|
||||||
|
*/
|
||||||
|
assert(str_written < BUF_SIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't support justified quoted string primitive values for
|
||||||
|
* now. Fortunately, we don't want to emit them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
emitter_gen_fmt(fmt, FMT_SIZE, "s", justify, width);
|
||||||
|
emitter_printf(emitter, fmt, buf);
|
||||||
|
break;
|
||||||
|
case emitter_type_uint32:
|
||||||
|
EMIT_SIMPLE(uint32_t, FMTu32)
|
||||||
|
break;
|
||||||
|
case emitter_type_uint64:
|
||||||
|
EMIT_SIMPLE(uint64_t, FMTu64)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
#undef BUF_SIZE
|
||||||
|
#undef FMT_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Internal functions. In json mode, tracks nesting state. */
|
||||||
|
static inline void
|
||||||
|
emitter_nest_inc(emitter_t *emitter) {
|
||||||
|
emitter->nesting_depth++;
|
||||||
|
emitter->item_at_depth = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_nest_dec(emitter_t *emitter) {
|
||||||
|
emitter->nesting_depth--;
|
||||||
|
emitter->item_at_depth = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_indent(emitter_t *emitter) {
|
||||||
|
int amount = emitter->nesting_depth;
|
||||||
|
const char *indent_str;
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
indent_str = "\t";
|
||||||
|
} else {
|
||||||
|
amount *= 2;
|
||||||
|
indent_str = " ";
|
||||||
|
}
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
emitter_printf(emitter, "%s", indent_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_key_prefix(emitter_t *emitter) {
|
||||||
|
emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
|
||||||
|
emitter_indent(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_begin(emitter_t *emitter) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
assert(emitter->nesting_depth == 0);
|
||||||
|
emitter_printf(emitter, "{");
|
||||||
|
emitter_nest_inc(emitter);
|
||||||
|
} else {
|
||||||
|
// tabular init
|
||||||
|
emitter_printf(emitter, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_end(emitter_t *emitter) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
assert(emitter->nesting_depth == 1);
|
||||||
|
emitter_nest_dec(emitter);
|
||||||
|
emitter_printf(emitter, "\n}\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note emits a different kv pair as well, but only in table mode. Omits the
|
||||||
|
* note if table_note_key is NULL.
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
assert(emitter->nesting_depth > 0);
|
||||||
|
emitter_json_key_prefix(emitter);
|
||||||
|
emitter_printf(emitter, "\"%s\": ", json_key);
|
||||||
|
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||||
|
value_type, value);
|
||||||
|
} else {
|
||||||
|
emitter_indent(emitter);
|
||||||
|
emitter_printf(emitter, "%s: ", table_key);
|
||||||
|
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||||
|
value_type, value);
|
||||||
|
if (table_note_key != NULL) {
|
||||||
|
emitter_printf(emitter, " (%s: ", table_note_key);
|
||||||
|
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||||
|
table_note_value_type, table_note_value);
|
||||||
|
emitter_printf(emitter, ")");
|
||||||
|
}
|
||||||
|
emitter_printf(emitter, "\n");
|
||||||
|
}
|
||||||
|
emitter->item_at_depth = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
|
||||||
|
emitter_type_t value_type, const void *value) {
|
||||||
|
emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
|
||||||
|
emitter_type_bool, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_kv(emitter_t *emitter, const char *json_key,
|
||||||
|
emitter_type_t value_type, const void *value) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
emitter_kv(emitter, json_key, NULL, value_type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_table_kv(emitter_t *emitter, const char *table_key,
|
||||||
|
emitter_type_t value_type, const void *value) {
|
||||||
|
if (emitter->output == emitter_output_table) {
|
||||||
|
emitter_kv(emitter, NULL, table_key, value_type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_dict_begin(emitter_t *emitter, const char *json_key,
|
||||||
|
const char *table_header) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
emitter_json_key_prefix(emitter);
|
||||||
|
emitter_printf(emitter, "\"%s\": {", json_key);
|
||||||
|
emitter_nest_inc(emitter);
|
||||||
|
} else {
|
||||||
|
emitter_indent(emitter);
|
||||||
|
emitter_printf(emitter, "%s\n", table_header);
|
||||||
|
emitter_nest_inc(emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_dict_end(emitter_t *emitter) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
assert(emitter->nesting_depth > 0);
|
||||||
|
emitter_nest_dec(emitter);
|
||||||
|
emitter_printf(emitter, "\n");
|
||||||
|
emitter_indent(emitter);
|
||||||
|
emitter_printf(emitter, "}");
|
||||||
|
} else {
|
||||||
|
emitter_nest_dec(emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_dict_begin(emitter_t *emitter, const char *json_key) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
emitter_dict_begin(emitter, json_key, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_dict_end(emitter_t *emitter) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
emitter_dict_end(emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
|
||||||
|
if (emitter->output == emitter_output_table) {
|
||||||
|
emitter_dict_begin(emitter, NULL, table_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_table_dict_end(emitter_t *emitter) {
|
||||||
|
if (emitter->output == emitter_output_table) {
|
||||||
|
emitter_dict_end(emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_arr_begin(emitter_t *emitter, const char *json_key) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
emitter_json_key_prefix(emitter);
|
||||||
|
emitter_printf(emitter, "\"%s\": [", json_key);
|
||||||
|
emitter_nest_inc(emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_arr_end(emitter_t *emitter) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
assert(emitter->nesting_depth > 0);
|
||||||
|
emitter_nest_dec(emitter);
|
||||||
|
emitter_printf(emitter, "\n");
|
||||||
|
emitter_indent(emitter);
|
||||||
|
emitter_printf(emitter, "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_arr_obj_begin(emitter_t *emitter) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
emitter_json_key_prefix(emitter);
|
||||||
|
emitter_printf(emitter, "{");
|
||||||
|
emitter_nest_inc(emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_arr_obj_end(emitter_t *emitter) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
assert(emitter->nesting_depth > 0);
|
||||||
|
emitter_nest_dec(emitter);
|
||||||
|
emitter_printf(emitter, "\n");
|
||||||
|
emitter_indent(emitter);
|
||||||
|
emitter_printf(emitter, "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
emitter_json_arr_value(emitter_t *emitter, emitter_type_t value_type,
|
||||||
|
const void *value) {
|
||||||
|
if (emitter->output == emitter_output_json) {
|
||||||
|
emitter_json_key_prefix(emitter);
|
||||||
|
emitter_print_value(emitter, emitter_justify_none, -1,
|
||||||
|
value_type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* JEMALLOC_INTERNAL_EMITTER_H */
|
351
test/unit/emitter.c
Normal file
351
test/unit/emitter.c
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
#include "test/jemalloc_test.h"
|
||||||
|
#include "jemalloc/internal/emitter.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is so useful for debugging and feature work, we'll leave printing
|
||||||
|
* functionality committed but disabled by default.
|
||||||
|
*/
|
||||||
|
/* Print the text as it will appear. */
|
||||||
|
static bool print_raw = false;
|
||||||
|
/* Print the text escaped, so it can be copied back into the test case. */
|
||||||
|
static bool print_escaped = false;
|
||||||
|
|
||||||
|
typedef struct buf_descriptor_s buf_descriptor_t;
|
||||||
|
struct buf_descriptor_s {
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
bool mid_quote;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Forwards all writes to the passed-in buf_v (which should be cast from a
|
||||||
|
* buf_descriptor_t *).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
forwarding_cb(void *buf_descriptor_v, const char *str) {
|
||||||
|
buf_descriptor_t *buf_descriptor = (buf_descriptor_t *)buf_descriptor_v;
|
||||||
|
|
||||||
|
if (print_raw) {
|
||||||
|
malloc_printf("%s", str);
|
||||||
|
}
|
||||||
|
if (print_escaped) {
|
||||||
|
const char *it = str;
|
||||||
|
while (*it != '\0') {
|
||||||
|
if (!buf_descriptor->mid_quote) {
|
||||||
|
malloc_printf("\"");
|
||||||
|
buf_descriptor->mid_quote = true;
|
||||||
|
}
|
||||||
|
switch (*it) {
|
||||||
|
case '\\':
|
||||||
|
malloc_printf("\\");
|
||||||
|
break;
|
||||||
|
case '\"':
|
||||||
|
malloc_printf("\\\"");
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
malloc_printf("\\t");
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
malloc_printf("\\n\"\n");
|
||||||
|
buf_descriptor->mid_quote = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
malloc_printf("%c", *it);
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t written = malloc_snprintf(buf_descriptor->buf,
|
||||||
|
buf_descriptor->len, "%s", str);
|
||||||
|
assert_zu_eq(written, strlen(str), "Buffer overflow!");
|
||||||
|
buf_descriptor->buf += written;
|
||||||
|
buf_descriptor->len -= written;
|
||||||
|
assert_zu_gt(buf_descriptor->len, 0, "Buffer out of space!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
assert_emit_output(void (*emit_fn)(emitter_t *),
|
||||||
|
const char *expected_json_output, const char *expected_table_output) {
|
||||||
|
emitter_t emitter;
|
||||||
|
char buf[MALLOC_PRINTF_BUFSIZE];
|
||||||
|
buf_descriptor_t buf_descriptor;
|
||||||
|
|
||||||
|
buf_descriptor.buf = buf;
|
||||||
|
buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
|
||||||
|
buf_descriptor.mid_quote = false;
|
||||||
|
|
||||||
|
emitter_init(&emitter, emitter_output_json, &forwarding_cb,
|
||||||
|
&buf_descriptor);
|
||||||
|
(*emit_fn)(&emitter);
|
||||||
|
assert_str_eq(expected_json_output, buf, "json output failure");
|
||||||
|
|
||||||
|
buf_descriptor.buf = buf;
|
||||||
|
buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
|
||||||
|
buf_descriptor.mid_quote = false;
|
||||||
|
|
||||||
|
emitter_init(&emitter, emitter_output_table, &forwarding_cb,
|
||||||
|
&buf_descriptor);
|
||||||
|
(*emit_fn)(&emitter);
|
||||||
|
assert_str_eq(expected_table_output, buf, "table output failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_dict(emitter_t *emitter) {
|
||||||
|
bool b_false = false;
|
||||||
|
bool b_true = true;
|
||||||
|
int i_123 = 123;
|
||||||
|
const char *str = "a string";
|
||||||
|
|
||||||
|
emitter_begin(emitter);
|
||||||
|
emitter_dict_begin(emitter, "foo", "This is the foo table:");
|
||||||
|
emitter_kv(emitter, "abc", "ABC", emitter_type_bool, &b_false);
|
||||||
|
emitter_kv(emitter, "def", "DEF", emitter_type_bool, &b_true);
|
||||||
|
emitter_kv_note(emitter, "ghi", "GHI", emitter_type_int, &i_123,
|
||||||
|
"note_key1", emitter_type_string, &str);
|
||||||
|
emitter_kv_note(emitter, "jkl", "JKL", emitter_type_string, &str,
|
||||||
|
"note_key2", emitter_type_bool, &b_false);
|
||||||
|
emitter_dict_end(emitter);
|
||||||
|
emitter_end(emitter);
|
||||||
|
}
|
||||||
|
static const char *dict_json =
|
||||||
|
"{\n"
|
||||||
|
"\t\"foo\": {\n"
|
||||||
|
"\t\t\"abc\": false,\n"
|
||||||
|
"\t\t\"def\": true,\n"
|
||||||
|
"\t\t\"ghi\": 123,\n"
|
||||||
|
"\t\t\"jkl\": \"a string\"\n"
|
||||||
|
"\t}\n"
|
||||||
|
"}\n";
|
||||||
|
static const char *dict_table =
|
||||||
|
"This is the foo table:\n"
|
||||||
|
" ABC: false\n"
|
||||||
|
" DEF: true\n"
|
||||||
|
" GHI: 123 (note_key1: \"a string\")\n"
|
||||||
|
" JKL: \"a string\" (note_key2: false)\n";
|
||||||
|
|
||||||
|
TEST_BEGIN(test_dict) {
|
||||||
|
assert_emit_output(&emit_dict, dict_json, dict_table);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_table_printf(emitter_t *emitter) {
|
||||||
|
emitter_begin(emitter);
|
||||||
|
emitter_table_printf(emitter, "Table note 1\n");
|
||||||
|
emitter_table_printf(emitter, "Table note 2 %s\n",
|
||||||
|
"with format string");
|
||||||
|
emitter_end(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *table_printf_json =
|
||||||
|
"{\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char *table_printf_table =
|
||||||
|
"Table note 1\n"
|
||||||
|
"Table note 2 with format string\n";
|
||||||
|
|
||||||
|
TEST_BEGIN(test_table_printf) {
|
||||||
|
assert_emit_output(&emit_table_printf, table_printf_json,
|
||||||
|
table_printf_table);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static void emit_nested_dict(emitter_t *emitter) {
|
||||||
|
int val = 123;
|
||||||
|
emitter_begin(emitter);
|
||||||
|
emitter_dict_begin(emitter, "json1", "Dict 1");
|
||||||
|
emitter_dict_begin(emitter, "json2", "Dict 2");
|
||||||
|
emitter_kv(emitter, "primitive", "A primitive", emitter_type_int, &val);
|
||||||
|
emitter_dict_end(emitter); /* Close 2 */
|
||||||
|
emitter_dict_begin(emitter, "json3", "Dict 3");
|
||||||
|
emitter_dict_end(emitter); /* Close 3 */
|
||||||
|
emitter_dict_end(emitter); /* Close 1 */
|
||||||
|
emitter_dict_begin(emitter, "json4", "Dict 4");
|
||||||
|
emitter_kv(emitter, "primitive", "Another primitive",
|
||||||
|
emitter_type_int, &val);
|
||||||
|
emitter_dict_end(emitter); /* Close 4 */
|
||||||
|
emitter_end(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *nested_dict_json =
|
||||||
|
"{\n"
|
||||||
|
"\t\"json1\": {\n"
|
||||||
|
"\t\t\"json2\": {\n"
|
||||||
|
"\t\t\t\"primitive\": 123\n"
|
||||||
|
"\t\t},\n"
|
||||||
|
"\t\t\"json3\": {\n"
|
||||||
|
"\t\t}\n"
|
||||||
|
"\t},\n"
|
||||||
|
"\t\"json4\": {\n"
|
||||||
|
"\t\t\"primitive\": 123\n"
|
||||||
|
"\t}\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char *nested_dict_table =
|
||||||
|
"Dict 1\n"
|
||||||
|
" Dict 2\n"
|
||||||
|
" A primitive: 123\n"
|
||||||
|
" Dict 3\n"
|
||||||
|
"Dict 4\n"
|
||||||
|
" Another primitive: 123\n";
|
||||||
|
|
||||||
|
TEST_BEGIN(test_nested_dict) {
|
||||||
|
assert_emit_output(&emit_nested_dict, nested_dict_json,
|
||||||
|
nested_dict_table);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_types(emitter_t *emitter) {
|
||||||
|
bool b = false;
|
||||||
|
int i = -123;
|
||||||
|
unsigned u = 123;
|
||||||
|
ssize_t zd = -456;
|
||||||
|
size_t zu = 456;
|
||||||
|
const char *str = "string";
|
||||||
|
uint32_t u32 = 789;
|
||||||
|
uint64_t u64 = 10000000000ULL;
|
||||||
|
|
||||||
|
emitter_begin(emitter);
|
||||||
|
emitter_kv(emitter, "k1", "K1", emitter_type_bool, &b);
|
||||||
|
emitter_kv(emitter, "k2", "K2", emitter_type_int, &i);
|
||||||
|
emitter_kv(emitter, "k3", "K3", emitter_type_unsigned, &u);
|
||||||
|
emitter_kv(emitter, "k4", "K4", emitter_type_ssize, &zd);
|
||||||
|
emitter_kv(emitter, "k5", "K5", emitter_type_size, &zu);
|
||||||
|
emitter_kv(emitter, "k6", "K6", emitter_type_string, &str);
|
||||||
|
emitter_kv(emitter, "k7", "K7", emitter_type_uint32, &u32);
|
||||||
|
emitter_kv(emitter, "k8", "K8", emitter_type_uint64, &u64);
|
||||||
|
emitter_end(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *types_json =
|
||||||
|
"{\n"
|
||||||
|
"\t\"k1\": false,\n"
|
||||||
|
"\t\"k2\": -123,\n"
|
||||||
|
"\t\"k3\": 123,\n"
|
||||||
|
"\t\"k4\": -456,\n"
|
||||||
|
"\t\"k5\": 456,\n"
|
||||||
|
"\t\"k6\": \"string\",\n"
|
||||||
|
"\t\"k7\": 789,\n"
|
||||||
|
"\t\"k8\": 10000000000\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char *types_table =
|
||||||
|
"K1: false\n"
|
||||||
|
"K2: -123\n"
|
||||||
|
"K3: 123\n"
|
||||||
|
"K4: -456\n"
|
||||||
|
"K5: 456\n"
|
||||||
|
"K6: \"string\"\n"
|
||||||
|
"K7: 789\n"
|
||||||
|
"K8: 10000000000\n";
|
||||||
|
|
||||||
|
TEST_BEGIN(test_types) {
|
||||||
|
assert_emit_output(&emit_types, types_json, types_table);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_modal(emitter_t *emitter) {
|
||||||
|
int val = 123;
|
||||||
|
emitter_begin(emitter);
|
||||||
|
emitter_dict_begin(emitter, "j0", "T0");
|
||||||
|
emitter_json_dict_begin(emitter, "j1");
|
||||||
|
emitter_kv(emitter, "i1", "I1", emitter_type_int, &val);
|
||||||
|
emitter_json_kv(emitter, "i2", emitter_type_int, &val);
|
||||||
|
emitter_table_kv(emitter, "I3", emitter_type_int, &val);
|
||||||
|
emitter_table_dict_begin(emitter, "T1");
|
||||||
|
emitter_kv(emitter, "i4", "I4", emitter_type_int, &val);
|
||||||
|
emitter_json_dict_end(emitter); /* Close j1 */
|
||||||
|
emitter_kv(emitter, "i5", "I5", emitter_type_int, &val);
|
||||||
|
emitter_table_dict_end(emitter); /* Close T1 */
|
||||||
|
emitter_kv(emitter, "i6", "I6", emitter_type_int, &val);
|
||||||
|
emitter_dict_end(emitter); /* Close j0 / T0 */
|
||||||
|
emitter_end(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *modal_json =
|
||||||
|
"{\n"
|
||||||
|
"\t\"j0\": {\n"
|
||||||
|
"\t\t\"j1\": {\n"
|
||||||
|
"\t\t\t\"i1\": 123,\n"
|
||||||
|
"\t\t\t\"i2\": 123,\n"
|
||||||
|
"\t\t\t\"i4\": 123\n"
|
||||||
|
"\t\t},\n"
|
||||||
|
"\t\t\"i5\": 123,\n"
|
||||||
|
"\t\t\"i6\": 123\n"
|
||||||
|
"\t}\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const char *modal_table =
|
||||||
|
"T0\n"
|
||||||
|
" I1: 123\n"
|
||||||
|
" I3: 123\n"
|
||||||
|
" T1\n"
|
||||||
|
" I4: 123\n"
|
||||||
|
" I5: 123\n"
|
||||||
|
" I6: 123\n";
|
||||||
|
|
||||||
|
TEST_BEGIN(test_modal) {
|
||||||
|
assert_emit_output(&emit_modal, modal_json, modal_table);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
emit_json_arr(emitter_t *emitter) {
|
||||||
|
int ival = 123;
|
||||||
|
|
||||||
|
emitter_begin(emitter);
|
||||||
|
emitter_json_dict_begin(emitter, "dict");
|
||||||
|
emitter_json_arr_begin(emitter, "arr");
|
||||||
|
emitter_json_arr_obj_begin(emitter);
|
||||||
|
emitter_json_kv(emitter, "foo", emitter_type_int, &ival);
|
||||||
|
emitter_json_arr_obj_end(emitter); /* Close arr[0] */
|
||||||
|
/* arr[1] and arr[2] are primitives. */
|
||||||
|
emitter_json_arr_value(emitter, emitter_type_int, &ival);
|
||||||
|
emitter_json_arr_value(emitter, emitter_type_int, &ival);
|
||||||
|
emitter_json_arr_obj_begin(emitter);
|
||||||
|
emitter_json_kv(emitter, "bar", emitter_type_int, &ival);
|
||||||
|
emitter_json_kv(emitter, "baz", emitter_type_int, &ival);
|
||||||
|
emitter_json_arr_obj_end(emitter); /* Close arr[3]. */
|
||||||
|
emitter_json_arr_end(emitter); /* Close arr. */
|
||||||
|
emitter_json_dict_end(emitter); /* Close dict. */
|
||||||
|
emitter_end(emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *json_arr_json =
|
||||||
|
"{\n"
|
||||||
|
"\t\"dict\": {\n"
|
||||||
|
"\t\t\"arr\": [\n"
|
||||||
|
"\t\t\t{\n"
|
||||||
|
"\t\t\t\t\"foo\": 123\n"
|
||||||
|
"\t\t\t},\n"
|
||||||
|
"\t\t\t123,\n"
|
||||||
|
"\t\t\t123,\n"
|
||||||
|
"\t\t\t{\n"
|
||||||
|
"\t\t\t\t\"bar\": 123,\n"
|
||||||
|
"\t\t\t\t\"baz\": 123\n"
|
||||||
|
"\t\t\t}\n"
|
||||||
|
"\t\t]\n"
|
||||||
|
"\t}\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
static const char *json_arr_table = "";
|
||||||
|
|
||||||
|
TEST_BEGIN(test_json_arr) {
|
||||||
|
assert_emit_output(&emit_json_arr, json_arr_json, json_arr_table);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void) {
|
||||||
|
return test_no_reentrancy(
|
||||||
|
test_dict,
|
||||||
|
test_table_printf,
|
||||||
|
test_nested_dict,
|
||||||
|
test_types,
|
||||||
|
test_modal,
|
||||||
|
test_json_arr);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user