summaryrefslogtreecommitdiff
path: root/src/lib/eina/eina_json.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/eina/eina_json.c')
-rw-r--r--src/lib/eina/eina_json.c1294
1 files changed, 1294 insertions, 0 deletions
diff --git a/src/lib/eina/eina_json.c b/src/lib/eina/eina_json.c
new file mode 100644
index 0000000000..4288249082
--- /dev/null
+++ b/src/lib/eina/eina_json.c
@@ -0,0 +1,1294 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2013 Yossi Kantor
+ * Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "eina_json.h"
+#include <string.h>
+#include <math.h>
+
+/*============================================================================*
+ * Local *
+ *============================================================================*/
+
+#define _RETURN_SW(st, retr) { Stm_Switch _sw; _sw.state = st;\
+ _sw.retrans = retr; return _sw; }
+
+#define _CASE_DIGIT case '0':case '1':case '2':\
+ case '3':case '4':case '5':\
+ case '6':case '7':case '8':\
+ case '9'
+
+#define _ISDIGIT(a) (a >= '0' && a <= '9')
+
+#define JSON_GLUE_BUFF_STEP 64
+
+static Eina_Json_Value *_eina_json_type_new(Eina_Json_Type type);
+
+typedef struct _Stm_State Stm_State;
+typedef struct _Stm_Machine Stm_Machine;
+typedef struct _Stm_switch Stm_Switch;
+typedef unsigned int Stm_Val;
+
+typedef Stm_Switch(*Stm_State_Cb)(Stm_State *state, Stm_Val token, void* data);
+
+struct _Stm_switch
+{
+ Stm_State *state;
+ Eina_Bool retrans;
+};
+
+struct _Stm_State
+{
+ Stm_State_Cb state_cb;
+ void *param_ptr1;
+ int param_int1;
+};
+
+struct _Stm_Machine
+{
+ Stm_State *initial_state;
+ Stm_State *current_state;
+ void *data;
+};
+
+struct _lex_keyword_param
+{
+ char* str;
+ int len;
+ Stm_Val token;
+};
+
+struct _Eina_Json_Value
+{
+ EINA_INLIST;
+ Eina_Json_Type type;
+ Eina_Json_Value *parent;
+ union
+ {
+ double number;
+ Eina_Strbuf *string;
+ Eina_Bool boolean;
+ Eina_Inlist *lst;
+ struct _Pair
+ {
+ Eina_Strbuf *name;
+ Eina_Json_Value *val;
+ } pair;
+ };
+};
+
+struct _Eina_Json_Context
+{
+ Stm_Machine lex_machine;
+ Stm_Machine syntax_machine;
+
+ Eina_Json_Error latest_error;
+
+ Eina_Json_Value *jobj;
+
+ Eina_Array *jstack;
+ void *jparent;
+ Eina_Json_Type jparent_type;
+ Eina_Json_Parser_Cb parser_cb;
+ void *cb_data;
+
+ const char *str;
+ const char *head;
+
+ Eina_Bool glue_on;
+ char *glue_buf;
+ unsigned glue_buf_size;
+ unsigned glue_len;
+
+ unsigned line;
+ unsigned col;
+};
+
+enum _Eina_Json_Token
+{
+ JSON_STRING = 1,
+ JSON_NUMBER,
+ JSON_TRUE,
+ JSON_FALSE,
+ JSON_NULL,
+ JSON_OBJ_OPEN = '{',
+ JSON_OBJ_CLOSE = '}',
+ JSON_ARR_OPEN = '[',
+ JSON_ARR_CLOSE = ']',
+ JSON_COMMA = ',',
+ JSON_COLON = ':'
+};
+
+// Syntax and Lexical parsing state's callbacks forward declaration
+static Stm_Switch _syntx_entry_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_value_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_new_object_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_object_name_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_object_colon_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_object_next_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_array_next_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_end_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_unexpected_cb(Stm_State *state, Stm_Val token, void *data);
+
+static Stm_Switch _lex_initial_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_int_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_frac_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_digit_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_exp_sign_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_exp_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_string_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_string_esc_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_keyword_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_unexpected_cb(Stm_State *state, Stm_Val token, void *data);
+
+// Syntax states
+static Stm_State syntx_entry = { _syntx_entry_cb };
+static Stm_State syntx_end = { _syntx_end_cb };
+static Stm_State syntx_new_object = { _syntx_new_object_cb };
+static Stm_State syntx_value = { _syntx_value_cb };
+static Stm_State syntx_object_name = { _syntx_object_name_cb };
+static Stm_State syntx_object_colon = { _syntx_object_colon_cb };
+static Stm_State syntx_next_object = { _syntx_object_next_cb };
+static Stm_State syntx_next_array = { _syntx_array_next_cb };
+static Stm_State syntx_unexpected = { _syntx_unexpected_cb };
+static const Stm_Switch switch_syntax_unexpected = { &syntx_unexpected, EINA_TRUE };
+
+// Lexical states
+static struct _lex_keyword_param lex_keyword_true = {"true", 4, JSON_TRUE};
+static struct _lex_keyword_param lex_keyword_false = {"false", 5, JSON_FALSE};
+static struct _lex_keyword_param lex_keyword_null = {"null", 4, JSON_NULL};
+
+static Stm_State lex_initial = {_lex_initial_cb };
+static Stm_State lex_int = { _lex_int_cb };
+static Stm_State lex_int_entry = {_lex_digit_cb, (void*)&lex_int };
+static Stm_State lex_fraction = {_lex_frac_cb };
+static Stm_State lex_fraction_entry = {_lex_digit_cb, (void*)&lex_fraction };
+static Stm_State lex_exp_sign = { _lex_exp_sign_cb };
+static Stm_State lex_exp = { _lex_exp_cb };
+static Stm_State lex_exp_entry = {_lex_digit_cb, (void*)&lex_exp };
+static Stm_State lex_string = {_lex_string_cb };
+static Stm_State lex_string_esc = {_lex_string_esc_cb };
+static Stm_State lex_unexpected = { _lex_unexpected_cb };
+static Stm_State lex_true = {_lex_keyword_cb, (void*)&lex_keyword_true };
+static Stm_State lex_false = {_lex_keyword_cb, (void*)&lex_keyword_false };
+static Stm_State lex_null = {_lex_keyword_cb, (void*)&lex_keyword_null };
+static const Stm_Switch switch_lex_unexpected = { &lex_unexpected, EINA_TRUE };
+
+
+void *
+_eina_json_parser_dom_cb(Eina_Json_Type type, void *parent, const char *text, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ Eina_Json_Value *newjval = NULL;
+
+ switch(type)
+ {
+ case EINA_JSON_TYPE_NULL:
+ newjval = eina_json_null_new();
+ break;
+ case EINA_JSON_TYPE_NUMBER:
+ newjval = eina_json_number_new(atof(text));
+ break;
+ case EINA_JSON_TYPE_STRING:
+ newjval = eina_json_string_new(text);
+ break;
+ case EINA_JSON_TYPE_BOOLEAN:
+ newjval = eina_json_boolean_new((*text == 't') ? EINA_TRUE : EINA_FALSE );
+ break;
+ case EINA_JSON_TYPE_PAIR:
+ newjval = _eina_json_type_new(EINA_JSON_TYPE_PAIR);
+ if (newjval)
+ {
+ newjval->pair.name = eina_strbuf_new();
+ eina_strbuf_append(newjval->pair.name, text);
+ }
+ break;
+ case EINA_JSON_TYPE_OBJECT:
+ newjval = eina_json_object_new();
+ break;
+ case EINA_JSON_TYPE_ARRAY:
+ newjval = eina_json_array_new();
+ break;
+ }
+
+ if (!newjval) return NULL;
+
+ if (parent)
+ {
+ Eina_Json_Value *jcontainer = (Eina_Json_Value*)parent;
+ switch(jcontainer->type)
+ {
+ case EINA_JSON_TYPE_PAIR:
+ jcontainer->pair.val = newjval;
+ break;
+ case EINA_JSON_TYPE_OBJECT:
+ jcontainer->lst = eina_inlist_append(jcontainer->lst,
+ EINA_INLIST_GET(newjval));
+ break;
+ case EINA_JSON_TYPE_ARRAY:
+ eina_json_array_append(jcontainer, newjval);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ jsctx->jobj = newjval;
+ }
+ return newjval;
+}
+
+void
+_state_machine_feed(Stm_Machine *machine, Stm_Val token)
+{
+ while (machine->current_state)
+ {
+ Stm_Switch sw =
+ machine->current_state->state_cb
+ (machine->current_state, token, machine->data);
+
+ machine->current_state = sw.state;
+ if (!sw.retrans) break;
+ }
+}
+
+inline static void
+_syntax_token_process(Stm_Val token, Eina_Json_Context *data)
+{
+ _state_machine_feed(&(data->syntax_machine), token);
+}
+
+static Stm_Switch
+_syntx_entry_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ case JSON_OBJ_OPEN:
+ case JSON_ARR_OPEN:
+ _RETURN_SW(&syntx_value, EINA_TRUE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_value_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ Stm_State* nextstate = NULL;
+ Eina_Json_Type jtype;
+ Eina_Bool call_cb = EINA_TRUE;
+ void* valptr = NULL;
+
+ switch (token)
+ {
+ case JSON_OBJ_CLOSE:
+ case JSON_ARR_CLOSE:
+ if (eina_array_count(jsctx->jstack) < 1)
+ _RETURN_SW(&syntx_end, EINA_FALSE);
+ jsctx->jparent = eina_array_pop(jsctx->jstack);
+ jsctx->jparent_type = (Eina_Json_Type)eina_array_pop(jsctx->jstack);
+ call_cb = EINA_FALSE;
+ break;
+
+ case JSON_OBJ_OPEN:
+ nextstate = &syntx_new_object;
+ jtype = EINA_JSON_TYPE_OBJECT;
+ break;
+
+ case JSON_ARR_OPEN:
+ nextstate = &syntx_value;
+ jtype = EINA_JSON_TYPE_ARRAY;
+ break;
+
+ case JSON_NUMBER: jtype = EINA_JSON_TYPE_NUMBER; break;
+ case JSON_STRING: jtype = EINA_JSON_TYPE_STRING; break;
+ case JSON_TRUE: case JSON_FALSE: jtype = EINA_JSON_TYPE_BOOLEAN; break;
+ case JSON_NULL: jtype = EINA_JSON_TYPE_NULL; break;
+
+ default:
+ return switch_syntax_unexpected;
+ }
+
+ if (call_cb)
+ {
+ valptr = jsctx->parser_cb(jtype, jsctx->jparent, jsctx->glue_buf,
+ jsctx->cb_data);
+
+ if (!valptr)
+ return switch_syntax_unexpected;
+ }
+
+ if (nextstate)
+ {
+ if (jsctx->jparent)
+ {
+ eina_array_push(jsctx->jstack, (void*)jsctx->jparent_type);
+ eina_array_push(jsctx->jstack, jsctx->jparent);
+ }
+ jsctx->jparent = valptr;
+ jsctx->jparent_type = jtype;
+ } else
+ {
+ if (jsctx->jparent_type == EINA_JSON_TYPE_PAIR)
+ {
+ jsctx->jparent = eina_array_pop(jsctx->jstack);
+ jsctx->jparent_type = (Eina_Json_Type)eina_array_pop(jsctx->jstack);
+ }
+ nextstate = (jsctx->jparent_type == EINA_JSON_TYPE_OBJECT) ?
+ &syntx_next_object :
+ &syntx_next_array;
+ }
+
+ _RETURN_SW(nextstate, EINA_FALSE);
+}
+
+static Stm_Switch
+_syntx_new_object_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ case JSON_OBJ_CLOSE:
+ _RETURN_SW(&syntx_value, EINA_TRUE);
+ case JSON_STRING:
+ _RETURN_SW(&syntx_object_name, EINA_TRUE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static
+Stm_Switch _syntx_object_name_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+
+ if (token == JSON_STRING)
+ {
+ eina_array_push(jsctx->jstack, (void*)jsctx->jparent_type);
+ eina_array_push(jsctx->jstack, jsctx->jparent);
+
+ jsctx->jparent_type = EINA_JSON_TYPE_PAIR;
+ jsctx->jparent = jsctx->parser_cb(EINA_JSON_TYPE_PAIR, jsctx->jparent,
+ jsctx->glue_buf, jsctx->cb_data);
+ if (jsctx->jparent) _RETURN_SW(&syntx_object_colon, EINA_FALSE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_object_colon_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ if (token == JSON_COLON)
+ _RETURN_SW(&syntx_value, EINA_FALSE);
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_object_next_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ case JSON_COMMA:
+ _RETURN_SW(&syntx_object_name, EINA_FALSE);
+ case JSON_OBJ_CLOSE:
+ _RETURN_SW(&syntx_value, EINA_TRUE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_array_next_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ case JSON_COMMA:
+ _RETURN_SW(&syntx_value, EINA_FALSE);
+ case JSON_ARR_CLOSE:
+ _RETURN_SW(&syntx_value, EINA_TRUE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_end_cb(Stm_State *state EINA_UNUSED, Stm_Val token EINA_UNUSED, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ jsctx->latest_error = EINA_JSON_ERROR_PAST_END;
+ _RETURN_SW(NULL, EINA_FALSE);
+}
+
+static Stm_Switch
+_syntx_unexpected_cb(Stm_State *state EINA_UNUSED, Stm_Val token EINA_UNUSED, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ if (jsctx->latest_error == EINA_JSON_ERROR_NONE)
+ jsctx->latest_error = EINA_JSON_ERROR_SYNTAX_TOKEN;
+ _RETURN_SW(NULL, EINA_FALSE);
+}
+
+Stm_Switch
+_lex_initial_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+
+ if (jsctx->glue_on)
+ {
+ jsctx->glue_buf[0]='\0';
+ jsctx->glue_len = 0;
+ jsctx->glue_on = EINA_FALSE;
+ }
+
+ switch (token)
+ {
+ case '}': case '{': case ',':
+ case ':': case '[': case ']':
+ _syntax_token_process(token, data);
+ _RETURN_SW (state, EINA_FALSE);
+
+ case '\n':
+ jsctx->line ++;
+ jsctx->col = 0;
+ case '\r': case '\t': case ' ':
+ _RETURN_SW (state, EINA_FALSE);
+
+ case '\"':
+ _RETURN_SW(&lex_string, EINA_FALSE);
+
+ _CASE_DIGIT:
+ _RETURN_SW(&lex_int_entry, EINA_TRUE);
+ case '-':
+ jsctx->glue_on = EINA_TRUE;
+ _RETURN_SW(&lex_int_entry, EINA_FALSE);
+
+ case 't':
+ _RETURN_SW(&lex_true, EINA_TRUE);
+ case 'f':
+ _RETURN_SW(&lex_false, EINA_TRUE);
+ case 'n':
+ _RETURN_SW(&lex_null, EINA_TRUE);
+ }
+ return switch_lex_unexpected;
+}
+
+static Stm_Switch
+_lex_digit_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ jsctx->glue_on = EINA_TRUE;
+
+ Stm_State* next_state = (Stm_State*)(state->param_ptr1);
+ if (_ISDIGIT(token))
+ _RETURN_SW(next_state, EINA_FALSE);
+
+ return switch_lex_unexpected;
+}
+
+static Stm_Switch
+_lex_int_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ switch (token)
+ {
+ _CASE_DIGIT:
+ _RETURN_SW(state, EINA_FALSE);
+ case 'e':
+ case 'E':
+ _RETURN_SW(&lex_exp_sign, EINA_FALSE);
+ case '.':
+ _RETURN_SW(&lex_fraction_entry, EINA_FALSE);
+ }
+ _syntax_token_process(JSON_NUMBER, data);
+ _RETURN_SW(&lex_initial, EINA_TRUE);
+}
+
+static Stm_Switch
+_lex_frac_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ switch (token)
+ {
+ _CASE_DIGIT:
+ _RETURN_SW(state, EINA_FALSE);
+ case 'e':
+ case 'E':
+ _RETURN_SW(&lex_exp_sign, EINA_FALSE);
+ }
+ _syntax_token_process(JSON_NUMBER, data);
+ _RETURN_SW (&lex_initial, EINA_TRUE);
+}
+
+static Stm_Switch
+_lex_exp_sign_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ _CASE_DIGIT:
+ _RETURN_SW (&lex_exp_entry, EINA_TRUE);
+ case '-':
+ case '+':
+ _RETURN_SW (&lex_exp_entry, EINA_FALSE);
+ }
+ return switch_lex_unexpected;
+}
+
+static Stm_Switch
+_lex_exp_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ switch (token)
+ {
+ _CASE_DIGIT:
+ _RETURN_SW(state, EINA_FALSE);
+ }
+ _syntax_token_process(JSON_NUMBER, data);
+ _RETURN_SW(&lex_initial, EINA_TRUE);
+}
+
+static Stm_Switch
+_lex_string_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ jsctx->glue_on = EINA_TRUE;
+
+ switch (token)
+ {
+ case '\\':
+ _RETURN_SW (&lex_string_esc, EINA_FALSE);
+ case '\"':
+ _syntax_token_process(JSON_STRING, data);
+ _RETURN_SW (&lex_initial, EINA_FALSE);
+ }
+ _RETURN_SW(state, EINA_FALSE);
+}
+
+static Stm_Switch
+_lex_string_esc_cb(Stm_State *state EINA_UNUSED, Stm_Val token EINA_UNUSED, void *data EINA_UNUSED)
+{
+ _RETURN_SW(&lex_string, EINA_FALSE);
+}
+
+static Stm_Switch
+_lex_keyword_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ struct _lex_keyword_param *param = state->param_ptr1;
+
+ char* cmpstr = param->str;
+ unsigned maxlen = param->len - 1;
+
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+
+ jsctx->glue_on = EINA_TRUE;
+
+ if ((jsctx->glue_len > maxlen) || (cmpstr[jsctx->glue_len] != (char)token))
+ return switch_lex_unexpected;
+
+ if (jsctx->glue_len == maxlen)
+ {
+ _syntax_token_process(param->token, data);
+ _RETURN_SW(&lex_initial, EINA_FALSE);
+ }
+ _RETURN_SW(state, EINA_FALSE);
+}
+
+static Stm_Switch
+_lex_unexpected_cb(Stm_State *state EINA_UNUSED, Stm_Val token EINA_UNUSED, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ if (jsctx->latest_error == EINA_JSON_ERROR_NONE)
+ jsctx->latest_error = EINA_JSON_ERROR_LEX_TOKEN;
+ _RETURN_SW(NULL, EINA_FALSE);
+}
+
+static Eina_Json_Context *
+_eina_json_context_new(Eina_Json_Parser_Cb cb, void *data)
+{
+ Eina_Json_Context *ret = calloc(1, sizeof(Eina_Json_Context));
+
+ if (ret)
+ {
+ ret->jstack = eina_array_new(10);
+
+ ret->glue_buf_size = JSON_GLUE_BUFF_STEP;
+ ret->glue_buf = malloc(ret->glue_buf_size);
+
+ ret->parser_cb = (cb) ? cb : _eina_json_parser_dom_cb;
+ ret->cb_data = (cb) ? data : ret;
+
+ if (!(ret->jstack && ret->glue_buf))
+ {
+ if (ret->jstack) free(ret->jstack);
+ if (ret->glue_buf) free(ret->glue_buf);
+ free(ret);
+ return NULL;
+ }
+
+ eina_json_context_reset(ret);
+ }
+ return ret;
+}
+
+static Eina_Bool
+_eina_json_context_parse(Eina_Json_Context *ctx, const char *text, unsigned text_len)
+{
+ unsigned lencount = (text_len) ? text_len : 1;
+
+ ctx->head = ctx->str = text;
+
+ if (ctx->latest_error)
+ return EINA_FALSE;
+
+ while (*ctx->head && lencount)
+ {
+ _state_machine_feed(&(ctx->lex_machine), *ctx->head);
+
+ if (ctx->glue_on)
+ {
+ if (ctx->glue_buf_size - ctx->glue_len < 2)
+ {
+ ctx->glue_buf_size += JSON_GLUE_BUFF_STEP;
+ ctx->glue_buf = realloc(ctx->glue_buf, ctx->glue_buf_size);
+ }
+ ctx->glue_buf[ctx->glue_len++] = *ctx->head;
+ ctx->glue_buf[ctx->glue_len] = '\0';
+ }
+
+ if (ctx->latest_error)
+ return EINA_FALSE;
+
+ ctx->head++;
+ ctx->col++;
+
+ if (text_len) lencount--;
+ }
+ return EINA_TRUE;
+}
+
+static Eina_Json_Value *
+_eina_json_parse(const char *text, unsigned size)
+{
+ Eina_Json_Value *ret = NULL;
+ Eina_Json_Context *ctx = eina_json_context_dom_new();
+ _eina_json_context_parse(ctx, text, size);
+ if (eina_json_context_completed_get(ctx))
+ ret = eina_json_context_dom_tree_take(ctx);
+ eina_json_context_free(ctx);
+ return ret;
+}
+
+static Eina_Json_Value *
+_eina_json_type_new(Eina_Json_Type type)
+{
+ Eina_Json_Value *ret = calloc(1, sizeof(Eina_Json_Value));
+ if (ret)
+ ret->type = type;
+ return ret;
+}
+
+static unsigned
+_eina_json_gen_count_get(Eina_Json_Value *obj)
+{
+ return eina_inlist_count(obj->lst);
+}
+
+static Eina_Json_Value *
+_eina_json_gen_nth_get(Eina_Json_Value *obj, unsigned idx)
+{
+ unsigned count = 0;
+ Eina_Inlist *l = NULL;
+ for (l = obj->lst; l; l = l->next)
+ if ((count++) == idx) break;
+ return EINA_INLIST_CONTAINER_GET(l, Eina_Json_Value);
+}
+
+static Eina_Json_Value *
+_eina_json_gen_append(Eina_Json_Value *obj, Eina_Json_Value *objadd)
+{
+ if (objadd->parent)
+ {
+ EINA_LOG_ERR("Can't append json object %p. "
+ "Its already belongs to another json object %p.\n",
+ objadd, objadd->parent);
+ return NULL;
+ }
+ obj->lst = eina_inlist_append(obj->lst, EINA_INLIST_GET(objadd));
+ objadd->parent = obj;
+ return objadd;
+}
+
+static Eina_Json_Value *
+_eina_json_gen_insert(Eina_Json_Value *obj, unsigned ind, Eina_Json_Value *jsnval)
+{
+ Eina_Json_Value *o = _eina_json_gen_nth_get(obj, ind);
+ if (!o && ind) return NULL;
+
+ if (jsnval->parent)
+ {
+ EINA_LOG_ERR("Can't insert json object %p. "
+ "Its already belongs to another json object %p.\n",
+ jsnval, jsnval->parent);
+ return NULL;
+ }
+
+ obj->lst = eina_inlist_prepend_relative(obj->lst,
+ EINA_INLIST_GET(jsnval),
+ EINA_INLIST_GET(o));
+ jsnval->parent = obj;
+ return jsnval;
+}
+
+static Eina_Bool
+_eina_json_gen_nth_remove(Eina_Json_Value *obj, unsigned ind)
+{
+ Eina_Json_Value *o = _eina_json_gen_nth_get(obj, ind);
+ if (!o) return EINA_FALSE;
+ obj->lst = eina_inlist_remove(obj->lst, EINA_INLIST_GET(o));
+ o->parent = NULL;
+ eina_json_value_free(o);
+ return EINA_TRUE;
+}
+
+static inline Eina_Iterator *
+_eina_json_gen_iterator_new(Eina_Json_Value *obj)
+{
+ return eina_inlist_iterator_new(obj->lst);
+}
+
+static void
+_eina_json_delim_print(Eina_Json_Value *jsnval, Eina_Strbuf *jtext,
+ unsigned ident0, unsigned identV, Eina_Bool objbreak)
+{
+ int newident;
+ Eina_Json_Value *jval;
+ Eina_Inlist *l = NULL;
+
+ switch (jsnval->type)
+ {
+ case EINA_JSON_TYPE_NULL:
+ eina_strbuf_append(jtext, "null");
+ break;
+
+ case EINA_JSON_TYPE_NUMBER:
+ if (ceil(jsnval->number) == jsnval->number)
+ eina_strbuf_append_printf(jtext, "%ld", (long)(jsnval->number));
+ else
+ eina_strbuf_append_printf(jtext, "%.2f", jsnval->number);
+ break;
+
+ case EINA_JSON_TYPE_STRING:
+ eina_strbuf_append_printf(jtext, "\"%s\"",
+ eina_strbuf_string_get(jsnval->string));
+ break;
+
+ case EINA_JSON_TYPE_BOOLEAN:
+ eina_strbuf_append(jtext, (jsnval->boolean) ? "true" : "false");
+ break;
+
+ case EINA_JSON_TYPE_PAIR:
+ eina_strbuf_append_printf(jtext, "\"%s\"",
+ eina_strbuf_string_get(jsnval->pair.name));
+ eina_strbuf_append(jtext, ":");
+ if (objbreak)
+ if (jsnval->pair.val->type == EINA_JSON_TYPE_OBJECT)
+ eina_strbuf_append_printf(jtext, "\n%*s", ident0, " ");
+ _eina_json_delim_print(jsnval->pair.val, jtext, ident0, identV, objbreak);
+ break;
+
+ case EINA_JSON_TYPE_ARRAY:
+ eina_strbuf_append_char(jtext, '[');
+
+ for (l = jsnval->lst; l; l = l->next)
+ {
+ jval = EINA_INLIST_CONTAINER_GET(l, Eina_Json_Value);
+ _eina_json_delim_print(jval, jtext, ident0, identV, objbreak);
+ if (l->next) eina_strbuf_append(jtext, (objbreak) ? ", " : ",");
+ }
+ eina_strbuf_append_char(jtext, ']');
+ break;
+
+ case EINA_JSON_TYPE_OBJECT:
+ if (!jsnval->lst)
+ {
+ eina_strbuf_append(jtext, "{}");
+ break;
+ }
+ newident = ident0 + identV;
+ eina_strbuf_append_char(jtext, '{');
+
+ for (l = jsnval->lst; l;l = l->next)
+ {
+ if (objbreak)
+ eina_strbuf_append_printf(jtext, "\n%*s", newident, " ");
+ jval = EINA_INLIST_CONTAINER_GET(l, Eina_Json_Value);
+ _eina_json_delim_print(jval, jtext, newident, identV, objbreak);
+ if (l->next) eina_strbuf_append(jtext, ",");
+ }
+ if (objbreak)
+ eina_strbuf_append_printf(jtext, "\n%*s", ident0, " ");
+
+ eina_strbuf_append_char(jtext, '}');
+ break;
+ }
+}
+
+/*============================================================================*
+ * JSON Parser API *
+ *============================================================================*/
+
+EAPI void
+eina_json_context_reset(Eina_Json_Context* ctx)
+{
+ Stm_Machine * lexm = &(ctx->lex_machine);
+ Stm_Machine * stxm = &(ctx->syntax_machine);
+
+ lexm->current_state = lexm->initial_state = &lex_initial;
+ stxm->current_state = stxm->initial_state = &syntx_entry;
+ stxm->data = lexm->data = ctx;
+
+ ctx->latest_error = EINA_JSON_ERROR_NONE;
+
+ ctx->glue_len = 0;
+ ctx->glue_buf[0] = '\0';
+ ctx->glue_on = EINA_FALSE;
+
+ eina_array_clean(ctx->jstack);
+ eina_json_value_free(ctx->jobj);
+ ctx->jobj = NULL;
+ ctx->jparent=NULL;
+
+ ctx->str = ctx->head = NULL;
+ ctx->line = ctx->col = 1;
+}
+
+EAPI Eina_Json_Context *
+eina_json_context_dom_new()
+{
+ return _eina_json_context_new(NULL, NULL);
+}
+
+EAPI Eina_Json_Context *
+eina_json_context_sax_new(Eina_Json_Parser_Cb cb, void* data)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL);
+ return _eina_json_context_new(cb, data);
+}
+
+EAPI void
+eina_json_context_free(Eina_Json_Context* ctx)
+{
+ if (!ctx) return;
+
+ eina_json_context_reset(ctx);
+ free(ctx->glue_buf);
+ eina_array_free(ctx->jstack);
+ free(ctx);
+}
+
+EAPI inline unsigned
+eina_json_context_line_get(Eina_Json_Context* ctx)
+{
+ return ctx->line;
+}
+
+EAPI inline unsigned
+eina_json_context_column_get(Eina_Json_Context* ctx)
+{
+ return ctx->col;
+}
+
+EAPI inline Eina_Json_Error
+eina_json_context_error_get(Eina_Json_Context* ctx)
+{
+ return ctx->latest_error;
+}
+
+EAPI inline Eina_Bool
+eina_json_context_completed_get(Eina_Json_Context* ctx)
+{
+ return (ctx->syntax_machine.current_state == &syntx_end);
+}
+
+EAPI Eina_Bool
+eina_json_context_unfinished_get(Eina_Json_Context* ctx)
+{
+ return (Eina_Bool)(!eina_json_context_completed_get(ctx) &&
+ !eina_json_context_error_get(ctx));
+}
+
+EAPI Eina_Json_Value *
+eina_json_context_dom_tree_take(Eina_Json_Context* ctx)
+{
+ if (!eina_json_context_completed_get(ctx))
+ {
+ EINA_LOG_ERR("Taking json tree from erroneous or uncompleted json context");
+ return NULL;
+ }
+ if (!ctx->jobj)
+ {
+ EINA_LOG_ERR("Json tree already taken for this json context");
+ return NULL;
+ }
+ Eina_Json_Value* ret = ctx->jobj;
+ ctx->jobj = NULL;
+ return ret;
+}
+
+EAPI inline Eina_Json_Value *
+eina_json_parse(const char *text)
+{
+ return _eina_json_parse(text, 0);
+}
+
+EAPI Eina_Json_Value *
+eina_json_parse_n(const char *text, unsigned text_len)
+{
+ if (!text_len) return NULL;
+ return _eina_json_parse(text, text_len);
+}
+
+EAPI Eina_Bool
+eina_json_context_parse(Eina_Json_Context *ctx, const char *text)
+{
+ return _eina_json_context_parse(ctx, text, 0);
+}
+
+EAPI Eina_Bool
+eina_json_context_parse_n(Eina_Json_Context *ctx, const char *text, unsigned text_len)
+{
+ if (!text_len) return !!(ctx->latest_error);
+ return _eina_json_context_parse(ctx, text, text_len);
+}
+
+/*============================================================================*
+ * JSON Value manipulation API *
+ *============================================================================*/
+
+EAPI Eina_Json_Value *
+eina_json_number_new(double num)
+{
+ Eina_Json_Value *ret = _eina_json_type_new(EINA_JSON_TYPE_NUMBER);
+ if (ret) ret->number = num;
+ return ret;
+}
+
+EAPI Eina_Json_Value *
+eina_json_string_new(const char* string)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(string, NULL);
+ Eina_Json_Value *ret = _eina_json_type_new(EINA_JSON_TYPE_STRING);
+ if (ret)
+ {
+ ret->string = eina_strbuf_new();
+ eina_strbuf_append(ret->string, string);
+ }
+ return ret;
+}
+
+EAPI Eina_Json_Value *
+eina_json_boolean_new(Eina_Bool boolval)
+{
+ Eina_Json_Value *ret = _eina_json_type_new(EINA_JSON_TYPE_BOOLEAN);
+ if (ret) ret->boolean = boolval;
+ return ret;
+}
+
+EAPI inline Eina_Json_Value *
+eina_json_null_new()
+{
+ return _eina_json_type_new(EINA_JSON_TYPE_NULL);
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_new()
+{
+ return _eina_json_type_new(EINA_JSON_TYPE_OBJECT);
+}
+
+EAPI Eina_Json_Value *
+eina_json_array_new()
+{
+ return _eina_json_type_new(EINA_JSON_TYPE_ARRAY);
+}
+
+EAPI void
+eina_json_value_free(Eina_Json_Value *jsnval)
+{
+ if (!jsnval) return;
+
+ if (jsnval->parent)
+ {
+ EINA_LOG_ERR("Can't free json object %p. It belongs to %p.\n",
+ jsnval, jsnval->parent);
+ return;
+ }
+
+ switch (jsnval->type)
+ {
+ case EINA_JSON_TYPE_STRING:
+ eina_strbuf_free(jsnval->string);
+ break;
+ case EINA_JSON_TYPE_PAIR:
+ eina_strbuf_free(jsnval->pair.name);
+ if (jsnval->pair.val) jsnval->pair.val->parent = NULL;
+ eina_json_value_free(jsnval->pair.val);
+ break;
+ case EINA_JSON_TYPE_OBJECT:
+ case EINA_JSON_TYPE_ARRAY:
+ while (jsnval->lst)
+ {
+ Eina_Json_Value *aux = EINA_INLIST_CONTAINER_GET(jsnval->lst,
+ Eina_Json_Value);
+ jsnval->lst = eina_inlist_remove(jsnval->lst, jsnval->lst);
+ aux->parent = NULL;
+ eina_json_value_free(aux);
+ }
+ default:
+ break;
+ }
+ free(jsnval);
+}
+
+EAPI inline
+Eina_Json_Type eina_json_type_get(Eina_Json_Value *jsnval)
+{
+ return jsnval->type;
+}
+
+EAPI Eina_Bool
+eina_json_number_set(Eina_Json_Value *jsnval, double num)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_NUMBER), EINA_FALSE);
+ jsnval->number = num;
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_json_string_set(Eina_Json_Value *jsnval, const char* string)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_STRING), EINA_FALSE);
+ eina_strbuf_reset(jsnval->string);
+ eina_strbuf_append(jsnval->string, string);
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_json_boolean_set(Eina_Json_Value *jsnval, Eina_Bool boolval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_BOOLEAN), EINA_FALSE);
+ jsnval->boolean = boolval;
+ return EINA_TRUE;
+}
+
+EAPI double
+eina_json_number_get(Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_NUMBER), 0.0);
+ return jsnval->number;
+}
+
+EAPI const char *
+eina_json_string_get(Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_STRING), NULL);
+ return eina_strbuf_string_get(jsnval->string);
+}
+
+EAPI Eina_Bool
+eina_json_boolean_get(Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_BOOLEAN), EINA_FALSE);
+ return jsnval->boolean;
+}
+
+EAPI const char *
+eina_json_pair_name_get(Eina_Json_Value *obj)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_PAIR), NULL);
+ return eina_strbuf_string_get(obj->pair.name);
+}
+
+EAPI Eina_Json_Value *
+eina_json_pair_value_get(Eina_Json_Value *obj)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_PAIR), NULL);
+ return obj->pair.val;
+}
+
+EAPI unsigned
+eina_json_object_count_get(Eina_Json_Value *obj)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), 0);
+ return _eina_json_gen_count_get(obj);
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_nth_get(Eina_Json_Value *obj, unsigned idx)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+ return _eina_json_gen_nth_get(obj, idx);
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_append(Eina_Json_Value *obj, const char* keyname, Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type == EINA_JSON_TYPE_PAIR), NULL);
+
+ if (jsnval->parent)
+ {
+ EINA_LOG_ERR("Can't append json object %p.Its already "
+ "belongs to another json object %p.\n",
+ jsnval, jsnval->parent);
+ return NULL;
+ }
+
+ Eina_Json_Value *pr = _eina_json_type_new(EINA_JSON_TYPE_PAIR);
+ pr->pair.name = eina_strbuf_new();
+ eina_strbuf_append(pr->pair.name, keyname);
+ pr->pair.val = jsnval;
+
+ Eina_Json_Value *ins = _eina_json_gen_append(obj, pr);
+ return (ins) ? pr : NULL;
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_insert(Eina_Json_Value *obj, unsigned idx, const char* keyname, Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type == EINA_JSON_TYPE_PAIR), NULL);
+
+ Eina_Json_Value *o = _eina_json_gen_nth_get(obj, idx);
+ if (!o && idx) return NULL;
+
+ if (jsnval->parent)
+ {
+ EINA_LOG_ERR("Can't insert json object %p.Its already "
+ "belongs to another json object %p.\n",
+ jsnval, jsnval->parent);
+ return NULL;
+ }
+
+ Eina_Json_Value *pr = _eina_json_type_new(EINA_JSON_TYPE_PAIR);
+ pr->pair.name = eina_strbuf_new();
+ eina_strbuf_append(pr->pair.name, keyname);
+ pr->pair.val = jsnval;
+ jsnval->parent = pr;
+
+ Eina_Json_Value *ins = _eina_json_gen_insert(obj, idx, pr);
+ return (ins) ? pr : NULL;
+}
+
+EAPI Eina_Bool
+eina_json_object_nth_remove(Eina_Json_Value *obj, unsigned idx)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), EINA_FALSE);
+ return _eina_json_gen_nth_remove(obj, idx);
+}
+
+EAPI Eina_Iterator *
+eina_json_object_iterator_new(Eina_Json_Value *obj)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+ return _eina_json_gen_iterator_new(obj);
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_value_get_internal(Eina_Json_Value *obj, ...)
+{
+ char* jkey;
+ va_list vl;
+ Eina_Json_Value *jval;
+ Eina_Inlist *l;
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+
+ jval = obj;
+ va_start(vl, obj);
+ while ((jkey = va_arg(vl, char*)))
+ {
+ if (jval->type != EINA_JSON_TYPE_OBJECT) return NULL;
+ for (l = jval->lst; l; l = l->next)
+ {
+ jval = EINA_INLIST_CONTAINER_GET(l, Eina_Json_Value);
+ if (!strcmp(eina_strbuf_string_get(jval->pair.name), jkey)) break;
+ jval = NULL;
+ }
+ if (!jval) break;
+ jval = jval->pair.val;
+ }
+ va_end(vl);
+
+ if (jval == obj) jval = NULL;
+
+ return jval;
+}
+
+EAPI unsigned
+eina_json_array_count_get(Eina_Json_Value *arr)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), 0);
+ return _eina_json_gen_count_get(arr);
+}
+
+EAPI Eina_Json_Value *
+eina_json_array_nth_get(Eina_Json_Value *arr, unsigned idx)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), NULL);
+ return _eina_json_gen_nth_get(arr, idx);
+}
+
+EAPI Eina_Json_Value *
+eina_json_array_append(Eina_Json_Value *arr, Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type == EINA_JSON_TYPE_PAIR), NULL);
+ return _eina_json_gen_append(arr, jsnval);
+}
+
+EAPI Eina_Json_Value *
+eina_json_array_insert(Eina_Json_Value *arr, unsigned idx, Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type == EINA_JSON_TYPE_PAIR), NULL);
+ return _eina_json_gen_insert(arr, idx, jsnval);
+}
+
+EAPI Eina_Bool
+eina_json_array_nth_remove(Eina_Json_Value *arr, unsigned idx)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), EINA_FALSE);
+ return _eina_json_gen_nth_remove(arr, idx);
+}
+
+EAPI Eina_Iterator *
+eina_json_array_iterator_new(Eina_Json_Value *arr)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), NULL);
+ return _eina_json_gen_iterator_new(arr);
+}
+
+EAPI char *
+eina_json_format_string_get(Eina_Json_Value *jsnval, Eina_Json_Format format)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((format > EINA_JSON_FORMAT_BASIC), NULL);
+
+ int ident0 = 0;
+ int identV = (format) ? 2 : 0;
+ int objbreak = (format) ? EINA_TRUE : EINA_FALSE;
+
+ Eina_Strbuf *jtext = eina_strbuf_new();
+ _eina_json_delim_print(jsnval, jtext, ident0, identV, objbreak);
+ char *ret = eina_strbuf_string_steal(jtext);
+ eina_strbuf_free(jtext);
+ return ret;
+}