json: added module with json encode/decode functionality

This commit is contained in:
Richard Aas 2015-10-15 06:53:29 +00:00
parent b6fba247a3
commit 8e93e8817d
8 changed files with 750 additions and 0 deletions

View file

@ -31,6 +31,7 @@ MODULES += mod conf
MODULES += bfcp
MODULES += aes srtp
MODULES += odict
MODULES += json
INSTALL := install
ifeq ($(DESTDIR),)

View file

@ -33,6 +33,7 @@ Modules:
* httpauth testing HTTP-based Authentication (RFC 2617)
* ice unstable Interactive Connectivity Establishment (ICE)
* jbuf testing Jitter buffer
* json unstable JavaScript Object Notation (JSON)
* list stable Sortable doubly-linked list handling
* lock testing Resource locking functions
* main testing Main poll loop

View file

@ -40,6 +40,7 @@ extern "C" {
#include "re_mqueue.h"
#include "re_net.h"
#include "re_odict.h"
#include "re_json.h"
#include "re_rtp.h"
#include "re_sdp.h"
#include "re_uri.h"

50
include/re_json.h Normal file
View file

@ -0,0 +1,50 @@
/**
* @file re_json.h Interface to JavaScript Object Notation (JSON) -- RFC 7159
*
* Copyright (C) 2010 - 2015 Creytiv.com
*/
enum json_typ {
JSON_STRING,
JSON_INT,
JSON_DOUBLE,
JSON_BOOL,
JSON_NULL,
};
struct json_value {
union {
char *str;
int64_t integer;
double dbl;
bool boolean;
} v;
enum json_typ type;
};
struct json_handlers;
typedef int (json_object_entry_h)(const char *name,
const struct json_value *value, void *arg);
typedef int (json_array_entry_h)(unsigned idx,
const struct json_value *value, void *arg);
typedef int (json_object_h)(const char *name, unsigned idx,
struct json_handlers *h);
typedef int (json_array_h)(const char *name, unsigned idx,
struct json_handlers *h);
struct json_handlers {
json_object_h *oh;
json_array_h *ah;
json_object_entry_h *oeh;
json_array_entry_h *aeh;
void *arg;
};
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);
int json_decode_odict(struct odict **op, uint32_t hash_size, const char *str,
size_t len, unsigned maxdepth);
int json_encode_odict(struct re_printf *pf, const struct odict *o);

464
src/json/decode.c Normal file
View file

@ -0,0 +1,464 @@
/**
* @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 uint64_t mypower10(uint64_t e)
{
uint64_t i, n = 1;
for (i=0; i<e; i++)
n *= 10;
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 && e < 0));
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 unkown 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;
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;
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);
}

125
src/json/decode_odict.c Normal file
View file

@ -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;
}

99
src/json/encode.c Normal file
View file

@ -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;
}

9
src/json/mod.mk Normal file
View file

@ -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