util-lib: drop json parser

This was used by the dkr logic, which is gone now, hence remove this too.
Should we need it one day again the git history never forgets...

Note that this only covers the JSON parser. The JSON generator used by
"journalctl -o json" remains, as its much much simpler and requires no
infrastructure except printf() and the most basic escaping.
This commit is contained in:
Lennart Poettering 2016-02-12 23:10:23 +01:00
parent 4de282cf93
commit 2621af5346
5 changed files with 0 additions and 1173 deletions

1
.gitignore vendored
View File

@ -216,7 +216,6 @@
/test-journal-stream
/test-journal-syslog
/test-journal-verify
/test-json
/test-libsystemd-sym*
/test-libudev
/test-libudev-sym*

View File

@ -895,8 +895,6 @@ libbasic_la_SOURCES = \
src/basic/audit-util.h \
src/basic/xml.c \
src/basic/xml.h \
src/basic/json.c \
src/basic/json.h \
src/basic/barrier.c \
src/basic/barrier.h \
src/basic/async.c \
@ -1459,7 +1457,6 @@ tests += \
test-tables \
test-device-nodes \
test-xml \
test-json \
test-architecture \
test-socket-util \
test-fdset \
@ -1922,12 +1919,6 @@ test_xml_SOURCES = \
test_xml_LDADD = \
libshared.la
test_json_SOURCES = \
src/test/test-json.c
test_json_LDADD = \
libshared.la
test_list_SOURCES = \
src/test/test-list.c

View File

@ -1,871 +0,0 @@
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "alloc-util.h"
#include "hexdecoct.h"
#include "json.h"
#include "macro.h"
#include "string-util.h"
#include "utf8.h"
int json_variant_new(JsonVariant **ret, JsonVariantType type) {
JsonVariant *v;
v = new0(JsonVariant, 1);
if (!v)
return -ENOMEM;
v->type = type;
*ret = v;
return 0;
}
static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) {
int r;
assert(ret);
assert(variant);
ret->type = variant->type;
ret->size = variant->size;
if (variant->type == JSON_VARIANT_STRING) {
ret->string = memdup(variant->string, variant->size+1);
if (!ret->string)
return -ENOMEM;
} else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) {
size_t i;
ret->objects = new0(JsonVariant, variant->size);
if (!ret->objects)
return -ENOMEM;
for (i = 0; i < variant->size; ++i) {
r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]);
if (r < 0)
return r;
}
} else
ret->value = variant->value;
return 0;
}
static JsonVariant *json_object_unref(JsonVariant *variant);
static JsonVariant *json_variant_unref_inner(JsonVariant *variant) {
if (!variant)
return NULL;
if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
return json_object_unref(variant);
else if (variant->type == JSON_VARIANT_STRING)
free(variant->string);
return NULL;
}
static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) {
if (!variant)
return NULL;
for (size_t i = 0; i < size; ++i)
json_variant_unref_inner(&variant[i]);
free(variant);
return NULL;
}
static JsonVariant *json_object_unref(JsonVariant *variant) {
size_t i;
assert(variant);
if (!variant->objects)
return NULL;
for (i = 0; i < variant->size; ++i)
json_variant_unref_inner(&variant->objects[i]);
free(variant->objects);
return NULL;
}
static JsonVariant **json_variant_array_unref(JsonVariant **variant) {
size_t i = 0;
JsonVariant *p = NULL;
if (!variant)
return NULL;
while((p = (variant[i++])) != NULL) {
if (p->type == JSON_VARIANT_STRING)
free(p->string);
free(p);
}
free(variant);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref);
JsonVariant *json_variant_unref(JsonVariant *variant) {
if (!variant)
return NULL;
if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
json_object_unref(variant);
else if (variant->type == JSON_VARIANT_STRING)
free(variant->string);
free(variant);
return NULL;
}
char *json_variant_string(JsonVariant *variant){
assert(variant);
assert(variant->type == JSON_VARIANT_STRING);
return variant->string;
}
bool json_variant_bool(JsonVariant *variant) {
assert(variant);
assert(variant->type == JSON_VARIANT_BOOLEAN);
return variant->value.boolean;
}
intmax_t json_variant_integer(JsonVariant *variant) {
assert(variant);
assert(variant->type == JSON_VARIANT_INTEGER);
return variant->value.integer;
}
double json_variant_real(JsonVariant *variant) {
assert(variant);
assert(variant->type == JSON_VARIANT_REAL);
return variant->value.real;
}
JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) {
assert(variant);
assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT);
assert(index < variant->size);
assert(variant->objects);
return &variant->objects[index];
}
JsonVariant *json_variant_value(JsonVariant *variant, const char *key) {
size_t i;
assert(variant);
assert(variant->type == JSON_VARIANT_OBJECT);
assert(variant->objects);
for (i = 0; i < variant->size; i += 2) {
JsonVariant *p = &variant->objects[i];
if (p->type == JSON_VARIANT_STRING && streq(key, p->string))
return &variant->objects[i + 1];
}
return NULL;
}
static void inc_lines(unsigned *line, const char *s, size_t n) {
const char *p = s;
if (!line)
return;
for (;;) {
const char *f;
f = memchr(p, '\n', n);
if (!f)
return;
n -= (f - p) + 1;
p = f + 1;
(*line)++;
}
}
static int unhex_ucs2(const char *c, uint16_t *ret) {
int aa, bb, cc, dd;
uint16_t x;
assert(c);
assert(ret);
aa = unhexchar(c[0]);
if (aa < 0)
return -EINVAL;
bb = unhexchar(c[1]);
if (bb < 0)
return -EINVAL;
cc = unhexchar(c[2]);
if (cc < 0)
return -EINVAL;
dd = unhexchar(c[3]);
if (dd < 0)
return -EINVAL;
x = ((uint16_t) aa << 12) |
((uint16_t) bb << 8) |
((uint16_t) cc << 4) |
((uint16_t) dd);
if (x <= 0)
return -EINVAL;
*ret = x;
return 0;
}
static int json_parse_string(const char **p, char **ret) {
_cleanup_free_ char *s = NULL;
size_t n = 0, allocated = 0;
const char *c;
assert(p);
assert(*p);
assert(ret);
c = *p;
if (*c != '"')
return -EINVAL;
c++;
for (;;) {
int len;
/* Check for EOF */
if (*c == 0)
return -EINVAL;
/* Check for control characters 0x00..0x1f */
if (*c > 0 && *c < ' ')
return -EINVAL;
/* Check for control character 0x7f */
if (*c == 0x7f)
return -EINVAL;
if (*c == '"') {
if (!s) {
s = strdup("");
if (!s)
return -ENOMEM;
} else
s[n] = 0;
*p = c + 1;
*ret = s;
s = NULL;
return JSON_STRING;
}
if (*c == '\\') {
char ch = 0;
c++;
if (*c == 0)
return -EINVAL;
if (IN_SET(*c, '"', '\\', '/'))
ch = *c;
else if (*c == 'b')
ch = '\b';
else if (*c == 'f')
ch = '\f';
else if (*c == 'n')
ch = '\n';
else if (*c == 'r')
ch = '\r';
else if (*c == 't')
ch = '\t';
else if (*c == 'u') {
char16_t x;
int r;
r = unhex_ucs2(c + 1, &x);
if (r < 0)
return r;
c += 5;
if (!GREEDY_REALLOC(s, allocated, n + 4))
return -ENOMEM;
if (!utf16_is_surrogate(x))
n += utf8_encode_unichar(s + n, (char32_t) x);
else if (utf16_is_trailing_surrogate(x))
return -EINVAL;
else {
char16_t y;
if (c[0] != '\\' || c[1] != 'u')
return -EINVAL;
r = unhex_ucs2(c + 2, &y);
if (r < 0)
return r;
c += 6;
if (!utf16_is_trailing_surrogate(y))
return -EINVAL;
n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y));
}
continue;
} else
return -EINVAL;
if (!GREEDY_REALLOC(s, allocated, n + 2))
return -ENOMEM;
s[n++] = ch;
c ++;
continue;
}
len = utf8_encoded_valid_unichar(c);
if (len < 0)
return len;
if (!GREEDY_REALLOC(s, allocated, n + len + 1))
return -ENOMEM;
memcpy(s + n, c, len);
n += len;
c += len;
}
}
static int json_parse_number(const char **p, union json_value *ret) {
bool negative = false, exponent_negative = false, is_double = false;
double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0;
intmax_t i = 0;
const char *c;
assert(p);
assert(*p);
assert(ret);
c = *p;
if (*c == '-') {
negative = true;
c++;
}
if (*c == '0')
c++;
else {
if (!strchr("123456789", *c) || *c == 0)
return -EINVAL;
do {
if (!is_double) {
int64_t t;
t = 10 * i + (*c - '0');
if (t < i) /* overflow */
is_double = false;
else
i = t;
}
x = 10.0 * x + (*c - '0');
c++;
} while (strchr("0123456789", *c) && *c != 0);
}
if (*c == '.') {
is_double = true;
c++;
if (!strchr("0123456789", *c) || *c == 0)
return -EINVAL;
do {
y = 10.0 * y + (*c - '0');
shift = 10.0 * shift;
c++;
} while (strchr("0123456789", *c) && *c != 0);
}
if (*c == 'e' || *c == 'E') {
is_double = true;
c++;
if (*c == '-') {
exponent_negative = true;
c++;
} else if (*c == '+')
c++;
if (!strchr("0123456789", *c) || *c == 0)
return -EINVAL;
do {
exponent = 10.0 * exponent + (*c - '0');
c++;
} while (strchr("0123456789", *c) && *c != 0);
}
*p = c;
if (is_double) {
ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10((exponent_negative ? -1.0 : 1.0) * exponent);
return JSON_REAL;
} else {
ret->integer = negative ? -i : i;
return JSON_INTEGER;
}
}
int json_tokenize(
const char **p,
char **ret_string,
union json_value *ret_value,
void **state,
unsigned *line) {
const char *c;
int t;
int r;
enum {
STATE_NULL,
STATE_VALUE,
STATE_VALUE_POST,
};
assert(p);
assert(*p);
assert(ret_string);
assert(ret_value);
assert(state);
t = PTR_TO_INT(*state);
c = *p;
if (t == STATE_NULL) {
if (line)
*line = 1;
t = STATE_VALUE;
}
for (;;) {
const char *b;
b = c + strspn(c, WHITESPACE);
if (*b == 0)
return JSON_END;
inc_lines(line, c, b - c);
c = b;
switch (t) {
case STATE_VALUE:
if (*c == '{') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE);
return JSON_OBJECT_OPEN;
} else if (*c == '}') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_OBJECT_CLOSE;
} else if (*c == '[') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE);
return JSON_ARRAY_OPEN;
} else if (*c == ']') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_ARRAY_CLOSE;
} else if (*c == '"') {
r = json_parse_string(&c, ret_string);
if (r < 0)
return r;
*ret_value = JSON_VALUE_NULL;
*p = c;
*state = INT_TO_PTR(STATE_VALUE_POST);
return r;
} else if (strchr("-0123456789", *c)) {
r = json_parse_number(&c, ret_value);
if (r < 0)
return r;
*ret_string = NULL;
*p = c;
*state = INT_TO_PTR(STATE_VALUE_POST);
return r;
} else if (startswith(c, "true")) {
*ret_string = NULL;
ret_value->boolean = true;
*p = c + 4;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_BOOLEAN;
} else if (startswith(c, "false")) {
*ret_string = NULL;
ret_value->boolean = false;
*p = c + 5;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_BOOLEAN;
} else if (startswith(c, "null")) {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 4;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_NULL;
} else
return -EINVAL;
case STATE_VALUE_POST:
if (*c == ':') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE);
return JSON_COLON;
} else if (*c == ',') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE);
return JSON_COMMA;
} else if (*c == '}') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_OBJECT_CLOSE;
} else if (*c == ']') {
*ret_string = NULL;
*ret_value = JSON_VALUE_NULL;
*p = c + 1;
*state = INT_TO_PTR(STATE_VALUE_POST);
return JSON_ARRAY_CLOSE;
} else
return -EINVAL;
}
}
}
static bool json_is_value(JsonVariant *var) {
assert(var);
return var->type != JSON_VARIANT_CONTROL;
}
static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) {
bool arr = scope->type == JSON_VARIANT_ARRAY;
int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE;
size_t allocated = 0, size = 0;
JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL;
enum {
STATE_KEY,
STATE_COLON,
STATE_COMMA,
STATE_VALUE
} state = arr ? STATE_VALUE : STATE_KEY;
assert(tokens);
assert(i);
assert(scope);
while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) {
bool stopper;
int r;
stopper = !json_is_value(var) && var->value.integer == terminator;
if (stopper) {
if (state != STATE_COMMA && size > 0)
goto error;
goto out;
}
if (state == STATE_KEY) {
if (var->type != JSON_VARIANT_STRING)
goto error;
else {
key = var;
state = STATE_COLON;
}
}
else if (state == STATE_COLON) {
if (key == NULL)
goto error;
if (json_is_value(var))
goto error;
if (var->value.integer != JSON_COLON)
goto error;
state = STATE_VALUE;
}
else if (state == STATE_VALUE) {
_cleanup_json_variant_unref_ JsonVariant *v = NULL;
size_t toadd = arr ? 1 : 2;
if (!json_is_value(var)) {
int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT;
r = json_variant_new(&v, type);
if (r < 0)
goto error;
r = json_scoped_parse(tokens, i, n, v);
if (r < 0)
goto error;
value = v;
}
else
value = var;
if(!GREEDY_REALLOC(items, allocated, size + toadd))
goto error;
if (arr) {
r = json_variant_deep_copy(&items[size], value);
if (r < 0)
goto error;
} else {
r = json_variant_deep_copy(&items[size], key);
if (r < 0)
goto error;
r = json_variant_deep_copy(&items[size+1], value);
if (r < 0)
goto error;
}
size += toadd;
state = STATE_COMMA;
}
else if (state == STATE_COMMA) {
if (json_is_value(var))
goto error;
if (var->value.integer != JSON_COMMA)
goto error;
key = NULL;
value = NULL;
state = arr ? STATE_VALUE : STATE_KEY;
}
}
error:
json_raw_unref(items, size);
return -EBADMSG;
out:
scope->size = size;
scope->objects = items;
return scope->type;
}
static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) {
size_t it = 0;
int r;
JsonVariant *e;
_cleanup_json_variant_unref_ JsonVariant *p = NULL;
assert(tokens);
assert(ntokens);
e = tokens[it++];
r = json_variant_new(&p, JSON_VARIANT_OBJECT);
if (r < 0)
return r;
if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN)
return -EBADMSG;
r = json_scoped_parse(tokens, &it, ntokens, p);
if (r < 0)
return r;
*rv = p;
p = NULL;
return 0;
}
static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) {
_cleanup_free_ char *buf = NULL;
_cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL;
union json_value v = {};
void *json_state = NULL;
const char *p;
int t, r;
size_t allocated = 0, s = 0;
assert(string);
assert(n);
if (size <= 0)
return -EBADMSG;
buf = strndup(string, size);
if (!buf)
return -ENOMEM;
p = buf;
for (;;) {
_cleanup_json_variant_unref_ JsonVariant *var = NULL;
_cleanup_free_ char *rstr = NULL;
t = json_tokenize(&p, &rstr, &v, &json_state, NULL);
if (t < 0)
return t;
else if (t == JSON_END)
break;
if (t <= JSON_ARRAY_CLOSE) {
r = json_variant_new(&var, JSON_VARIANT_CONTROL);
if (r < 0)
return r;
var->value.integer = t;
} else {
switch (t) {
case JSON_STRING:
r = json_variant_new(&var, JSON_VARIANT_STRING);
if (r < 0)
return r;
var->size = strlen(rstr);
var->string = strdup(rstr);
if (!var->string) {
return -ENOMEM;
}
break;
case JSON_INTEGER:
r = json_variant_new(&var, JSON_VARIANT_INTEGER);
if (r < 0)
return r;
var->value = v;
break;
case JSON_REAL:
r = json_variant_new(&var, JSON_VARIANT_REAL);
if (r < 0)
return r;
var->value = v;
break;
case JSON_BOOLEAN:
r = json_variant_new(&var, JSON_VARIANT_BOOLEAN);
if (r < 0)
return r;
var->value = v;
break;
case JSON_NULL:
r = json_variant_new(&var, JSON_VARIANT_NULL);
if (r < 0)
return r;
break;
}
}
if (!GREEDY_REALLOC(items, allocated, s+2))
return -ENOMEM;
items[s++] = var;
items[s] = NULL;
var = NULL;
}
*n = s;
*tokens = items;
items = NULL;
return 0;
}
int json_parse(const char *string, JsonVariant **rv) {
_cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL;
JsonVariant *v = NULL;
size_t n = 0;
int r;
assert(string);
assert(rv);
r = json_tokens(string, strlen(string), &s, &n);
if (r < 0)
return r;
r = json_parse_tokens(s, n, &v);
if (r < 0)
return r;
*rv = v;
return 0;
}

View File

@ -1,90 +0,0 @@
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "macro.h"
#include "util.h"
enum {
JSON_END,
JSON_COLON,
JSON_COMMA,
JSON_OBJECT_OPEN,
JSON_OBJECT_CLOSE,
JSON_ARRAY_OPEN,
JSON_ARRAY_CLOSE,
JSON_STRING,
JSON_REAL,
JSON_INTEGER,
JSON_BOOLEAN,
JSON_NULL,
};
typedef enum {
JSON_VARIANT_CONTROL,
JSON_VARIANT_STRING,
JSON_VARIANT_INTEGER,
JSON_VARIANT_BOOLEAN,
JSON_VARIANT_REAL,
JSON_VARIANT_ARRAY,
JSON_VARIANT_OBJECT,
JSON_VARIANT_NULL
} JsonVariantType;
union json_value {
bool boolean;
double real;
intmax_t integer;
};
typedef struct JsonVariant {
JsonVariantType type;
size_t size;
union {
char *string;
struct JsonVariant *objects;
union json_value value;
};
} JsonVariant;
int json_variant_new(JsonVariant **ret, JsonVariantType type);
JsonVariant *json_variant_unref(JsonVariant *v);
DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
#define _cleanup_json_variant_unref_ _cleanup_(json_variant_unrefp)
char *json_variant_string(JsonVariant *v);
bool json_variant_bool(JsonVariant *v);
intmax_t json_variant_integer(JsonVariant *v);
double json_variant_real(JsonVariant *v);
JsonVariant *json_variant_element(JsonVariant *v, unsigned index);
JsonVariant *json_variant_value(JsonVariant *v, const char *key);
#define JSON_VALUE_NULL ((union json_value) {})
int json_tokenize(const char **p, char **ret_string, union json_value *ret_value, void **state, unsigned *line);
int json_parse(const char *string, JsonVariant **rv);
int json_parse_measure(const char *string, size_t *size);

View File

@ -1,202 +0,0 @@
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <math.h>
#include "alloc-util.h"
#include "json.h"
#include "string-util.h"
#include "util.h"
static void test_one(const char *data, ...) {
void *state = NULL;
va_list ap;
va_start(ap, data);
for (;;) {
_cleanup_free_ char *str = NULL;
union json_value v = {};
int t, tt;
t = json_tokenize(&data, &str, &v, &state, NULL);
tt = va_arg(ap, int);
assert_se(t == tt);
if (t == JSON_END || t < 0)
break;
else if (t == JSON_STRING) {
const char *nn;
nn = va_arg(ap, const char *);
assert_se(streq_ptr(nn, str));
} else if (t == JSON_REAL) {
double d;
d = va_arg(ap, double);
assert_se(fabs(d - v.real) < 0.001);
} else if (t == JSON_INTEGER) {
intmax_t i;
i = va_arg(ap, intmax_t);
assert_se(i == v.integer);
} else if (t == JSON_BOOLEAN) {
bool b;
b = va_arg(ap, int);
assert_se(b == v.boolean);
}
}
va_end(ap);
}
typedef void (*Test)(JsonVariant *);
static void test_file(const char *data, Test test) {
_cleanup_json_variant_unref_ JsonVariant *v = NULL;
int r;
r = json_parse(data, &v);
assert_se(r == 0);
assert_se(v != NULL);
assert_se(v->type == JSON_VARIANT_OBJECT);
if (test)
test(v);
}
static void test_1(JsonVariant *v) {
JsonVariant *p, *q;
unsigned i;
/* 3 keys + 3 values */
assert_se(v->size == 6);
/* has k */
p = json_variant_value(v, "k");
assert_se(p && p->type == JSON_VARIANT_STRING);
/* k equals v */
assert_se(streq(json_variant_string(p), "v"));
/* has foo */
p = json_variant_value(v, "foo");
assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 3);
/* check foo[0] = 1, foo[1] = 2, foo[2] = 3 */
for (i = 0; i < 3; ++i) {
q = json_variant_element(p, i);
assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == (i+1));
}
/* has bar */
p = json_variant_value(v, "bar");
assert_se(p && p->type == JSON_VARIANT_OBJECT && p->size == 2);
/* zap is null */
q = json_variant_value(p, "zap");
assert_se(q && q->type == JSON_VARIANT_NULL);
}
static void test_2(JsonVariant *v) {
JsonVariant *p, *q;
/* 2 keys + 2 values */
assert_se(v->size == 4);
/* has mutant */
p = json_variant_value(v, "mutant");
assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 4);
/* mutant[0] == 1 */
q = json_variant_element(p, 0);
assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == 1);
/* mutant[1] == null */
q = json_variant_element(p, 1);
assert_se(q && q->type == JSON_VARIANT_NULL);
/* mutant[2] == "1" */
q = json_variant_element(p, 2);
assert_se(q && q->type == JSON_VARIANT_STRING && streq(json_variant_string(q), "1"));
/* mutant[3] == JSON_VARIANT_OBJECT */
q = json_variant_element(p, 3);
assert_se(q && q->type == JSON_VARIANT_OBJECT && q->size == 2);
/* has 1 */
p = json_variant_value(q, "1");
assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 2);
/* "1"[0] == 1 */
q = json_variant_element(p, 0);
assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == 1);
/* "1"[1] == "1" */
q = json_variant_element(p, 1);
assert_se(q && q->type == JSON_VARIANT_STRING && streq(json_variant_string(q), "1"));
/* has blah */
p = json_variant_value(v, "blah");
assert_se(p && p->type == JSON_VARIANT_REAL && fabs(json_variant_real(p) - 1.27) < 0.001);
}
int main(int argc, char *argv[]) {
test_one("x", -EINVAL);
test_one("", JSON_END);
test_one(" ", JSON_END);
test_one("0", JSON_INTEGER, (intmax_t) 0, JSON_END);
test_one("1234", JSON_INTEGER, (intmax_t) 1234, JSON_END);
test_one("3.141", JSON_REAL, 3.141, JSON_END);
test_one("0.0", JSON_REAL, 0.0, JSON_END);
test_one("7e3", JSON_REAL, 7e3, JSON_END);
test_one("-7e-3", JSON_REAL, -7e-3, JSON_END);
test_one("true", JSON_BOOLEAN, true, JSON_END);
test_one("false", JSON_BOOLEAN, false, JSON_END);
test_one("null", JSON_NULL, JSON_END);
test_one("{}", JSON_OBJECT_OPEN, JSON_OBJECT_CLOSE, JSON_END);
test_one("\t {\n} \n", JSON_OBJECT_OPEN, JSON_OBJECT_CLOSE, JSON_END);
test_one("[]", JSON_ARRAY_OPEN, JSON_ARRAY_CLOSE, JSON_END);
test_one("\t [] \n\n", JSON_ARRAY_OPEN, JSON_ARRAY_CLOSE, JSON_END);
test_one("\"\"", JSON_STRING, "", JSON_END);
test_one("\"foo\"", JSON_STRING, "foo", JSON_END);
test_one("\"foo\\nfoo\"", JSON_STRING, "foo\nfoo", JSON_END);
test_one("{\"foo\" : \"bar\"}", JSON_OBJECT_OPEN, JSON_STRING, "foo", JSON_COLON, JSON_STRING, "bar", JSON_OBJECT_CLOSE, JSON_END);
test_one("{\"foo\" : [true, false]}", JSON_OBJECT_OPEN, JSON_STRING, "foo", JSON_COLON, JSON_ARRAY_OPEN, JSON_BOOLEAN, true, JSON_COMMA, JSON_BOOLEAN, false, JSON_ARRAY_CLOSE, JSON_OBJECT_CLOSE, JSON_END);
test_one("\"\xef\xbf\xbd\"", JSON_STRING, "\xef\xbf\xbd", JSON_END);
test_one("\"\\ufffd\"", JSON_STRING, "\xef\xbf\xbd", JSON_END);
test_one("\"\\uf\"", -EINVAL);
test_one("\"\\ud800a\"", -EINVAL);
test_one("\"\\udc00\\udc00\"", -EINVAL);
test_one("\"\\ud801\\udc37\"", JSON_STRING, "\xf0\x90\x90\xb7", JSON_END);
test_one("[1, 2]", JSON_ARRAY_OPEN, JSON_INTEGER, (intmax_t) 1, JSON_COMMA, JSON_INTEGER, (intmax_t) 2, JSON_ARRAY_CLOSE, JSON_END);
test_file("{\"k\": \"v\", \"foo\": [1, 2, 3], \"bar\": {\"zap\": null}}", test_1);
test_file("{\"mutant\": [1, null, \"1\", {\"1\": [1, \"1\"]}], \"blah\": 1.27}", test_2);
return 0;
}