Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/json/decode.c b/src/json/decode.c
new file mode 100644
index 0000000..067ecff
--- /dev/null
+++ b/src/json/decode.c
@@ -0,0 +1,469 @@
+/**
+ * @file json/decode.c JSON decoder
+ *
+ * Copyright (C) 2010 - 2015 Creytiv.com
+ */
+
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_list.h>
+#include <re_hash.h>
+#include <re_odict.h>
+#include <re_json.h>
+
+
+static inline long double mypower10(uint64_t e)
+{
+ long double p = 10, n = 1;
+
+ while (e > 0) {
+ if (e & 1)
+ n *= p;
+
+ p *= p;
+ e >>= 1;
+ }
+
+ return n;
+}
+
+
+static bool is_string(struct pl *c, const struct pl *pl)
+{
+ if (pl->l < 2)
+ return false;
+
+ if (pl->p[0] != '"'|| pl->p[pl->l-1] != '"')
+ return false;
+
+ c->p = pl->p + 1;
+ c->l = pl->l - 2;
+
+ return true;
+}
+
+
+static bool is_number(long double *d, bool *isfloat, const struct pl *pl)
+{
+ bool neg = false, pos = false, frac = false, exp = false;
+ long double v = 0, mul = 1;
+ const char *p;
+ int64_t e = 0;
+
+ if (!pl->l)
+ return false;
+
+ p = &pl->p[pl->l];
+
+ while (p > pl->p) {
+
+ const char ch = *--p;
+
+ if (ch == 'e' || ch == 'E') {
+
+ if (exp || frac)
+ return false;
+
+ exp = true;
+ e = neg ? -v : v;
+ v = 0;
+ mul = 1;
+ neg = false;
+ pos = false;
+ }
+ else if (pos || neg) {
+ return false;
+ }
+ else if (ch == '.') {
+
+ if (frac)
+ return false;
+
+ frac = true;
+ v /= mul;
+ mul = 1;
+ }
+ else if ('0' <= ch && ch <= '9') {
+ v += mul * (ch - '0');
+ mul *= 10;
+ }
+ else if (ch == '-') {
+ neg = true;
+ }
+ else if (ch == '+') {
+ pos = true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ *isfloat = (frac || exp);
+
+ if (exp) {
+ if (e < 0)
+ v /= mypower10(-e);
+ else
+ v *= mypower10(e);
+ }
+
+ if (neg)
+ v = -v;
+
+ *d = v;
+
+ return true;
+}
+
+
+static int decode_name(char **str, const struct pl *pl)
+{
+ struct pl pls;
+
+ if (!pl->p)
+ return EBADMSG;
+
+ if (!is_string(&pls, pl))
+ return EBADMSG;
+
+ return re_sdprintf(str, "%H", utf8_decode, &pls);
+}
+
+
+static int decode_value(struct json_value *val, const struct pl *pl)
+{
+ long double dbl;
+ struct pl pls;
+ bool isfloat;
+ int err = 0;
+
+ if (!pl->p)
+ return EBADMSG;
+
+ if (is_string(&pls, pl)) {
+
+ err = re_sdprintf(&val->v.str, "%H", utf8_decode, &pls);
+ val->type = JSON_STRING;
+ }
+ else if (is_number(&dbl, &isfloat, pl)) {
+
+ if (isfloat) {
+ val->type = JSON_DOUBLE;
+ val->v.dbl = dbl;
+ }
+ else {
+ val->type = JSON_INT;
+ val->v.integer = dbl;
+ }
+ }
+ else if (!pl_strcasecmp(pl, "false")) {
+
+ val->v.boolean = false;
+ val->type = JSON_BOOL;
+ }
+ else if (!pl_strcasecmp(pl, "true")) {
+
+ val->v.boolean = true;
+ val->type = JSON_BOOL;
+ }
+ else if (!pl_strcasecmp(pl, "null")) {
+
+ val->type = JSON_NULL;
+ }
+ else {
+ re_printf("json: value of unknown type: <%r>\n", pl);
+ err = EBADMSG;
+ }
+
+ return err;
+}
+
+
+static int object_entry(const struct pl *pl_name, const struct pl *pl_val,
+ json_object_entry_h *oeh, void *arg)
+{
+ struct json_value val;
+ char *name;
+ int err;
+
+ err = decode_name(&name, pl_name);
+ if (err)
+ return err;
+
+ err = decode_value(&val, pl_val);
+ if (err)
+ goto out;
+
+ if (oeh)
+ err = oeh(name, &val, arg);
+
+ if (val.type == JSON_STRING)
+ mem_deref(val.v.str);
+
+ out:
+ mem_deref(name);
+
+ return err;
+}
+
+
+static int array_entry(unsigned idx, const struct pl *pl_val,
+ json_array_entry_h *aeh, void *arg)
+{
+ struct json_value val;
+ int err;
+
+ err = decode_value(&val, pl_val);
+ if (err)
+ return err;
+
+ if (aeh)
+ err = aeh(idx, &val, arg);
+
+ if (val.type == JSON_STRING)
+ mem_deref(val.v.str);
+
+ return err;
+}
+
+
+static int object_start(const struct pl *pl_name, unsigned idx,
+ struct json_handlers *h)
+{
+ char *name = NULL;
+ int err = 0;
+
+ if (pl_name->p) {
+
+ err = decode_name(&name, pl_name);
+ if (err)
+ return err;
+ }
+
+ if (h->oh)
+ err = h->oh(name, idx, h);
+
+ mem_deref(name);
+
+ return err;
+}
+
+
+static int array_start(const struct pl *pl_name, unsigned idx,
+ struct json_handlers *h)
+{
+ char *name = NULL;
+ int err = 0;
+
+ if (pl_name->p) {
+
+ err = decode_name(&name, pl_name);
+ if (err)
+ return err;
+ }
+
+ if (h->ah)
+ err = h->ah(name, idx, h);
+
+ mem_deref(name);
+
+ return err;
+}
+
+
+static inline int chkval(struct pl *val, const char *p)
+{
+ if (!val->p || p<val->p)
+ return EINVAL;
+
+ val->l = p - val->p;
+
+ return 0;
+}
+
+
+static int _json_decode(const char **str, size_t *len,
+ unsigned depth, unsigned maxdepth,
+ json_object_h *oh, json_array_h *ah,
+ json_object_entry_h *oeh, json_array_entry_h *aeh,
+ void *arg)
+{
+ bool esc = false, inquot = false, inobj = false, inarray = false;
+ struct pl name = PL_INIT, val = PL_INIT;
+ size_t ws = 0;
+ unsigned idx = 0;
+ int err;
+
+ for (; *len>0; ++(*str), --(*len)) {
+
+ if (inquot) {
+ if (esc)
+ esc = false;
+ else if (**str == '\"')
+ inquot = false;
+ else if (**str == '\\')
+ esc = true;
+
+ continue;
+ }
+
+ switch (**str) {
+
+ case ':':
+ if (!inobj || name.p || chkval(&val, *str - ws))
+ return EBADMSG;
+
+ name = val;
+ val = pl_null;
+ break;
+
+ case ',':
+ if (chkval(&val, *str - ws))
+ break;
+
+ if (inobj) {
+
+ if (!name.p)
+ return EBADMSG;
+
+ err = object_entry(&name, &val, oeh, arg);
+ if (err)
+ return err;
+ }
+ else if (inarray) {
+
+ err = array_entry(idx, &val, aeh, arg);
+ if (err)
+ return err;
+
+ ++idx;
+ }
+ else
+ return EBADMSG;
+
+ name = pl_null;
+ val = pl_null;
+ break;
+
+ case '{':
+ if (inobj || inarray) {
+
+ struct json_handlers h = {oh,ah,oeh,aeh,arg};
+
+ if (depth >= maxdepth)
+ return EOVERFLOW;
+
+ if (inobj && !name.p)
+ return EBADMSG;
+
+ err = object_start(&name, idx, &h);
+ if (err)
+ return err;
+
+ name = pl_null;
+
+ err = _json_decode(str, len, depth + 1,
+ maxdepth, h.oh, h.ah,
+ h.oeh, h.aeh, h.arg);
+ if (err)
+ return err;
+
+ if (inarray)
+ ++idx;
+ }
+ else {
+ inobj = true;
+ }
+ break;
+
+ case '[':
+ if (inobj || inarray) {
+
+ struct json_handlers h = {oh,ah,oeh,aeh,arg};
+
+ if (depth >= maxdepth)
+ return EOVERFLOW;
+
+ if (inobj && !name.p)
+ return EBADMSG;
+
+ err = array_start(&name, idx, &h);
+ if (err)
+ return err;
+
+ name = pl_null;
+
+ err = _json_decode(str, len, depth + 1,
+ maxdepth, h.oh, h.ah,
+ h.oeh, h.aeh, h.arg);
+ if (err)
+ return err;
+
+ if (inarray)
+ ++idx;
+ }
+ else {
+ inarray = true;
+ idx = 0;
+ }
+ break;
+
+ case '}':
+ if (!inobj)
+ return EBADMSG;
+
+ if (chkval(&val, *str - ws))
+ return 0;
+
+ if (!name.p)
+ return EBADMSG;
+
+ return object_entry(&name, &val, oeh, arg);
+
+ case ']':
+ if (!inarray)
+ return EBADMSG;
+
+ if (chkval(&val, *str - ws))
+ return 0;
+
+ return array_entry(idx, &val, aeh, arg);
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ ++ws;
+ break;
+
+ default:
+ if (val.p)
+ break;
+
+ if (**str == '\"')
+ inquot = true;
+
+ val.p = *str;
+ val.l = 0;
+ ws = 0;
+ break;
+ }
+ }
+
+ if (inobj || inarray)
+ return EBADMSG;
+
+ return 0;
+}
+
+
+int json_decode(const char *str, size_t len, unsigned maxdepth,
+ json_object_h *oh, json_array_h *ah,
+ json_object_entry_h *oeh, json_array_entry_h *aeh, void *arg)
+{
+ if (!str)
+ return EINVAL;
+
+ return _json_decode(&str, &len, 0, maxdepth, oh, ah, oeh, aeh, arg);
+}
diff --git a/src/json/decode_odict.c b/src/json/decode_odict.c
new file mode 100644
index 0000000..cd64aff
--- /dev/null
+++ b/src/json/decode_odict.c
@@ -0,0 +1,125 @@
+/**
+ * @file json/decode_odict.c JSON odict decode
+ *
+ * Copyright (C) 2010 - 2015 Creytiv.com
+ */
+
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_list.h>
+#include <re_hash.h>
+#include <re_odict.h>
+#include <re_json.h>
+
+
+static int container_add(const char *name, unsigned idx,
+ enum odict_type type, struct json_handlers *h)
+{
+ struct odict *o = h->arg, *oc;
+ char index[64];
+ int err;
+
+ if (!name) {
+ if (re_snprintf(index, sizeof(index), "%u", idx) < 0)
+ return ENOMEM;
+
+ name = index;
+ }
+
+ err = odict_alloc(&oc, hash_bsize(o->ht));
+ if (err)
+ return err;
+
+ err = odict_entry_add(o, name, type, oc);
+ mem_deref(oc);
+ h->arg = oc;
+
+ return err;
+}
+
+
+static int object_handler(const char *name, unsigned idx,
+ struct json_handlers *h)
+{
+ return container_add(name, idx, ODICT_OBJECT, h);
+}
+
+
+static int array_handler(const char *name, unsigned idx,
+ struct json_handlers *h)
+{
+ return container_add(name, idx, ODICT_ARRAY, h);
+}
+
+
+static int entry_add(struct odict *o, const char *name,
+ const struct json_value *val)
+{
+ switch (val->type) {
+
+ case JSON_STRING:
+ return odict_entry_add(o, name, ODICT_STRING, val->v.str);
+
+ case JSON_INT:
+ return odict_entry_add(o, name, ODICT_INT, val->v.integer);
+
+ case JSON_DOUBLE:
+ return odict_entry_add(o, name, ODICT_DOUBLE, val->v.dbl);
+
+ case JSON_BOOL:
+ return odict_entry_add(o, name, ODICT_BOOL, val->v.boolean);
+
+ case JSON_NULL:
+ return odict_entry_add(o, name, ODICT_NULL);
+
+ default:
+ return ENOSYS;
+ }
+}
+
+
+static int object_entry_handler(const char *name, const struct json_value *val,
+ void *arg)
+{
+ struct odict *o = arg;
+
+ return entry_add(o, name, val);
+}
+
+
+static int array_entry_handler(unsigned idx, const struct json_value *val,
+ void *arg)
+{
+ struct odict *o = arg;
+ char index[64];
+
+ if (re_snprintf(index, sizeof(index), "%u", idx) < 0)
+ return ENOMEM;
+
+ return entry_add(o, index, val);
+}
+
+
+int json_decode_odict(struct odict **op, uint32_t hash_size, const char *str,
+ size_t len, unsigned maxdepth)
+{
+ struct odict *o;
+ int err;
+
+ if (!op || !str)
+ return EINVAL;
+
+ err = odict_alloc(&o, hash_size);
+ if (err)
+ return err;
+
+ err = json_decode(str, len, maxdepth, object_handler, array_handler,
+ object_entry_handler, array_entry_handler, o);
+ if (err)
+ mem_deref(o);
+ else
+ *op = o;
+
+ return err;
+}
diff --git a/src/json/encode.c b/src/json/encode.c
new file mode 100644
index 0000000..fa56de3
--- /dev/null
+++ b/src/json/encode.c
@@ -0,0 +1,99 @@
+/**
+ * @file json/encode.c JSON encoder
+ *
+ * Copyright (C) 2010 - 2015 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_list.h>
+#include <re_odict.h>
+#include <re_json.h>
+
+
+static int encode_entry(struct re_printf *pf, const struct odict_entry *e)
+{
+ struct odict *array;
+ struct le *le;
+ int err;
+
+ if (!e)
+ return 0;
+
+ switch (e->type) {
+
+ case ODICT_OBJECT:
+ err = json_encode_odict(pf, e->u.odict);
+ break;
+
+ case ODICT_ARRAY:
+ array = e->u.odict;
+ if (!array)
+ return 0;
+
+ err = re_hprintf(pf, "[");
+
+ for (le=array->lst.head; le; le=le->next) {
+
+ const struct odict_entry *ae = le->data;
+
+ err |= re_hprintf(pf, "%H%s",
+ encode_entry, ae,
+ le->next ? "," : "");
+ }
+
+ err |= re_hprintf(pf, "]");
+ break;
+
+ case ODICT_INT:
+ err = re_hprintf(pf, "%lld", e->u.integer);
+ break;
+
+ case ODICT_DOUBLE:
+ err = re_hprintf(pf, "%f", e->u.dbl);
+ break;
+
+ case ODICT_STRING:
+ err = re_hprintf(pf, "\"%H\"", utf8_encode, e->u.str);
+ break;
+
+ case ODICT_BOOL:
+ err = re_hprintf(pf, "%s", e->u.boolean ? "true" : "false");
+ break;
+
+ case ODICT_NULL:
+ err = re_hprintf(pf, "null");
+ break;
+
+ default:
+ re_fprintf(stderr, "json: unsupported type %d\n", e->type);
+ err = EINVAL;
+ }
+
+ return err;
+}
+
+
+int json_encode_odict(struct re_printf *pf, const struct odict *o)
+{
+ struct le *le;
+ int err;
+
+ if (!o)
+ return 0;
+
+ err = re_hprintf(pf, "{");
+
+ for (le=o->lst.head; le; le=le->next) {
+
+ const struct odict_entry *e = le->data;
+
+ err |= re_hprintf(pf, "\"%H\":%H%s",
+ utf8_encode, e->key,
+ encode_entry, e,
+ le->next ? "," : "");
+ }
+
+ err |= re_hprintf(pf, "}");
+
+ return err;
+}
diff --git a/src/json/mod.mk b/src/json/mod.mk
new file mode 100644
index 0000000..731ddc5
--- /dev/null
+++ b/src/json/mod.mk
@@ -0,0 +1,9 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 - 2015 Creytiv.com
+#
+
+SRCS += json/decode.c
+SRCS += json/decode_odict.c
+SRCS += json/encode.c