#include "test/jemalloc_test.h"
#include "jemalloc/internal/util.h"
typedef enum {
TOKEN_TYPE_NONE,
TOKEN_TYPE_ERROR,
TOKEN_TYPE_EOI,
TOKEN_TYPE_NULL,
TOKEN_TYPE_FALSE,
TOKEN_TYPE_TRUE,
TOKEN_TYPE_LBRACKET,
TOKEN_TYPE_RBRACKET,
TOKEN_TYPE_LBRACE,
TOKEN_TYPE_RBRACE,
TOKEN_TYPE_COLON,
TOKEN_TYPE_COMMA,
TOKEN_TYPE_STRING,
TOKEN_TYPE_NUMBER
} token_type_t;
typedef struct parser_s parser_t;
typedef struct {
parser_t *parser;
token_type_t token_type;
size_t pos;
size_t len;
size_t line;
size_t col;
} token_t;
struct parser_s {
bool verbose;
char *buf; /* '\0'-terminated. */
size_t len; /* Number of characters preceding '\0' in buf. */
size_t pos;
size_t line;
size_t col;
token_t token;
};
static void
token_init(token_t *token, parser_t *parser, token_type_t token_type,
size_t pos, size_t len, size_t line, size_t col) {
token->parser = parser;
token->token_type = token_type;
token->pos = pos;
token->len = len;
token->line = line;
token->col = col;
}
static void
token_error(token_t *token) {
if (!token->parser->verbose) {
return;
}
switch (token->token_type) {
case TOKEN_TYPE_NONE:
not_reached();
case TOKEN_TYPE_ERROR:
malloc_printf("%zu:%zu: Unexpected character in token: ",
token->line, token->col);
break;
default:
malloc_printf("%zu:%zu: Unexpected token: ", token->line,
token->col);
break;
}
UNUSED ssize_t err = malloc_write_fd(STDERR_FILENO,
&token->parser->buf[token->pos], token->len);
malloc_printf("\n");
}
static void
parser_init(parser_t *parser, bool verbose) {
parser->verbose = verbose;
parser->buf = NULL;
parser->len = 0;
parser->pos = 0;
parser->line = 1;
parser->col = 0;
}
static void
parser_fini(parser_t *parser) {
if (parser->buf != NULL) {
dallocx(parser->buf, MALLOCX_TCACHE_NONE);
}
}
static bool
parser_append(parser_t *parser, const char *str) {
size_t len = strlen(str);
char *buf = (parser->buf == NULL) ? mallocx(len + 1,
MALLOCX_TCACHE_NONE) : rallocx(parser->buf, parser->len + len + 1,
MALLOCX_TCACHE_NONE);
if (buf == NULL) {
return true;
}
memcpy(&buf[parser->len], str, len + 1);
parser->buf = buf;
parser->len += len;
return false;
}
static bool
parser_tokenize(parser_t *parser) {
enum {
STATE_START,
STATE_EOI,
STATE_N, STATE_NU, STATE_NUL, STATE_NULL,
STATE_F, STATE_FA, STATE_FAL, STATE_FALS, STATE_FALSE,
STATE_T, STATE_TR, STATE_TRU, STATE_TRUE,
STATE_LBRACKET,
STATE_RBRACKET,
STATE_LBRACE,
STATE_RBRACE,
STATE_COLON,
STATE_COMMA,
STATE_CHARS,
STATE_CHAR_ESCAPE,
STATE_CHAR_U, STATE_CHAR_UD, STATE_CHAR_UDD, STATE_CHAR_UDDD,
STATE_STRING,
STATE_MINUS,
STATE_LEADING_ZERO,
STATE_DIGITS,
STATE_DECIMAL,
STATE_FRAC_DIGITS,
STATE_EXP,
STATE_EXP_SIGN,
STATE_EXP_DIGITS,
STATE_ACCEPT
} state = STATE_START;
size_t token_pos JEMALLOC_CC_SILENCE_INIT(0);
size_t token_line JEMALLOC_CC_SILENCE_INIT(1);
size_t token_col JEMALLOC_CC_SILENCE_INIT(0);
assert_zu_le(parser->pos, parser->len,
"Position is past end of buffer");
while (state != STATE_ACCEPT) {
char c = parser->buf[parser->pos];
switch (state) {
case STATE_START:
token_pos = parser->pos;
token_line = parser->line;
token_col = parser->col;
switch (c) {
case ' ': case '\b': case '\n': case '\r': case '\t':
break;
case '\0':
state = STATE_EOI;
break;
case 'n':
state = STATE_N;
break;
case 'f':
state = STATE_F;
break;
case 't':
state = STATE_T;
break;
case '[':
state = STATE_LBRACKET;
break;
case ']':
state = STATE_RBRACKET;
break;
case '{':
state = STATE_LBRACE;
break;
case '}':
state = STATE_RBRACE;
break;
case ':':
state = STATE_COLON;
break;
case ',':
state = STATE_COMMA;
break;
case '"':
state = STATE_CHARS;
break;
case '-':
state = STATE_MINUS;
break;
case '0':
state = STATE_LEADING_ZERO;
break;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
state = STATE_DIGITS;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_EOI:
token_init(&parser->token, parser,
TOKEN_TYPE_EOI, token_pos, parser->pos -
token_pos, token_line, token_col);
state = STATE_ACCEPT;
break;
case STATE_N:
switch (c) {
case 'u':
state = STATE_NU;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_NU:
switch (c) {
case 'l':
state = STATE_NUL;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_NUL:
switch (c) {
case 'l':
state = STATE_NULL;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_NULL:
switch (c) {
case ' ': case '\b': case '\n': case '\r': case '\t':
case '\0':
case '[': case ']': case '{': case '}': case ':':
case ',':
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
token_init(&parser->token, parser, TOKEN_TYPE_NULL,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_F:
switch (c) {
case 'a':
state = STATE_FA;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_FA:
switch (c) {
case 'l':
state = STATE_FAL;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_FAL:
switch (c) {
case 's':
state = STATE_FALS;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_FALS:
switch (c) {
case 'e':
state = STATE_FALSE;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_FALSE:
switch (c) {
case ' ': case '\b': case '\n': case '\r': case '\t':
case '\0':
case '[': case ']': case '{': case '}': case ':':
case ',':
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
token_init(&parser->token, parser,
TOKEN_TYPE_FALSE, token_pos, parser->pos -
token_pos, token_line, token_col);
state = STATE_ACCEPT;
break;
case STATE_T:
switch (c) {
case 'r':
state = STATE_TR;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_TR:
switch (c) {
case 'u':
state = STATE_TRU;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_TRU:
switch (c) {
case 'e':
state = STATE_TRUE;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_TRUE:
switch (c) {
case ' ': case '\b': case '\n': case '\r': case '\t':
case '\0':
case '[': case ']': case '{': case '}': case ':':
case ',':
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
token_init(&parser->token, parser, TOKEN_TYPE_TRUE,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_LBRACKET:
token_init(&parser->token, parser, TOKEN_TYPE_LBRACKET,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_RBRACKET:
token_init(&parser->token, parser, TOKEN_TYPE_RBRACKET,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_LBRACE:
token_init(&parser->token, parser, TOKEN_TYPE_LBRACE,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_RBRACE:
token_init(&parser->token, parser, TOKEN_TYPE_RBRACE,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_COLON:
token_init(&parser->token, parser, TOKEN_TYPE_COLON,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_COMMA:
token_init(&parser->token, parser, TOKEN_TYPE_COMMA,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_CHARS:
switch (c) {
case '\\':
state = STATE_CHAR_ESCAPE;
break;
case '"':
state = STATE_STRING;
break;
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04:
case 0x05: case 0x06: case 0x07: case 0x08: case 0x09:
case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e:
case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13:
case 0x14: case 0x15: case 0x16: case 0x17: case 0x18:
case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d:
case 0x1e: case 0x1f:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
default:
break;
}
break;
case STATE_CHAR_ESCAPE:
switch (c) {
case '"': case '\\': case '/': case 'b': case 'n':
case 'r': case 't':
state = STATE_CHARS;
break;
case 'u':
state = STATE_CHAR_U;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_CHAR_U:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f':
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F':
state = STATE_CHAR_UD;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_CHAR_UD:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f':
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F':
state = STATE_CHAR_UDD;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_CHAR_UDD:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f':
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F':
state = STATE_CHAR_UDDD;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_CHAR_UDDD:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f':
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F':
state = STATE_CHARS;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_STRING:
token_init(&parser->token, parser, TOKEN_TYPE_STRING,
token_pos, parser->pos - token_pos, token_line,
token_col);
state = STATE_ACCEPT;
break;
case STATE_MINUS:
switch (c) {
case '0':
state = STATE_LEADING_ZERO;
break;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
state = STATE_DIGITS;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_LEADING_ZERO:
switch (c) {
case '.':
state = STATE_DECIMAL;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_NUMBER, token_pos, parser->pos -
token_pos, token_line, token_col);
state = STATE_ACCEPT;
break;
}
break;
case STATE_DIGITS:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break;
case '.':
state = STATE_DECIMAL;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_NUMBER, token_pos, parser->pos -
token_pos, token_line, token_col);
state = STATE_ACCEPT;
break;
}
break;
case STATE_DECIMAL:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
state = STATE_FRAC_DIGITS;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_FRAC_DIGITS:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break;
case 'e': case 'E':
state = STATE_EXP;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_NUMBER, token_pos, parser->pos -
token_pos, token_line, token_col);
state = STATE_ACCEPT;
break;
}
break;
case STATE_EXP:
switch (c) {
case '-': case '+':
state = STATE_EXP_SIGN;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
state = STATE_EXP_DIGITS;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_EXP_SIGN:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
state = STATE_EXP_DIGITS;
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_ERROR, token_pos, parser->pos + 1
- token_pos, token_line, token_col);
return true;
}
break;
case STATE_EXP_DIGITS:
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break;
default:
token_init(&parser->token, parser,
TOKEN_TYPE_NUMBER, token_pos, parser->pos -
token_pos, token_line, token_col);
state = STATE_ACCEPT;
break;
}
break;
default:
not_reached();
}
if (state != STATE_ACCEPT) {
if (c == '\n') {
parser->line++;
parser->col = 0;
} else {
parser->col++;
}
parser->pos++;
}
}
return false;
}
static bool parser_parse_array(parser_t *parser);
static bool parser_parse_object(parser_t *parser);
static bool
parser_parse_value(parser_t *parser) {
switch (parser->token.token_type) {
case TOKEN_TYPE_NULL:
case TOKEN_TYPE_FALSE:
case TOKEN_TYPE_TRUE:
case TOKEN_TYPE_STRING:
case TOKEN_TYPE_NUMBER:
return false;
case TOKEN_TYPE_LBRACE:
return parser_parse_object(parser);
case TOKEN_TYPE_LBRACKET:
return parser_parse_array(parser);
default:
return true;
}
not_reached();
}
static bool
parser_parse_pair(parser_t *parser) {
assert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,
"Pair should start with string");
if (parser_tokenize(parser)) {
return true;
}
switch (parser->token.token_type) {
case TOKEN_TYPE_COLON:
if (parser_tokenize(parser)) {
return true;
}
return parser_parse_value(parser);
default:
return true;
}
}
static bool
parser_parse_values(parser_t *parser) {
if (parser_parse_value(parser)) {
return true;
}
while (true) {
if (parser_tokenize(parser)) {
return true;
}
switch (parser->token.token_type) {
case TOKEN_TYPE_COMMA:
if (parser_tokenize(parser)) {
return true;
}
if (parser_parse_value(parser)) {
return true;
}
break;
case TOKEN_TYPE_RBRACKET:
return false;
default:
return true;
}
}
}
static bool
parser_parse_array(parser_t *parser) {
assert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACKET,
"Array should start with [");
if (parser_tokenize(parser)) {
return true;
}
switch (parser->token.token_type) {
case TOKEN_TYPE_RBRACKET:
return false;
default:
return parser_parse_values(parser);
}
not_reached();
}
static bool
parser_parse_pairs(parser_t *parser) {
assert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,
"Object should start with string");
if (parser_parse_pair(parser)) {
return true;
}
while (true) {
if (parser_tokenize(parser)) {
return true;
}
switch (parser->token.token_type) {
case TOKEN_TYPE_COMMA:
if (parser_tokenize(parser)) {
return true;
}
switch (parser->token.token_type) {
case TOKEN_TYPE_STRING:
if (parser_parse_pair(parser)) {
return true;
}
break;
default:
return true;
}
break;
case TOKEN_TYPE_RBRACE:
return false;
default:
return true;
}
}
}
static bool
parser_parse_object(parser_t *parser) {
assert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACE,
"Object should start with {");
if (parser_tokenize(parser)) {
return true;
}
switch (parser->token.token_type) {
case TOKEN_TYPE_STRING:
return parser_parse_pairs(parser);
case TOKEN_TYPE_RBRACE:
return false;
default:
return true;
}
not_reached();
}
static bool
parser_parse(parser_t *parser) {
if (parser_tokenize(parser)) {
goto label_error;
}
if (parser_parse_value(parser)) {
goto label_error;
}
if (parser_tokenize(parser)) {
goto label_error;
}
switch (parser->token.token_type) {
case TOKEN_TYPE_EOI:
return false;
default:
goto label_error;
}
not_reached();
label_error:
token_error(&parser->token);
return true;
}
TEST_BEGIN(test_json_parser) {
size_t i;
const char *invalid_inputs[] = {
/* Tokenizer error case tests. */
"{ \"string\": X }",
"{ \"string\": nXll }",
"{ \"string\": nuXl }",
"{ \"string\": nulX }",
"{ \"string\": nullX }",
"{ \"string\": fXlse }",
"{ \"string\": faXse }",
"{ \"string\": falXe }",
"{ \"string\": falsX }",
"{ \"string\": falseX }",
"{ \"string\": tXue }",
"{ \"string\": trXe }",
"{ \"string\": truX }",
"{ \"string\": trueX }",
"{ \"string\": \"\n\" }",
"{ \"string\": \"\\z\" }",
"{ \"string\": \"\\uX000\" }",
"{ \"string\": \"\\u0X00\" }",
"{ \"string\": \"\\u00X0\" }",
"{ \"string\": \"\\u000X\" }",
"{ \"string\": -X }",
"{ \"string\": 0.X }",
"{ \"string\": 0.0eX }",
"{ \"string\": 0.0e+X }",
/* Parser error test cases. */
"{\"string\": }",
"{\"string\" }",
"{\"string\": [ 0 }",
"{\"string\": {\"a\":0, 1 } }",
"{\"string\": {\"a\":0: } }",
"{",
"{}{",
};
const char *valid_inputs[] = {
/* Token tests. */
"null",
"false",
"true",
"{}",
"{\"a\": 0}",
"[]",
"[0, 1]",
"0",
"1",
"10",
"-10",
"10.23",
"10.23e4",
"10.23e-4",
"10.23e+4",
"10.23E4",
"10.23E-4",
"10.23E+4",
"-10.23",
"-10.23e4",
"-10.23e-4",
"-10.23e+4",
"-10.23E4",
"-10.23E-4",
"-10.23E+4",
"\"value\"",
"\" \\\" \\/ \\b \\n \\r \\t \\u0abc \\u1DEF \"",
/* Parser test with various nesting. */
"{\"a\":null, \"b\":[1,[{\"c\":2},3]], \"d\":{\"e\":true}}",
};
for (i = 0; i < sizeof(invalid_inputs)/sizeof(const char *); i++) {
const char *input = invalid_inputs[i];
parser_t parser;
parser_init(&parser, false);
assert_false(parser_append(&parser, input),
"Unexpected input appending failure");
assert_true(parser_parse(&parser),
"Unexpected parse success for input: %s", input);
parser_fini(&parser);
}
for (i = 0; i < sizeof(valid_inputs)/sizeof(const char *); i++) {
const char *input = valid_inputs[i];
parser_t parser;
parser_init(&parser, true);
assert_false(parser_append(&parser, input),
"Unexpected input appending failure");
assert_false(parser_parse(&parser),
"Unexpected parse error for input: %s", input);
parser_fini(&parser);
}
}
TEST_END
void
write_cb(void *opaque, const char *str) {
parser_t *parser = (parser_t *)opaque;
if (parser_append(parser, str)) {
test_fail("Unexpected input appending failure");
}
}
TEST_BEGIN(test_stats_print_json) {
const char *opts[] = {
"J",
"Jg",
"Jm",
"Jd",
"Jmd",
"Jgd",
"Jgm",
"Jgmd",
"Ja",
"Jb",
"Jl",
"Jx",
"Jbl",
"Jal",
"Jab",
"Jabl",
"Jax",
"Jbx",
"Jlx",
"Jablx",
"Jgmdablx",
};
unsigned arena_ind, i;
for (i = 0; i < 3; i++) {
unsigned j;
switch (i) {
case 0:
break;
case 1: {
size_t sz = sizeof(arena_ind);
assert_d_eq(mallctl("arenas.create", (void *)&arena_ind,
&sz, NULL, 0), 0, "Unexpected mallctl failure");
break;
} case 2: {
size_t mib[3];
size_t miblen = sizeof(mib)/sizeof(size_t);
assert_d_eq(mallctlnametomib("arena.0.destroy",
mib, &miblen), 0,
"Unexpected mallctlnametomib failure");
mib[1] = arena_ind;
assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL,
0), 0, "Unexpected mallctlbymib failure");
break;
} default:
not_reached();
}
for (j = 0; j < sizeof(opts)/sizeof(const char *); j++) {
parser_t parser;
parser_init(&parser, true);
malloc_stats_print(write_cb, (void *)&parser, opts[j]);
assert_false(parser_parse(&parser),
"Unexpected parse error, opts=\"%s\"", opts[j]);
parser_fini(&parser);
}
}
}
TEST_END
int
main(void) {
return test(
test_json_parser,
test_stats_print_json);
}