X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fwrap-json.c;h=6fce73b20f8e1656b53a5debbb39be2747ef69bb;hb=65353dce81a629e042800bb7b86fcd869a76727e;hp=1b4b13eadfaa2b765646f7cee7e79e825d94d567;hpb=4a3d0c568ec5ee1296f4a50f269a1eadc4898e27;p=src%2Fapp-framework-binder.git diff --git a/src/wrap-json.c b/src/wrap-json.c index 1b4b13ea..6fce73b2 100644 --- a/src/wrap-json.c +++ b/src/wrap-json.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2016, 2017 "IoT.bzh" + Copyright (C) 2015-2020 "IoT.bzh" author: José Bollo @@ -17,70 +17,238 @@ */ #include +#include #include "wrap-json.h" -#define STACKCOUNT 32 -#define STRCOUNT 8 +#define STACKCOUNT 32 +#define STRCOUNT 8 enum { - wrap_json_pack_error_none, - wrap_json_pack_error_null_object, - wrap_json_pack_error_truncated, - wrap_json_pack_error_internal_error, - wrap_json_pack_error_out_of_memory, - wrap_json_pack_error_invalid_character, - wrap_json_pack_error_too_long, - wrap_json_pack_error_too_deep, - wrap_json_pack_error_null_spec, - wrap_json_pack_error_null_key, - wrap_json_pack_error_null_string, - _wrap_json_pack_error_count_ + wrap_json_error_none, + wrap_json_error_null_object, + wrap_json_error_truncated, + wrap_json_error_internal_error, + wrap_json_error_out_of_memory, + wrap_json_error_invalid_character, + wrap_json_error_too_long, + wrap_json_error_too_deep, + wrap_json_error_null_spec, + wrap_json_error_null_key, + wrap_json_error_null_string, + wrap_json_error_out_of_range, + wrap_json_error_incomplete, + wrap_json_error_missfit_type, + wrap_json_error_key_not_found, + wrap_json_error_bad_base64, + _wrap_json_error_count_ }; static const char ignore_all[] = " \t\n\r,:"; -static const char accept_arr[] = "][{snbiIfoO"; -static const char accept_key[] = "s}"; -#define accept_any (&accept_arr[1]) - -static const char *pack_errors[_wrap_json_pack_error_count_] = -{ - [wrap_json_pack_error_none] = "unknown error", - [wrap_json_pack_error_null_object] = "null object", - [wrap_json_pack_error_truncated] = "truncated", - [wrap_json_pack_error_internal_error] = "internal error", - [wrap_json_pack_error_out_of_memory] = "out of memory", - [wrap_json_pack_error_invalid_character] = "invalid character", - [wrap_json_pack_error_too_long] = "too long", - [wrap_json_pack_error_too_deep] = "too deep", - [wrap_json_pack_error_null_spec] = "spec is NULL", - [wrap_json_pack_error_null_key] = "key is NULL", - [wrap_json_pack_error_null_string] = "string is NULL" +static const char pack_accept_arr[] = "][{snbiIfoOyY"; +static const char pack_accept_key[] = "s}"; +#define pack_accept_any (&pack_accept_arr[1]) + +static const char unpack_accept_arr[] = "*!][{snbiIfFoOyY"; +static const char unpack_accept_key[] = "*!s}"; +#define unpack_accept_any (&unpack_accept_arr[3]) + +static const char *pack_errors[_wrap_json_error_count_] = +{ + [wrap_json_error_none] = "unknown error", + [wrap_json_error_null_object] = "null object", + [wrap_json_error_truncated] = "truncated", + [wrap_json_error_internal_error] = "internal error", + [wrap_json_error_out_of_memory] = "out of memory", + [wrap_json_error_invalid_character] = "invalid character", + [wrap_json_error_too_long] = "too long", + [wrap_json_error_too_deep] = "too deep", + [wrap_json_error_null_spec] = "spec is NULL", + [wrap_json_error_null_key] = "key is NULL", + [wrap_json_error_null_string] = "string is NULL", + [wrap_json_error_out_of_range] = "array too small", + [wrap_json_error_incomplete] = "incomplete container", + [wrap_json_error_missfit_type] = "missfit of type", + [wrap_json_error_key_not_found] = "key not found", + [wrap_json_error_bad_base64] = "bad base64 encoding" }; -int wrap_json_pack_error_position(int rc) +int wrap_json_get_error_position(int rc) { if (rc < 0) rc = -rc; return (rc >> 4) + 1; } -int wrap_json_pack_error_code(int rc) +int wrap_json_get_error_code(int rc) { if (rc < 0) rc = -rc; return rc & 15; } -const char *wrap_json_pack_error_string(int rc) +const char *wrap_json_get_error_string(int rc) { - rc = wrap_json_pack_error_code(rc); - if (rc >= sizeof pack_errors / sizeof *pack_errors) + rc = wrap_json_get_error_code(rc); + if (rc >= (int)(sizeof pack_errors / sizeof *pack_errors)) rc = 0; return pack_errors[rc]; } +static int encode_base64( + const uint8_t *data, + size_t datalen, + char **encoded, + size_t *encodedlen, + int width, + int pad, + int url) +{ + uint16_t u16 = 0; + uint8_t u8 = 0; + size_t in, out, rlen, n3, r3, iout, nout; + int iw; + char *result, c; + + /* compute unformatted output length */ + n3 = datalen / 3; + r3 = datalen % 3; + nout = 4 * n3 + r3 + !!r3; + + /* deduce formatted output length */ + rlen = nout; + if (pad) + rlen += ((~rlen) + 1) & 3; + if (width) + rlen += rlen / width; + + /* allocate the output */ + result = malloc(rlen + 1); + if (result == NULL) + return wrap_json_error_out_of_memory; + + /* compute the formatted output */ + iw = width; + for (in = out = iout = 0 ; iout < nout ; iout++) { + /* get in 'u8' the 6 bits value to add */ + switch (iout & 3) { + case 0: + u16 = (uint16_t)data[in++]; + u8 = (uint8_t)(u16 >> 2); + break; + case 1: + u16 = (uint16_t)(u16 << 8); + if (in < datalen) + u16 = (uint16_t)(u16 | data[in++]); + u8 = (uint8_t)(u16 >> 4); + break; + case 2: + u16 = (uint16_t)(u16 << 8); + if (in < datalen) + u16 = (uint16_t)(u16 | data[in++]); + u8 = (uint8_t)(u16 >> 6); + break; + case 3: + u8 = (uint8_t)u16; + break; + } + u8 &= 63; + + /* encode 'u8' to the char 'c' */ + if (u8 < 52) { + if (u8 < 26) + c = (char)('A' + u8); + else + c = (char)('a' + u8 - 26); + } else { + if (u8 < 62) + c = (char)('0' + u8 - 52); + else if (u8 == 62) + c = url ? '-' : '+'; + else + c = url ? '_' : '/'; + } + /* put to output with format */ + result[out++] = c; + if (iw && !--iw) { + result[out++] = '\n'; + iw = width; + } + } + + /* pad the output */ + while (out < rlen) { + result[out++] = '='; + if (iw && !--iw) { + result[out++] = '\n'; + iw = width; + } + } + + /* terminate */ + result[out] = 0; + *encoded = result; + *encodedlen = rlen; + return 0; +} + +static int decode_base64( + const char *data, + size_t datalen, + uint8_t **decoded, + size_t *decodedlen, + int url) +{ + uint16_t u16; + uint8_t u8, *result; + size_t in, out, iin; + char c; + + /* allocate enougth output */ + result = malloc(datalen); + if (result == NULL) + return wrap_json_error_out_of_memory; + + /* decode the input */ + for (iin = in = out = 0 ; in < datalen ; in++) { + c = data[in]; + if (c != '\n' && c != '\r' && c != '=') { + if ('A' <= c && c <= 'Z') + u8 = (uint8_t)(c - 'A'); + else if ('a' <= c && c <= 'z') + u8 = (uint8_t)(c - 'a' + 26); + else if ('0' <= c && c <= '9') + u8 = (uint8_t)(c - '0' + 52); + else if (c == '+' || c == '-') + u8 = (uint8_t)62; + else if (c == '/' || c == '_') + u8 = (uint8_t)63; + else { + free(result); + return wrap_json_error_bad_base64; + } + if (!iin) { + u16 = (uint16_t)u8; + iin = 6; + } else { + u16 = (uint16_t)((u16 << 6) | u8); + iin -= 2; + u8 = (uint8_t)(u16 >> iin); + result[out++] = u8; + } + } + } + + /* terminate */ + *decoded = realloc(result, out); + if (out && *decoded == NULL) { + free(result); + return wrap_json_error_out_of_memory; + } + *decodedlen = out; + return 0; +} static inline const char *skip(const char *d) { @@ -91,12 +259,14 @@ static inline const char *skip(const char *d) int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) { + /* TODO: the case of structs with key being single char should be optimized */ int nstr, notnull, nullable, rc; size_t sz, dsz, ssz; char *s; char c; const char *d; char buffer[256]; + struct { const uint8_t *in; size_t insz; char *out; size_t outsz; } bytes; struct { const char *str; size_t sz; } strs[STRCOUNT]; struct { struct json_object *cont, *key; const char *acc; char type; } stack[STACKCOUNT], *top; struct json_object *obj; @@ -106,18 +276,19 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) top = stack; top->key = NULL; top->cont = NULL; - top->acc = accept_any; + top->acc = pack_accept_any; top->type = 0; - if (!desc) + d = desc; + if (!d) goto null_spec; - d = skip(desc); + d = skip(d); for(;;) { c = *d; if (!c) goto truncated; if (!strchr(top->acc, c)) goto invalid_character; - d = skip(++d); + d = skip(d + 1); switch(c) { case 's': nullable = 0; @@ -129,24 +300,24 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) if (strs[nstr].str) notnull = 1; if (*d == '?') { - d = skip(++d); + d = skip(d + 1); nullable = 1; } switch(*d) { - case '%': strs[nstr].sz = va_arg(args, size_t); d = skip(++d); break; - case '#': strs[nstr].sz = (size_t)va_arg(args, int); d = skip(++d); break; + case '%': strs[nstr].sz = va_arg(args, size_t); d = skip(d + 1); break; + case '#': strs[nstr].sz = (size_t)va_arg(args, int); d = skip(d + 1); break; default: strs[nstr].sz = strs[nstr].str ? strlen(strs[nstr].str) : 0; break; } sz += strs[nstr++].sz; if (*d == '?') { - d = skip(++d); + d = skip(d + 1); nullable = 1; } if (*d != '+') break; if (nstr >= STRCOUNT) goto too_long; - d = skip(++d); + d = skip(d + 1); } if (*d == '*') nullable = 1; @@ -163,7 +334,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) dsz -= strs[nstr].sz; memcpy(&s[dsz], strs[nstr].str, strs[nstr].sz); } - obj = json_object_new_string_len(s, sz); + obj = json_object_new_string_len(s, (int)sz); if (!obj) goto out_of_memory; } else if (nullable) @@ -198,12 +369,36 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) case 'O': obj = va_arg(args, struct json_object*); if (*d == '?') - d = skip(++d); + d = skip(d + 1); else if (*d != '*' && !obj) goto null_object; if (c == 'O') json_object_get(obj); break; + case 'y': + case 'Y': + bytes.in = va_arg(args, const uint8_t*); + bytes.insz = va_arg(args, size_t); + if (bytes.in == NULL || bytes.insz == 0) + obj = NULL; + else { + rc = encode_base64(bytes.in, bytes.insz, + &bytes.out, &bytes.outsz, 0, 0, c == 'y'); + if (rc) + goto error; + obj = json_object_new_string_len(bytes.out, (int)bytes.outsz); + free(bytes.out); + if (!obj) + goto out_of_memory; + } + if (*d == '?') + d = skip(d + 1); + else if (*d != '*' && !obj) { + obj = json_object_new_string_len(d, 0); + if (!obj) + goto out_of_memory; + } + break; case '[': case '{': if (++top >= &stack[STACKCOUNT]) @@ -211,11 +406,11 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) top->key = NULL; if (c == '[') { top->type = ']'; - top->acc = accept_arr; + top->acc = pack_accept_arr; top->cont = json_object_new_array(); } else { top->type = '}'; - top->acc = accept_key; + top->acc = pack_accept_key; top->cont = json_object_new_object(); } if (!top->cont) @@ -224,7 +419,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) case '}': case ']': if (c != top->type || top <= stack) - goto invalid_character; + goto internal_error; obj = (top--)->cont; if (*d == '*' && !(c == '}' ? json_object_object_length(obj) : json_object_array_length(obj))) { json_object_put(obj); @@ -246,57 +441,59 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) if (obj || *d != '*') json_object_array_add(top->cont, obj); if (*d == '*') - d = skip(++d); + d = skip(d + 1); break; case '}': if (!obj) goto null_key; top->key = obj; - top->acc = accept_any; + top->acc = pack_accept_any; top->type = ':'; break; case ':': if (obj || *d != '*') json_object_object_add(top->cont, json_object_get_string(top->key), obj); if (*d == '*') - d = skip(++d); + d = skip(d + 1); json_object_put(top->key); top->key = NULL; - top->acc = accept_key; + top->acc = pack_accept_key; top->type = '}'; break; + default: + goto internal_error; } } null_object: - rc = wrap_json_pack_error_null_object; + rc = wrap_json_error_null_object; goto error; truncated: - rc = wrap_json_pack_error_truncated; + rc = wrap_json_error_truncated; goto error; internal_error: - rc = wrap_json_pack_error_internal_error; + rc = wrap_json_error_internal_error; goto error; out_of_memory: - rc = wrap_json_pack_error_out_of_memory; + rc = wrap_json_error_out_of_memory; goto error; invalid_character: - rc = wrap_json_pack_error_invalid_character; + rc = wrap_json_error_invalid_character; goto error; too_long: - rc = wrap_json_pack_error_too_long; + rc = wrap_json_error_too_long; goto error; too_deep: - rc = wrap_json_pack_error_too_deep; + rc = wrap_json_error_too_deep; goto error; null_spec: - rc = wrap_json_pack_error_null_spec; + rc = wrap_json_error_null_spec; goto error; null_key: - rc = wrap_json_pack_error_null_key; + rc = wrap_json_error_null_key; goto error; null_string: - rc = wrap_json_pack_error_null_string; + rc = wrap_json_error_null_string; goto error; error: do { @@ -319,10 +516,762 @@ int wrap_json_pack(struct json_object **result, const char *desc, ...) return rc; } -#if 1 +static int vunpack(struct json_object *object, const char *desc, va_list args, int store) +{ + int rc = 0, optionnal, ignore; + char c, xacc[2] = { 0, 0 }; + const char *acc; + const char *d, *fit = NULL; + const char *key = NULL; + const char **ps = NULL; + double *pf = NULL; + int *pi = NULL; + int64_t *pI = NULL; + size_t *pz = NULL; + uint8_t **py = NULL; + struct { struct json_object *parent; const char *acc; int index; int count; char type; } stack[STACKCOUNT], *top; + struct json_object *obj; + struct json_object **po; + + xacc[0] = 0; + ignore = 0; + top = NULL; + acc = unpack_accept_any; + d = desc; + if (!d) + goto null_spec; + d = skip(d); + obj = object; + for(;;) { + fit = d; + c = *d; + if (!c) + goto truncated; + if (!strchr(acc, c)) + goto invalid_character; + d = skip(d + 1); + switch(c) { + case 's': + if (xacc[0] == '}') { + /* expects a key */ + key = va_arg(args, const char *); + if (!key) + goto null_key; + if (*d != '?') + optionnal = 0; + else { + optionnal = 1; + d = skip(d + 1); + } + if (ignore) + ignore++; + else { + if (json_object_object_get_ex(top->parent, key, &obj)) { + /* found */ + top->index++; + } else { + /* not found */ + if (!optionnal) + goto key_not_found; + ignore = 1; + obj = NULL; + } + } + xacc[0] = ':'; + acc = unpack_accept_any; + continue; + } + /* get a string */ + if (store) + ps = va_arg(args, const char **); + if (!ignore) { + if (!json_object_is_type(obj, json_type_string)) + goto missfit; + if (store && ps) + *ps = json_object_get_string(obj); + } + if (*d == '%') { + d = skip(d + 1); + if (store) { + pz = va_arg(args, size_t *); + if (!ignore && pz) + *pz = (size_t)json_object_get_string_len(obj); + } + } + break; + case 'n': + if (!ignore && !json_object_is_type(obj, json_type_null)) + goto missfit; + break; + case 'b': + if (store) + pi = va_arg(args, int *); + + if (!ignore) { + if (!json_object_is_type(obj, json_type_boolean)) + goto missfit; + if (store && pi) + *pi = json_object_get_boolean(obj); + } + break; + case 'i': + if (store) + pi = va_arg(args, int *); + + if (!ignore) { + if (!json_object_is_type(obj, json_type_int)) + goto missfit; + if (store && pi) + *pi = json_object_get_int(obj); + } + break; + case 'I': + if (store) + pI = va_arg(args, int64_t *); + + if (!ignore) { + if (!json_object_is_type(obj, json_type_int)) + goto missfit; + if (store && pI) + *pI = json_object_get_int64(obj); + } + break; + case 'f': + case 'F': + if (store) + pf = va_arg(args, double *); + + if (!ignore) { + if (!(json_object_is_type(obj, json_type_double) || (c == 'F' && json_object_is_type(obj, json_type_int)))) + goto missfit; + if (store && pf) + *pf = json_object_get_double(obj); + } + break; + case 'o': + case 'O': + if (store) { + po = va_arg(args, struct json_object **); + if (!ignore && po) { + if (c == 'O') + obj = json_object_get(obj); + *po = obj; + } + } + break; + case 'y': + case 'Y': + if (store) { + py = va_arg(args, uint8_t **); + pz = va_arg(args, size_t *); + } + if (!ignore) { + if (obj == NULL) { + if (store && py && pz) { + *py = NULL; + *pz = 0; + } + } else { + if (!json_object_is_type(obj, json_type_string)) + goto missfit; + if (store && py && pz) { + rc = decode_base64( + json_object_get_string(obj), + (size_t)json_object_get_string_len(obj), + py, pz, c == 'y'); + if (rc) + goto error; + } + } + } + break; + + case '[': + case '{': + if (!top) + top = stack; + else if (++top >= &stack[STACKCOUNT]) + goto too_deep; + + top->acc = acc; + top->type = xacc[0]; + top->index = 0; + top->parent = obj; + if (ignore) + ignore++; + if (c == '[') { + if (!ignore) { + if (!json_object_is_type(obj, json_type_array)) + goto missfit; + top->count = (int)json_object_array_length(obj); + } + xacc[0] = ']'; + acc = unpack_accept_arr; + } else { + if (!ignore) { + if (!json_object_is_type(obj, json_type_object)) + goto missfit; + top->count = json_object_object_length(obj); + } + xacc[0] = '}'; + acc = unpack_accept_key; + continue; + } + break; + case '}': + case ']': + if (!top || c != xacc[0]) + goto internal_error; + acc = top->acc; + xacc[0] = top->type; + top = top == stack ? NULL : top - 1; + if (ignore) + ignore--; + break; + case '!': + if (*d != xacc[0]) + goto invalid_character; + if (!ignore && top->index != top->count) + goto incomplete; + /*@fallthrough@*/ + case '*': + acc = xacc; + continue; + default: + goto internal_error; + } + switch (xacc[0]) { + case 0: + if (top) + goto internal_error; + if (*d) + goto invalid_character; + return 0; + case ']': + if (!ignore) { + key = strchr(unpack_accept_arr, *d); + if (key && key >= unpack_accept_any) { + if (top->index >= top->count) + goto out_of_range; + obj = json_object_array_get_idx(top->parent, top->index++); + } + } + break; + case ':': + acc = unpack_accept_key; + xacc[0] = '}'; + if (ignore) + ignore--; + break; + default: + goto internal_error; + } + } +truncated: + rc = wrap_json_error_truncated; + goto error; +internal_error: + rc = wrap_json_error_internal_error; + goto error; +invalid_character: + rc = wrap_json_error_invalid_character; + goto error; +too_deep: + rc = wrap_json_error_too_deep; + goto error; +null_spec: + rc = wrap_json_error_null_spec; + goto error; +null_key: + rc = wrap_json_error_null_key; + goto error; +out_of_range: + rc = wrap_json_error_out_of_range; + goto error; +incomplete: + rc = wrap_json_error_incomplete; + goto error; +missfit: + rc = wrap_json_error_missfit_type; + goto errorfit; +key_not_found: + rc = wrap_json_error_key_not_found; + goto error; +errorfit: + d = fit; +error: + rc = rc | (int)((d - desc) << 4); + return -rc; +} + +int wrap_json_vcheck(struct json_object *object, const char *desc, va_list args) +{ + return vunpack(object, desc, args, 0); +} + +int wrap_json_check(struct json_object *object, const char *desc, ...) +{ + int rc; + va_list args; + + va_start(args, desc); + rc = wrap_json_vcheck(object, desc, args); + va_end(args); + return rc; +} + +int wrap_json_vmatch(struct json_object *object, const char *desc, va_list args) +{ + return !vunpack(object, desc, args, 0); +} + +int wrap_json_match(struct json_object *object, const char *desc, ...) +{ + int rc; + va_list args; + + va_start(args, desc); + rc = wrap_json_vmatch(object, desc, args); + va_end(args); + return rc; +} + +int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args) +{ + return vunpack(object, desc, args, 1); +} + +int wrap_json_unpack(struct json_object *object, const char *desc, ...) +{ + int rc; + va_list args; + + va_start(args, desc); + rc = vunpack(object, desc, args, 1); + va_end(args); + return rc; +} + +static void object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) +{ + struct json_object_iterator it = json_object_iter_begin(object); + struct json_object_iterator end = json_object_iter_end(object); + while (!json_object_iter_equal(&it, &end)) { + callback(closure, json_object_iter_peek_value(&it), json_object_iter_peek_name(&it)); + json_object_iter_next(&it); + } +} + +static void array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) +{ + int n = (int)json_object_array_length(object); + int i = 0; + while(i < n) + callback(closure, json_object_array_get_idx(object, i++)); +} + +void wrap_json_optarray_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) +{ + if (json_object_is_type(object, json_type_array)) + array_for_all(object, callback, closure); + else + callback(closure, object); +} + +void wrap_json_array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) +{ + if (json_object_is_type(object, json_type_array)) + array_for_all(object, callback, closure); +} + +void wrap_json_object_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) +{ + if (json_object_is_type(object, json_type_object)) + object_for_all(object, callback, closure); +} + +void wrap_json_optobject_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) +{ + if (json_object_is_type(object, json_type_object)) + object_for_all(object, callback, closure); + else + callback(closure, object, NULL); +} + +void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct json_object*,const char*), void *closure) +{ + if (!object) + /* do nothing */; + else if (json_object_is_type(object, json_type_object)) + object_for_all(object, callback, closure); + else if (!json_object_is_type(object, json_type_array)) + callback(closure, object, NULL); + else { + int n = (int)json_object_array_length(object); + int i = 0; + while(i < n) + callback(closure, json_object_array_get_idx(object, i++), NULL); + } +} + +/** + * Clones the 'object' for the depth 'subdepth'. The object 'object' is + * duplicated and all its fields are cloned with the depth 'subdepth'. + * + * @param object the object to clone. MUST be an **object**. + * @param subdepth the depth to use when cloning the fields of the object. + * + * @return the cloned object. + */ +static struct json_object *clone_object(struct json_object *object, int subdepth) +{ + struct json_object *r = json_object_new_object(); + struct json_object_iterator it = json_object_iter_begin(object); + struct json_object_iterator end = json_object_iter_end(object); + while (!json_object_iter_equal(&it, &end)) { + json_object_object_add(r, + json_object_iter_peek_name(&it), + wrap_json_clone_depth(json_object_iter_peek_value(&it), subdepth)); + json_object_iter_next(&it); + } + return r; +} + +/** + * Clones the 'array' for the depth 'subdepth'. The array 'array' is + * duplicated and all its fields are cloned with the depth 'subdepth'. + * + * @param array the array to clone. MUST be an **array**. + * @param subdepth the depth to use when cloning the items of the array. + * + * @return the cloned array. + */ +static struct json_object *clone_array(struct json_object *array, int subdepth) +{ + int n = (int)json_object_array_length(array); + struct json_object *r = json_object_new_array(); + while (n) { + n--; + json_object_array_put_idx(r, n, + wrap_json_clone_depth(json_object_array_get_idx(array, n), subdepth)); + } + return r; +} + +/** + * Clones any json 'item' for the depth 'depth'. The item is duplicated + * and if 'depth' is not zero, its contents is recursively cloned with + * the depth 'depth' - 1. + * + * Be aware that this implementation doesn't copies the primitive json + * items (numbers, nulls, booleans, strings) but instead increments their + * use count. This can cause issues with newer versions of libjson-c that + * now unfortunately allows to change their values. + * + * @param item the item to clone. Can be of any kind. + * @param depth the depth to use when cloning composites: object or arrays. + * + * @return the cloned array. + * + * @see wrap_json_clone + * @see wrap_json_clone_deep + */ +struct json_object *wrap_json_clone_depth(struct json_object *item, int depth) +{ + if (depth) { + switch (json_object_get_type(item)) { + case json_type_object: + return clone_object(item, depth - 1); + case json_type_array: + return clone_array(item, depth - 1); + default: + break; + } + } + return json_object_get(item); +} + +/** + * Clones the 'object': returns a copy of it. But doesn't clones + * the content. Synonym of wrap_json_clone_depth(object, 1). + * + * Be aware that this implementation doesn't clones content that is deeper + * than 1 but it does link these contents to the original object and + * increments their use count. So, everything deeper that 1 is still available. + * + * @param object the object to clone + * + * @return a copy of the object. + * + * @see wrap_json_clone_depth + * @see wrap_json_clone_deep + */ +struct json_object *wrap_json_clone(struct json_object *object) +{ + return wrap_json_clone_depth(object, 1); +} + +/** + * Clones the 'object': returns a copy of it. Also clones all + * the content recursively. Synonym of wrap_json_clone_depth(object, INT_MAX). + * + * @param object the object to clone + * + * @return a copy of the object. + * + * @see wrap_json_clone_depth + * @see wrap_json_clone + */ +struct json_object *wrap_json_clone_deep(struct json_object *object) +{ + return wrap_json_clone_depth(object, INT_MAX); +} + +/** + * Adds the items of the object 'added' to the object 'dest'. + * + * @param dest the object to complete this object is modified + * @added the object containing fields to add + * + * @return the destination object 'dest' + * + * @example wrap_json_object_add({"a":"a"},{"X":"X"}) -> {"a":"a","X":"X"} + */ +struct json_object *wrap_json_object_add(struct json_object *dest, struct json_object *added) +{ + struct json_object_iterator it, end; + if (json_object_is_type(dest, json_type_object) && json_object_is_type(added, json_type_object)) { + it = json_object_iter_begin(added); + end = json_object_iter_end(added); + while (!json_object_iter_equal(&it, &end)) { + json_object_object_add(dest, + json_object_iter_peek_name(&it), + json_object_get(json_object_iter_peek_value(&it))); + json_object_iter_next(&it); + } + } + return dest; +} + +/** + * Sort the 'array' and returns it. Sorting is done accordingly to the + * order given by the function 'wrap_json_cmp'. If the paramater isn't + * an array, nothing is done and the parameter is returned unchanged. + * + * @param array the array to sort + * + * @returns the array sorted + */ +struct json_object *wrap_json_sort(struct json_object *array) +{ + if (json_object_is_type(array, json_type_array)) + json_object_array_sort(array, (int(*)(const void*, const void*))wrap_json_cmp); + + return array; +} + +/** + * Returns a json array of the sorted keys of 'object' or null if 'object' has no keys. + * + * @param object the object whose keys are to be returned + * + * @return either NULL is 'object' isn't an object or a sorted array of the key's strings. + */ +struct json_object *wrap_json_keys(struct json_object *object) +{ + struct json_object *r; + struct json_object_iterator it, end; + if (!json_object_is_type(object, json_type_object)) + r = NULL; + else { + r = json_object_new_array(); + it = json_object_iter_begin(object); + end = json_object_iter_end(object); + while (!json_object_iter_equal(&it, &end)) { + json_object_array_add(r, json_object_new_string(json_object_iter_peek_name(&it))); + json_object_iter_next(&it); + } + wrap_json_sort(r); + } + return r; +} + +/** + * Internal comparison of 'x' with 'y' + * + * @param x first object to compare + * @param y second object to compare + * @param inc boolean true if should test for inclusion of y in x + * @param sort boolean true if comparison used for sorting + * + * @return an integer indicating the computed result. Refer to + * the table below for meaning of the returned value. + * + * inc | sort | x < y | x == y | x > y | y in x + * ----+------+---------+----------+---------+--------- + * 0 | 0 | != 0 | 0 | != 0 | > 0 + * 0 | 1 | < 0 | 0 | > 0 | > 0 + * 1 | 0 | != 0 | 0 | != 0 | 0 + * 1 | 1 | < 0 | 0 | > 0 | 0 + * + * + * if 'x' is found, respectively, to be less than, to match, + * or be greater than 'y'. This is valid when 'sort' + */ +static int jcmp(struct json_object *x, struct json_object *y, int inc, int sort) +{ + double dx, dy; + int64_t ix, iy; + const char *sx, *sy; + enum json_type tx, ty; + int r, nx, ny, i; + struct json_object_iterator it, end; + struct json_object *jx, *jy; + + /* check equality of pointers */ + if (x == y) + return 0; + + /* get the types */ + tx = json_object_get_type(x); + ty = json_object_get_type(y); + r = (int)tx - (int)ty; + if (r) + return r; + + /* compare following the type */ + switch (tx) { + default: + case json_type_null: + break; + + case json_type_boolean: + r = (int)json_object_get_boolean(x) + - (int)json_object_get_boolean(y); + break; + + case json_type_double: + dx = json_object_get_double(x); + dy = json_object_get_double(y); + r = dx < dy ? -1 : dx > dy; + break; + + case json_type_int: + ix = json_object_get_int64(x); + iy = json_object_get_int64(y); + r = ix < iy ? -1 : ix > iy; + break; + + case json_type_object: + it = json_object_iter_begin(y); + end = json_object_iter_end(y); + nx = json_object_object_length(x); + ny = json_object_object_length(y); + r = nx - ny; + if (r > 0 && inc) + r = 0; + while (!r && !json_object_iter_equal(&it, &end)) { + if (json_object_object_get_ex(x, json_object_iter_peek_name(&it), &jx)) { + jy = json_object_iter_peek_value(&it); + json_object_iter_next(&it); + r = jcmp(jx, jy, inc, sort); + } else if (sort) { + jx = wrap_json_keys(x); + jy = wrap_json_keys(y); + r = wrap_json_cmp(jx, jy); + json_object_put(jx); + json_object_put(jy); + } else + r = 1; + } + break; + + case json_type_array: + nx = (int)json_object_array_length(x); + ny = (int)json_object_array_length(y); + r = nx - ny; + if (r > 0 && inc) + r = 0; + for (i = 0 ; !r && i < ny ; i++) { + jx = json_object_array_get_idx(x, i); + jy = json_object_array_get_idx(y, i); + r = jcmp(jx, jy, inc, sort); + } + break; + + case json_type_string: + sx = json_object_get_string(x); + sy = json_object_get_string(y); + r = strcmp(sx, sy); + break; + } + return r; +} + +/** + * Compares 'x' with 'y' + * + * @param x first object to compare + * @param y second object to compare + * + * @return an integer less than, equal to, or greater than zero + * if 'x' is found, respectively, to be less than, to match, + * or be greater than 'y'. + */ +int wrap_json_cmp(struct json_object *x, struct json_object *y) +{ + return jcmp(x, y, 0, 1); +} + +/** + * Searchs wether 'x' equals 'y' + * + * @param x first object to compare + * @param y second object to compare + * + * @return an integer equal to zero when 'x' != 'y' or 1 when 'x' == 'y'. + */ +int wrap_json_equal(struct json_object *x, struct json_object *y) +{ + return !jcmp(x, y, 0, 0); +} + +/** + * Searchs wether 'x' contains 'y' + * + * @param x first object to compare + * @param y second object to compare + * + * @return an integer equal to 1 when 'y' is a subset of 'x' or zero otherwise + */ +int wrap_json_contains(struct json_object *x, struct json_object *y) +{ + return !jcmp(x, y, 1, 0); +} + +#if defined(WRAP_JSON_TEST) #include +#if !defined(JSON_C_TO_STRING_NOSLASHESCAPE) +#define JSON_C_TO_STRING_NOSLASHESCAPE 0 +#endif +#define j2t(o) json_object_to_json_string_ext((o), JSON_C_TO_STRING_NOSLASHESCAPE) + +void tclone(struct json_object *object) +{ + struct json_object *o; + + o = wrap_json_clone(object); + if (!wrap_json_equal(object, o)) + printf("ERROR in clone or equal: %s VERSUS %s\n", j2t(object), j2t(o)); + json_object_put(o); -void T(const char *desc, ...) + o = wrap_json_clone_deep(object); + if (!wrap_json_equal(object, o)) + printf("ERROR in clone_deep or equal: %s VERSUS %s\n", j2t(object), j2t(o)); + json_object_put(o); +} + +void p(const char *desc, ...) { int rc; va_list args; @@ -331,72 +1280,304 @@ void T(const char *desc, ...) va_start(args, desc); rc = wrap_json_vpack(&result, desc, args); va_end(args); - if (!rc) - printf(" SUCCESS %s\n\n", json_object_to_json_string(result)); + if (!rc) + printf(" SUCCESS %s\n\n", j2t(result)); else - printf(" ERROR[char %d err %d] %s\n\n", wrap_json_pack_error_position(rc), wrap_json_pack_error_code(rc), wrap_json_pack_error_string(rc)); + printf(" ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc)); + tclone(result); json_object_put(result); } -#define t(...) printf("testing(%s)\n",#__VA_ARGS__); T(__VA_ARGS__); +const char *xs[10]; +int *xi[10]; +int64_t *xI[10]; +double *xf[10]; +struct json_object *xo[10]; +size_t xz[10]; +uint8_t *xy[10]; + +void u(const char *value, const char *desc, ...) +{ + unsigned m, k; + int rc; + va_list args; + struct json_object *object, *o; + + memset(xs, 0, sizeof xs); + memset(xi, 0, sizeof xi); + memset(xI, 0, sizeof xI); + memset(xf, 0, sizeof xf); + memset(xo, 0, sizeof xo); + memset(xy, 0, sizeof xy); + memset(xz, 0, sizeof xz); + object = json_tokener_parse(value); + va_start(args, desc); + rc = wrap_json_vunpack(object, desc, args); + va_end(args); + if (rc) + printf(" ERROR[char %d err %d] %s\n\n", wrap_json_get_error_position(rc), wrap_json_get_error_code(rc), wrap_json_get_error_string(rc)); + else { + value = NULL; + printf(" SUCCESS"); + va_start(args, desc); + k = m = 0; + while(*desc) { + switch(*desc) { + case '{': m = (m << 1) | 1; k = 1; break; + case '}': m = m >> 1; k = m&1; break; + case '[': m = m << 1; k = 0; break; + case ']': m = m >> 1; k = m&1; break; + case 's': printf(" s:%s", k ? va_arg(args, const char*) : *(va_arg(args, const char**)?:&value)); k ^= m&1; break; + case '%': printf(" %%:%zu", *va_arg(args, size_t*)); k = m&1; break; + case 'n': printf(" n"); k = m&1; break; + case 'b': printf(" b:%d", *va_arg(args, int*)); k = m&1; break; + case 'i': printf(" i:%d", *va_arg(args, int*)); k = m&1; break; + case 'I': printf(" I:%lld", *va_arg(args, int64_t*)); k = m&1; break; + case 'f': printf(" f:%f", *va_arg(args, double*)); k = m&1; break; + case 'F': printf(" F:%f", *va_arg(args, double*)); k = m&1; break; + case 'o': printf(" o:%s", j2t(*va_arg(args, struct json_object**))); k = m&1; break; + case 'O': o = *va_arg(args, struct json_object**); printf(" O:%s", j2t(o)); json_object_put(o); k = m&1; break; + case 'y': + case 'Y': { + uint8_t *p = *va_arg(args, uint8_t**); + size_t s = *va_arg(args, size_t*); + printf(" y/%d:%.*s", (int)s, (int)s, (char*)p); + k ^= m&1; + break; + } + default: break; + } + desc++; + } + va_end(args); + printf("\n\n"); + } + tclone(object); + json_object_put(object); +} + +void c(const char *sx, const char *sy, int e, int c) +{ + int re, rc; + struct json_object *jx, *jy; + + jx = json_tokener_parse(sx); + jy = json_tokener_parse(sy); + + re = wrap_json_cmp(jx, jy); + rc = wrap_json_contains(jx, jy); + + printf("compare(%s)(%s)\n", sx, sy); + printf(" -> %d / %d\n", re, rc); + + if (!re != !!e) + printf(" ERROR should be %s\n", e ? "equal" : "different"); + if (!rc != !c) + printf(" ERROR should %scontain\n", c ? "" : "not "); + + printf("\n"); +} + +#define P(...) do{ printf("pack(%s)\n",#__VA_ARGS__); p(__VA_ARGS__); } while(0) +#define U(...) do{ printf("unpack(%s)\n",#__VA_ARGS__); u(__VA_ARGS__); } while(0) int main() { char buffer[4] = {'t', 'e', 's', 't'}; - t("n"); - t("b", 1); - t("b", 0); - t("i", 1); - t("I", (uint64_t)0x123456789abcdef); - t("f", 3.14); - t("s", "test"); - t("s?", "test"); - t("s?", NULL); - t("s#", "test asdf", 4); - t("s%", "test asdf", (size_t)4); - t("s#", buffer, 4); - t("s%", buffer, (size_t)4); - t("s++", "te", "st", "ing"); - t("s#+#+", "test", 1, "test", 2, "test"); - t("s%+%+", "test", (size_t)1, "test", (size_t)2, "test"); - t("{}", 1.0); - t("[]", 1.0); - t("o", json_object_new_int(1)); - t("o?", json_object_new_int(1)); - t("o?", NULL); - t("O", json_object_new_int(1)); - t("O?", json_object_new_int(1)); - t("O?", NULL); - t("{s:[]}", "foo"); - t("{s+#+: []}", "foo", "barbar", 3, "baz"); - t("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL); - t("{s:**}", "a", NULL); - t("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL); - t("[i,i,i]", 0, 1, 2); - t("[s,o,O]", NULL, NULL, NULL); - t("[**]", NULL); - t("[s*,o*,O*]", NULL, NULL, NULL); - t(" s ", "test"); - t("[ ]"); - t("[ i , i, i ] ", 1, 2, 3); - t("{\n\n1"); - t("[}"); - t("{]"); - t("["); - t("{"); - t("[i]a", 42); - t("ia", 42); - t("s", NULL); - t("+", NULL); - t(NULL); - t("{s:i}", NULL, 1); - t("{ {}: s }", "foo"); - t("{ s: {}, s:[ii{} }", "foo", "bar", 12, 13); - t("[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"); + P("n"); + P("b", 1); + P("b", 0); + P("i", 1); + P("I", (uint64_t)0x123456789abcdef); + P("f", 3.14); + P("s", "test"); + P("s?", "test"); + P("s?", NULL); + P("s#", "test asdf", 4); + P("s%", "test asdf", (size_t)4); + P("s#", buffer, 4); + P("s%", buffer, (size_t)4); + P("s++", "te", "st", "ing"); + P("s#+#+", "test", 1, "test", 2, "test"); + P("s%+%+", "test", (size_t)1, "test", (size_t)2, "test"); + P("{}", 1.0); + P("[]", 1.0); + P("o", json_object_new_int(1)); + P("o?", json_object_new_int(1)); + P("o?", NULL); + P("O", json_object_new_int(1)); + P("O?", json_object_new_int(1)); + P("O?", NULL); + P("{s:[]}", "foo"); + P("{s+#+: []}", "foo", "barbar", 3, "baz"); + P("{s:s,s:o,s:O}", "a", NULL, "b", NULL, "c", NULL); + P("{s:**}", "a", NULL); + P("{s:s*,s:o*,s:O*}", "a", NULL, "b", NULL, "c", NULL); + P("[i,i,i]", 0, 1, 2); + P("[s,o,O]", NULL, NULL, NULL); + P("[**]", NULL); + P("[s*,o*,O*]", NULL, NULL, NULL); + P(" s ", "test"); + P("[ ]"); + P("[ i , i, i ] ", 1, 2, 3); + P("{\n\n1"); + P("[}"); + P("{]"); + P("["); + P("{"); + P("[i]a", 42); + P("ia", 42); + P("s", NULL); + P("+", NULL); + P(NULL); + P("{s:i}", NULL, 1); + P("{ {}: s }", "foo"); + P("{ s: {}, s:[ii{} }", "foo", "bar", 12, 13); + P("[[[[[ [[[[[ [[[[ }]]]] ]]]] ]]]]]"); + P("y", "???????hello>>>>>>>", (size_t)19); + P("Y", "???????hello>>>>>>>", (size_t)19); + P("{sy?}", "foo", "hi", (size_t)2); + P("{sy?}", "foo", NULL, 0); + P("{sy*}", "foo", "hi", (size_t)2); + P("{sy*}", "foo", NULL, 0); + + U("true", "b", &xi[0]); + U("false", "b", &xi[0]); + U("null", "n"); + U("42", "i", &xi[0]); + U("123456789", "I", &xI[0]); + U("3.14", "f", &xf[0]); + U("12345", "F", &xf[0]); + U("3.14", "F", &xf[0]); + U("\"foo\"", "s", &xs[0]); + U("\"foo\"", "s%", &xs[0], &xz[0]); + U("{}", "{}"); + U("[]", "[]"); + U("{}", "o", &xo[0]); + U("{}", "O", &xo[0]); + U("{\"foo\":42}", "{si}", "foo", &xi[0]); + U("[1,2,3]", "[i,i,i]", &xi[0], &xi[1], &xi[2]); + U("{\"a\":1,\"b\":2,\"c\":3}", "{s:i, s:i, s:i}", "a", &xi[0], "b", &xi[1], "c", &xi[2]); + U("42", "z"); + U("null", "[i]"); + U("[]", "[}"); + U("{}", "{]"); + U("[]", "["); + U("{}", "{"); + U("[42]", "[i]a", &xi[0]); + U("42", "ia", &xi[0]); + U("[]", NULL); + U("\"foo\"", "s", NULL); + U("42", "s", NULL); + U("42", "n"); + U("42", "b", NULL); + U("42", "f", NULL); + U("42", "[i]", NULL); + U("42", "{si}", "foo", NULL); + U("\"foo\"", "n"); + U("\"foo\"", "b", NULL); + U("\"foo\"", "i", NULL); + U("\"foo\"", "I", NULL); + U("\"foo\"", "f", NULL); + U("\"foo\"", "F", NULL); + U("true", "s", NULL); + U("true", "n"); + U("true", "i", NULL); + U("true", "I", NULL); + U("true", "f", NULL); + U("true", "F", NULL); + U("[42]", "[ii]", &xi[0], &xi[1]); + U("{\"foo\":42}", "{si}", NULL, &xi[0]); + U("{\"foo\":42}", "{si}", "baz", &xi[0]); + U("[1,2,3]", "[iii!]", &xi[0], &xi[1], &xi[2]); + U("[1,2,3]", "[ii!]", &xi[0], &xi[1]); + U("[1,2,3]", "[ii]", &xi[0], &xi[1]); + U("[1,2,3]", "[ii*]", &xi[0], &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{sisi}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{sisi*}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{sisi!}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{si}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{si*}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42,\"baz\":45}", "{si!}", "baz", &xi[0], "foo", &xi[1]); + U("[1,{\"foo\":2,\"bar\":null},[3,4]]", "[i{sisn}[ii]]", &xi[0], "foo", &xi[1], "bar", &xi[2], &xi[3]); + U("[1,2,3]", "[ii!i]", &xi[0], &xi[1], &xi[2]); + U("[1,2,3]", "[ii*i]", &xi[0], &xi[1], &xi[2]); + U("{\"foo\":1,\"bar\":2}", "{si!si}", "foo", &xi[1], "bar", &xi[2]); + U("{\"foo\":1,\"bar\":2}", "{si*si}", "foo", &xi[1], "bar", &xi[2]); + U("{\"foo\":{\"baz\":null,\"bar\":null}}", "{s{sn!}}", "foo", "bar"); + U("[[1,2,3]]", "[[ii!]]", &xi[0], &xi[1]); + U("{}", "{s?i}", "foo", &xi[0]); + U("{\"foo\":1}", "{s?i}", "foo", &xi[0]); + U("{}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); + U("{\"foo\":[1,2]}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); + U("{\"bar\":{\"baz\":{\"quux\":15}}}", "{s?[ii]s?{s{si!}}}", "foo", &xi[0], &xi[1], "bar", "baz", "quux", &xi[2]); + U("{\"foo\":{\"bar\":4}}", "{s?{s?i}}", "foo", "bar", &xi[0]); + U("{\"foo\":{}}", "{s?{s?i}}", "foo", "bar", &xi[0]); + U("{}", "{s?{s?i}}", "foo", "bar", &xi[0]); + U("{\"foo\":42,\"baz\":45}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]); + U("{\"foo\":42}", "{s?isi!}", "baz", &xi[0], "foo", &xi[1]); + + U("\"Pz8_Pz8_P2hlbGxvPj4-Pj4-Pg\"", "y", &xy[0], &xz[0]); + U("\"\"", "y", &xy[0], &xz[0]); + U("null", "y", &xy[0], &xz[0]); + U("{\"foo\":\"Pz8_Pz8_P2hlbGxvPj4-Pj4-Pg\"}", "{s?y}", "foo", &xy[0], &xz[0]); + U("{\"foo\":\"\"}", "{s?y}", "foo", &xy[0], &xz[0]); + U("{}", "{s?y}", "foo", &xy[0], &xz[0]); + + c("null", "null", 1, 1); + c("true", "true", 1, 1); + c("false", "false", 1, 1); + c("1", "1", 1, 1); + c("1.0", "1.0", 1, 1); + c("\"\"", "\"\"", 1, 1); + c("\"hi\"", "\"hi\"", 1, 1); + c("{}", "{}", 1, 1); + c("{\"a\":true,\"b\":false}", "{\"b\":false,\"a\":true}", 1, 1); + c("[]", "[]", 1, 1); + c("[1,true,null]", "[1,true,null]", 1, 1); + + c("null", "true", 0, 0); + c("null", "false", 0, 0); + c("0", "1", 0, 0); + c("1", "0", 0, 0); + c("0", "true", 0, 0); + c("0", "false", 0, 0); + c("0", "null", 0, 0); + + c("\"hi\"", "\"hello\"", 0, 0); + c("\"hello\"", "\"hi\"", 0, 0); + + c("{}", "null", 0, 0); + c("{}", "true", 0, 0); + c("{}", "1", 0, 0); + c("{}", "1.0", 0, 0); + c("{}", "[]", 0, 0); + c("{}", "\"x\"", 0, 0); + + c("[1,true,null]", "[1,true]", 0, 1); + c("{\"a\":true,\"b\":false}", "{\"a\":true}", 0, 1); + c("{\"a\":true,\"b\":false}", "{\"a\":true,\"c\":false}", 0, 0); + c("{\"a\":true,\"c\":false}", "{\"a\":true,\"b\":false}", 0, 0); return 0; } #endif +#if 0 + + /* Unpack the same item twice */ + j = json_pack("{s:s, s:i, s:b}", "foo", "bar", "baz", 42, "quux", 1); + if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s)) + fail("json_unpack object with strict validation failed"); + { + const char *possible_errors[] = { + "2 object item(s) left unpacked: baz, quux", + "2 object item(s) left unpacked: quux, baz" + }; + check_errors(possible_errors, 2, "", 1, 10, 10); + } + json_decref(j); + +#endif