X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fwrap-json.c;h=6fce73b20f8e1656b53a5debbb39be2747ef69bb;hb=65353dce81a629e042800bb7b86fcd869a76727e;hp=9bb8d192795913ed7eeb1c6b8a4dc186039a48b6;hpb=d7933c0f61a3316cc3d1ca7e74d30e193de2f612;p=src%2Fapp-framework-binder.git diff --git a/src/wrap-json.c b/src/wrap-json.c index 9bb8d192..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,6 +17,7 @@ */ #include +#include #include "wrap-json.h" @@ -39,15 +40,16 @@ enum { 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 pack_accept_arr[] = "][{snbiIfoO"; +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[] = "*!][{snbiIfFoO"; +static const char unpack_accept_arr[] = "*!][{snbiIfFoOyY"; static const char unpack_accept_key[] = "*!s}"; #define unpack_accept_any (&unpack_accept_arr[3]) @@ -67,7 +69,8 @@ static const char *pack_errors[_wrap_json_error_count_] = [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_key_not_found] = "key not found", + [wrap_json_error_bad_base64] = "bad base64 encoding" }; int wrap_json_get_error_position(int rc) @@ -87,12 +90,165 @@ int wrap_json_get_error_code(int rc) const char *wrap_json_get_error_string(int rc) { rc = wrap_json_get_error_code(rc); - if (rc >= sizeof pack_errors / sizeof *pack_errors) + 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) { @@ -110,6 +266,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) 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; @@ -131,7 +288,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) goto truncated; if (!strchr(top->acc, c)) goto invalid_character; - d = skip(++d); + d = skip(d + 1); switch(c) { case 's': nullable = 0; @@ -143,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; @@ -212,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]) @@ -260,7 +441,7 @@ 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) @@ -273,7 +454,7 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args) 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 = pack_accept_key; @@ -347,7 +528,8 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i int *pi = NULL; int64_t *pI = NULL; size_t *pz = NULL; - struct { struct json_object *parent; const char *acc; int index, count; char type; } stack[STACKCOUNT], *top; + 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; @@ -367,7 +549,7 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i goto truncated; if (!strchr(acc, c)) goto invalid_character; - d = skip(++d); + d = skip(d + 1); switch(c) { case 's': if (xacc[0] == '}') { @@ -379,7 +561,7 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i optionnal = 0; else { optionnal = 1; - d = skip(++d); + d = skip(d + 1); } if (ignore) ignore++; @@ -409,7 +591,7 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i *ps = json_object_get_string(obj); } if (*d == '%') { - d = skip(++d); + d = skip(d + 1); if (store) { pz = va_arg(args, size_t *); if (!ignore && pz) @@ -477,6 +659,32 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i } } 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 '{': @@ -495,7 +703,7 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i if (!ignore) { if (!json_object_is_type(obj, json_type_array)) goto missfit; - top->count = json_object_array_length(obj); + top->count = (int)json_object_array_length(obj); } xacc[0] = ']'; acc = unpack_accept_arr; @@ -607,7 +815,7 @@ int wrap_json_check(struct json_object *object, const char *desc, ...) va_list args; va_start(args, desc); - rc = vunpack(object, desc, args, 0); + rc = wrap_json_vcheck(object, desc, args); va_end(args); return rc; } @@ -623,9 +831,9 @@ int wrap_json_match(struct json_object *object, const char *desc, ...) va_list args; va_start(args, desc); - rc = vunpack(object, desc, args, 0); + rc = wrap_json_vmatch(object, desc, args); va_end(args); - return !rc; + return rc; } int wrap_json_vunpack(struct json_object *object, const char *desc, va_list args) @@ -656,7 +864,7 @@ static void object_for_all(struct json_object *object, void (*callback)(void*,st static void array_for_all(struct json_object *object, void (*callback)(void*,struct json_object*), void *closure) { - int n = json_object_array_length(object); + int n = (int)json_object_array_length(object); int i = 0; while(i < n) callback(closure, json_object_array_get_idx(object, i++)); @@ -699,15 +907,369 @@ void wrap_json_for_all(struct json_object *object, void (*callback)(void*,struct else if (!json_object_is_type(object, json_type_array)) callback(closure, object, NULL); else { - int n = json_object_array_length(object); + 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); + + 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, ...) { @@ -719,9 +1281,10 @@ void p(const char *desc, ...) rc = wrap_json_vpack(&result, desc, args); va_end(args); if (!rc) - printf(" SUCCESS %s\n\n", json_object_to_json_string(result)); + printf(" SUCCESS %s\n\n", j2t(result)); else 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); } @@ -731,23 +1294,25 @@ 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 *obj, *o; + 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); - obj = json_tokener_parse(value); + object = json_tokener_parse(value); va_start(args, desc); - rc = wrap_json_vunpack(obj, desc, args); + 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)); @@ -770,8 +1335,16 @@ void u(const char *value, const char *desc, ...) 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", json_object_to_json_string(*va_arg(args, struct json_object**))); k = m&1; break; - case 'O': o = *va_arg(args, struct json_object**); printf(" O:%s", json_object_to_json_string(o)); json_object_put(o); 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++; @@ -779,7 +1352,30 @@ void u(const char *value, const char *desc, ...) va_end(args); printf("\n\n"); } - json_object_put(obj); + 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) @@ -839,6 +1435,12 @@ int main() 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]); @@ -915,6 +1517,48 @@ int main() 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; }