wrap-json: Sync with app-afb-helpers-submodule
[src/app-framework-binder.git] / src / wrap-json.c
index 2354ea7..46a4d67 100644 (file)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2016, 2017 "IoT.bzh"
+ Copyright (C) 2016, 2017, 2018 "IoT.bzh"
 
  author: José Bollo <jose.bollo@iot.bzh>
 
@@ -39,15 +39,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 +68,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)
@@ -92,7 +94,160 @@ const char *wrap_json_get_error_string(int rc)
        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 (*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 +265,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;
@@ -121,9 +277,10 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
        top->cont = NULL;
        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)
@@ -217,6 +374,19 @@ int wrap_json_vpack(struct json_object **result, const char *desc, va_list args)
                        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);
+                       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;
+                       break;
                case '[':
                case '{':
                        if (++top >= &stack[STACKCOUNT])
@@ -336,17 +506,18 @@ int wrap_json_pack(struct json_object **result, const char *desc, ...)
 
 static int vunpack(struct json_object *object, const char *desc, va_list args, int store)
 {
-       int rc, optionnal, ignore;
+       int rc = 0, optionnal, ignore;
        char c, xacc[2] = { 0, 0 };
        const char *acc;
-       const char *d, *fit;
-       const char *key;
-       const char **ps;
-       double *pf;
-       int *pi;
-       int64_t *pI;
-       size_t *pz;
-       struct { struct json_object *parent; const char *acc; int index, count; char type; } stack[STACKCOUNT], *top;
+       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; size_t count; char type; } stack[STACKCOUNT], *top;
        struct json_object *obj;
        struct json_object **po;
 
@@ -354,9 +525,10 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i
        ignore = 0;
        top = NULL;
        acc = unpack_accept_any;
-       if (!desc)
+       d = desc;
+       if (!d)
                goto null_spec;
-       d = skip(desc);
+       d = skip(d);
        obj = object;
        for(;;) {
                fit = d;
@@ -475,6 +647,25 @@ 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 (!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 '{':
@@ -523,6 +714,7 @@ static int vunpack(struct json_object *object, const char *desc, va_list args, i
                                goto invalid_character;
                        if (!ignore && top->index != top->count)
                                goto incomplete;
+                       /*@fallthrough@*/
                case '*':
                        acc = xacc;
                        continue;
@@ -641,6 +833,68 @@ int wrap_json_unpack(struct json_object *object, const char *desc, ...)
        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)
+{
+       size_t n = 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 {
+               size_t n = json_object_array_length(object);
+               int i = 0;
+               while(i < n)
+                       callback(closure, json_object_array_get_idx(object, i++), NULL);
+       }
+}
+
 #if defined(WRAP_JSON_TEST)
 #include <stdio.h>
 
@@ -653,7 +907,7 @@ void p(const char *desc, ...)
        va_start(args, desc);
        rc = wrap_json_vpack(&result, desc, args);
        va_end(args);
-       if (!rc) 
+       if (!rc)
                printf("  SUCCESS %s\n\n", json_object_to_json_string(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));
@@ -666,6 +920,7 @@ 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, ...)
 {
@@ -684,7 +939,7 @@ void u(const char *value, const char *desc, ...)
        va_start(args, desc);
        rc = wrap_json_vunpack(obj, desc, args);
        va_end(args);
-       if (rc) 
+       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;
@@ -707,6 +962,14 @@ void u(const char *value, const char *desc, ...)
                        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 '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++;
@@ -774,6 +1037,8 @@ 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);
 
        U("true", "b", &xi[0]);
        U("false", "b", &xi[0]);
@@ -850,6 +1115,8 @@ 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]);
        return 0;
 }