2016-10-19 12:10:03 +02:00
|
|
|
#ifndef JSON_LIB_INCLUDED
|
|
|
|
#define JSON_LIB_INCLUDED
|
|
|
|
|
2021-07-02 07:59:56 +02:00
|
|
|
#include <my_sys.h>
|
|
|
|
|
2016-10-19 12:10:03 +02:00
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define JSON_DEPTH_LIMIT 32
|
|
|
|
|
|
|
|
/*
|
|
|
|
When error happens, the c_next of the JSON engine contains the
|
|
|
|
character that caused the error, and the c_str is the position
|
|
|
|
in string where the error occurs.
|
|
|
|
*/
|
|
|
|
enum json_errors {
|
|
|
|
JE_BAD_CHR= -1, /* Invalid character, charset handler cannot read it. */
|
|
|
|
|
|
|
|
JE_NOT_JSON_CHR= -2, /* Character met not used in JSON. */
|
|
|
|
/* ASCII 00-08 for instance. */
|
|
|
|
|
|
|
|
JE_EOS= -3, /* Unexpected end of string. */
|
|
|
|
|
|
|
|
JE_SYN= -4, /* The next character breaks the JSON syntax. */
|
|
|
|
|
|
|
|
JE_STRING_CONST= -5, /* Character disallowed in string constant. */
|
|
|
|
|
|
|
|
JE_ESCAPING= -6, /* Error in the escaping. */
|
|
|
|
|
|
|
|
JE_DEPTH= -7, /* The limit on the JSON depth was overrun. */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct st_json_string_t
|
|
|
|
{
|
|
|
|
const uchar *c_str; /* Current position in JSON string */
|
|
|
|
const uchar *str_end; /* The end on the string. */
|
|
|
|
my_wc_t c_next; /* UNICODE of the last read character */
|
2023-04-13 13:13:30 +02:00
|
|
|
int c_next_len; /* character lenght of the last read character. */
|
2016-10-19 12:10:03 +02:00
|
|
|
int error; /* error code. */
|
|
|
|
|
|
|
|
CHARSET_INFO *cs; /* Character set of the JSON string. */
|
|
|
|
|
|
|
|
my_charset_conv_mb_wc wc; /* UNICODE conversion function. */
|
|
|
|
/* It's taken out of the cs just to speed calls. */
|
|
|
|
} json_string_t;
|
|
|
|
|
|
|
|
|
|
|
|
void json_string_set_cs(json_string_t *s, CHARSET_INFO *i_cs);
|
|
|
|
void json_string_set_str(json_string_t *s,
|
|
|
|
const uchar *str, const uchar *end);
|
|
|
|
#define json_next_char(j) \
|
2023-04-13 13:13:30 +02:00
|
|
|
((j)->c_next_len= (j)->wc((j)->cs, &(j)->c_next, (j)->c_str, (j)->str_end))
|
2016-10-19 12:10:03 +02:00
|
|
|
#define json_eos(j) ((j)->c_str >= (j)->str_end)
|
|
|
|
/*
|
|
|
|
read_string_const_chr() reads the next character of the string constant
|
|
|
|
and saves it to the js->c_next.
|
|
|
|
It takes into account possible escapings, so if for instance
|
|
|
|
the string is '\b', the read_string_const_chr() sets 8.
|
|
|
|
*/
|
|
|
|
int json_read_string_const_chr(json_string_t *js);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Various JSON-related operations expect JSON path as a parameter.
|
|
|
|
The path is a string like this "$.keyA[2].*"
|
|
|
|
The path itself is a number of steps specifying either a key or a position
|
|
|
|
in an array. Some of them can be wildcards.
|
|
|
|
So the representation of the JSON path is the json_path_t class
|
|
|
|
containing an array of json_path_step_t objects.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2016-12-09 09:26:32 +01:00
|
|
|
/* Path step types - actually bitmasks to let '&' or '|' operations. */
|
2016-10-19 12:10:03 +02:00
|
|
|
enum json_path_step_types
|
|
|
|
{
|
2016-12-09 09:26:32 +01:00
|
|
|
JSON_PATH_KEY_NULL=0,
|
|
|
|
JSON_PATH_KEY=1, /* Must be equal to JSON_VALUE_OBJECT. */
|
|
|
|
JSON_PATH_ARRAY=2, /* Must be equal to JSON_VALUE_ARRAY. */
|
|
|
|
JSON_PATH_KEY_OR_ARRAY=3,
|
|
|
|
JSON_PATH_WILD=4, /* Step like .* or [*] */
|
|
|
|
JSON_PATH_DOUBLE_WILD=8, /* Step like **.k or **[1] */
|
|
|
|
JSON_PATH_KEY_WILD= 1+4,
|
|
|
|
JSON_PATH_KEY_DOUBLEWILD= 1+8,
|
|
|
|
JSON_PATH_ARRAY_WILD= 2+4,
|
2021-11-22 18:29:30 +01:00
|
|
|
JSON_PATH_ARRAY_DOUBLEWILD= 2+8,
|
2022-03-04 20:33:49 +01:00
|
|
|
JSON_PATH_NEGATIVE_INDEX= 16,
|
|
|
|
JSON_PATH_ARRAY_RANGE= 32
|
2016-10-19 12:10:03 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct st_json_path_step_t
|
|
|
|
{
|
2016-12-09 09:26:32 +01:00
|
|
|
enum json_path_step_types type; /* The type of the step - */
|
|
|
|
/* see json_path_step_types */
|
2016-10-19 12:10:03 +02:00
|
|
|
const uchar *key; /* Pointer to the beginning of the key. */
|
|
|
|
const uchar *key_end; /* Pointer to the end of the key. */
|
2021-11-22 18:29:30 +01:00
|
|
|
int n_item; /* Item number in an array. No meaning for the key step. */
|
2022-03-04 20:33:49 +01:00
|
|
|
int n_item_end; /* Last index of the range. */
|
2016-10-19 12:10:03 +02:00
|
|
|
} json_path_step_t;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct st_json_path_t
|
|
|
|
{
|
|
|
|
json_string_t s; /* The string to be parsed. */
|
|
|
|
json_path_step_t steps[JSON_DEPTH_LIMIT]; /* Steps of the path. */
|
|
|
|
json_path_step_t *last_step; /* Points to the last step. */
|
|
|
|
|
|
|
|
int mode_strict; /* TRUE if the path specified as 'strict' */
|
2016-12-13 09:39:48 +01:00
|
|
|
enum json_path_step_types types_used; /* The '|' of all step's 'type'-s */
|
2016-10-19 12:10:03 +02:00
|
|
|
} json_path_t;
|
|
|
|
|
|
|
|
|
|
|
|
int json_path_setup(json_path_t *p,
|
|
|
|
CHARSET_INFO *i_cs, const uchar *str, const uchar *end);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
The set of functions and structures below provides interface
|
|
|
|
to the JSON text parser.
|
|
|
|
Running the parser normally goes like this:
|
|
|
|
|
|
|
|
json_engine_t j_eng; // structure keeps parser's data
|
|
|
|
json_scan_start(j_eng) // begin the parsing
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// The parser has read next piece of JSON
|
|
|
|
// and set fields of j_eng structure accordingly.
|
|
|
|
// So let's see what we have:
|
|
|
|
switch (j_eng.state)
|
|
|
|
{
|
|
|
|
case JST_KEY:
|
|
|
|
// Handle key name. See the json_read_keyname_chr()
|
|
|
|
// Probably compare it with the keyname we're looking for
|
|
|
|
case JST_VALUE:
|
|
|
|
// Handle value. It is either value of the key or an array item.
|
|
|
|
// see the json_read_value()
|
|
|
|
case JST_OBJ_START:
|
|
|
|
// parser found an object (the '{' in JSON)
|
|
|
|
case JST_OBJ_END:
|
|
|
|
// parser found the end of the object (the '}' in JSON)
|
|
|
|
case JST_ARRAY_START:
|
|
|
|
// parser found an array (the '[' in JSON)
|
|
|
|
case JST_ARRAY_END:
|
|
|
|
// parser found the end of the array (the ']' in JSON)
|
|
|
|
|
|
|
|
};
|
|
|
|
} while (json_scan_next() == 0); // parse next structure
|
|
|
|
|
|
|
|
|
|
|
|
if (j_eng.s.error) // we need to check why the loop ended.
|
|
|
|
// Did we get to the end of JSON, or came upon error.
|
|
|
|
{
|
|
|
|
signal_error_in_JSON()
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Parts of JSON can be quickly skipped. If we are not interested
|
|
|
|
in a particular key, we can just skip it with json_skip_key() call.
|
|
|
|
Similarly json_skip_level() goes right to the end of an object
|
|
|
|
or an array.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* These are JSON parser states that user can expect and handle. */
|
|
|
|
enum json_states {
|
|
|
|
JST_VALUE, /* value found */
|
|
|
|
JST_KEY, /* key found */
|
|
|
|
JST_OBJ_START, /* object */
|
|
|
|
JST_OBJ_END, /* object ended */
|
|
|
|
JST_ARRAY_START, /* array */
|
|
|
|
JST_ARRAY_END, /* array ended */
|
|
|
|
NR_JSON_USER_STATES
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
enum json_value_types
|
|
|
|
{
|
2021-05-23 16:16:01 +02:00
|
|
|
JSON_VALUE_UNINITIALIZED=0,
|
2016-12-09 09:26:32 +01:00
|
|
|
JSON_VALUE_OBJECT=1,
|
|
|
|
JSON_VALUE_ARRAY=2,
|
2018-12-11 22:49:39 +01:00
|
|
|
JSON_VALUE_STRING=3,
|
|
|
|
JSON_VALUE_NUMBER=4,
|
|
|
|
JSON_VALUE_TRUE=5,
|
|
|
|
JSON_VALUE_FALSE=6,
|
|
|
|
JSON_VALUE_NULL=7
|
2016-10-19 12:10:03 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-12-04 22:01:09 +01:00
|
|
|
enum json_num_flags
|
|
|
|
{
|
|
|
|
JSON_NUM_NEG=1, /* Number is negative. */
|
|
|
|
JSON_NUM_FRAC_PART=2, /* The fractional part is not empty. */
|
|
|
|
JSON_NUM_EXP=4, /* The number has the 'e' part. */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-10-19 12:10:03 +02:00
|
|
|
typedef struct st_json_engine_t
|
|
|
|
{
|
|
|
|
json_string_t s; /* String to parse. */
|
|
|
|
int sav_c_len; /* Length of the current character.
|
|
|
|
Can be more than 1 for multibyte charsets */
|
|
|
|
|
|
|
|
int state; /* The state of the parser. One of 'enum json_states'.
|
|
|
|
It tells us what construction of JSON we've just read. */
|
|
|
|
|
|
|
|
/* These values are only set after the json_read_value() call. */
|
|
|
|
enum json_value_types value_type; /* type of the value.*/
|
|
|
|
const uchar *value; /* Points to the value. */
|
|
|
|
const uchar *value_begin;/* Points to where the value starts in the JSON. */
|
2017-03-14 12:25:02 +01:00
|
|
|
int value_escaped; /* Flag telling if the string value has escaping.*/
|
2016-12-04 22:01:09 +01:00
|
|
|
uint num_flags; /* the details of the JSON_VALUE_NUMBER, is it negative,
|
|
|
|
or if it has the fractional part.
|
|
|
|
See the enum json_num_flags. */
|
2016-10-19 12:10:03 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
In most cases the 'value' and 'value_begin' are equal.
|
|
|
|
They only differ if the value is a string constants. Then 'value_begin'
|
|
|
|
points to the starting quotation mark, while the 'value' - to
|
|
|
|
the first character of the string.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const uchar *value_end; /* Points to the next character after the value. */
|
|
|
|
int value_len; /* The length of the value. Does not count quotations for */
|
|
|
|
/* string constants. */
|
|
|
|
|
|
|
|
int stack[JSON_DEPTH_LIMIT]; /* Keeps the stack of nested JSON structures. */
|
2017-01-24 14:34:44 +01:00
|
|
|
int stack_p; /* The 'stack' pointer. */
|
2021-07-02 19:13:26 +02:00
|
|
|
volatile uchar *killed_ptr;
|
2016-10-19 12:10:03 +02:00
|
|
|
} json_engine_t;
|
|
|
|
|
|
|
|
|
|
|
|
int json_scan_start(json_engine_t *je,
|
|
|
|
CHARSET_INFO *i_cs, const uchar *str, const uchar *end);
|
|
|
|
int json_scan_next(json_engine_t *j);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
json_read_keyname_chr() function assists parsing the name of an JSON key.
|
|
|
|
It only can be called when the json_engine is in JST_KEY.
|
|
|
|
The json_read_keyname_chr() reads one character of the name of the key,
|
|
|
|
and puts it in j_eng.s.next_c.
|
|
|
|
Typical usage is like this:
|
|
|
|
|
|
|
|
if (j_eng.state == JST_KEY)
|
|
|
|
{
|
|
|
|
while (json_read_keyname_chr(&j) == 0)
|
|
|
|
{
|
|
|
|
//handle next character i.e. match it against the pattern
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
int json_read_keyname_chr(json_engine_t *j);
|
|
|
|
|
|
|
|
|
2016-11-15 14:04:31 +01:00
|
|
|
/*
|
|
|
|
Check if the name of the current JSON key matches
|
|
|
|
the step of the path.
|
|
|
|
*/
|
|
|
|
int json_key_matches(json_engine_t *je, json_string_t *k);
|
|
|
|
|
|
|
|
|
2016-10-19 12:10:03 +02:00
|
|
|
/*
|
|
|
|
json_read_value() function parses the JSON value syntax,
|
|
|
|
so that we can handle the value of a key or an array item.
|
|
|
|
It only returns meaningful result when the engine is in
|
|
|
|
the JST_VALUE state.
|
|
|
|
|
|
|
|
Typical usage is like this:
|
|
|
|
|
|
|
|
if (j_eng.state == JST_VALUE)
|
|
|
|
{
|
|
|
|
json_read_value(&j_eng);
|
|
|
|
switch(j_eng.value_type)
|
|
|
|
{
|
|
|
|
case JSON_VALUE_STRING:
|
|
|
|
// get the string
|
|
|
|
str= j_eng.value;
|
|
|
|
str_length= j_eng.value_len;
|
|
|
|
case JSON_VALUE_NUMBER:
|
|
|
|
// get the number
|
|
|
|
... etc
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
int json_read_value(json_engine_t *j);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
json_skip_key() makes parser skip the content of the current
|
|
|
|
JSON key quickly.
|
|
|
|
It can be called only when the json_engine state is JST_KEY.
|
|
|
|
Typical usage is:
|
|
|
|
|
|
|
|
if (j_eng.state == JST_KEY)
|
|
|
|
{
|
|
|
|
if (key_does_not_match(j_eng))
|
|
|
|
json_skip_key(j_eng);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
int json_skip_key(json_engine_t *j);
|
|
|
|
|
|
|
|
|
2016-12-16 09:32:56 +01:00
|
|
|
typedef const int *json_level_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
json_skip_to_level() makes parser quickly get out of nested
|
|
|
|
loops and arrays. It is used when we're not interested in what is
|
|
|
|
there in the rest of these structures.
|
|
|
|
The 'level' should be remembered in advance.
|
|
|
|
json_level_t level= json_get_level(j);
|
|
|
|
.... // getting into the nested JSON structures
|
|
|
|
json_skip_to_level(j, level);
|
|
|
|
*/
|
|
|
|
#define json_get_level(j) (j->stack_p)
|
|
|
|
|
2017-01-24 14:34:44 +01:00
|
|
|
int json_skip_to_level(json_engine_t *j, int level);
|
2016-12-16 09:32:56 +01:00
|
|
|
|
2016-10-19 12:10:03 +02:00
|
|
|
/*
|
2018-02-24 18:42:13 +01:00
|
|
|
json_skip_level() works as above with just current structure.
|
2016-12-16 09:32:56 +01:00
|
|
|
So it gets to the end of the current JSON array or object.
|
2016-10-19 12:10:03 +02:00
|
|
|
*/
|
2016-12-16 09:32:56 +01:00
|
|
|
#define json_skip_level(json_engine) \
|
|
|
|
json_skip_to_level((json_engine), (json_engine)->stack_p)
|
2016-10-19 12:10:03 +02:00
|
|
|
|
|
|
|
|
2018-09-11 12:37:45 +02:00
|
|
|
/*
|
|
|
|
works as json_skip_level() but also counts items on the current
|
|
|
|
level skipped.
|
|
|
|
*/
|
|
|
|
int json_skip_level_and_count(json_engine_t *j, int *n_items_skipped);
|
|
|
|
|
2016-10-19 12:10:03 +02:00
|
|
|
#define json_skip_array_item json_skip_key
|
|
|
|
|
|
|
|
/*
|
|
|
|
Checks if the current value is of scalar type -
|
|
|
|
not an OBJECT nor ARRAY.
|
|
|
|
*/
|
|
|
|
#define json_value_scalar(je) ((je)->value_type > JSON_VALUE_ARRAY)
|
|
|
|
|
2016-12-10 22:12:33 +01:00
|
|
|
|
2016-10-19 12:10:03 +02:00
|
|
|
/*
|
|
|
|
Look for the JSON PATH in the json string.
|
|
|
|
Function can be called several times with same JSON/PATH to
|
|
|
|
find multiple matches.
|
|
|
|
On the first call, the json_engine_t parameter should be
|
|
|
|
initialized with the JSON string, and the json_path_t with the JSON path
|
|
|
|
appropriately. The 'p_cur_step' should point at the first
|
|
|
|
step of the path.
|
|
|
|
The 'array_counters' is the array of JSON_DEPTH_LIMIT size.
|
|
|
|
It stores the array counters of the parsed JSON.
|
|
|
|
If function returns 0, it means it found the match. The position of
|
|
|
|
the match is je->s.c_str. Then we can call the json_find_path()
|
|
|
|
with same engine/path/p_cur_step to get the next match.
|
|
|
|
Non-zero return means no matches found.
|
|
|
|
Check je->s.error to see if there was an error in JSON.
|
|
|
|
*/
|
|
|
|
int json_find_path(json_engine_t *je,
|
|
|
|
json_path_t *p, json_path_step_t **p_cur_step,
|
2021-11-22 18:29:30 +01:00
|
|
|
int *array_counters);
|
2016-10-19 12:10:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
typedef struct st_json_find_paths_t
|
|
|
|
{
|
|
|
|
uint n_paths;
|
|
|
|
json_path_t *paths;
|
|
|
|
uint cur_depth;
|
|
|
|
uint *path_depths;
|
2021-11-22 18:29:30 +01:00
|
|
|
int array_counters[JSON_DEPTH_LIMIT];
|
2016-10-19 12:10:03 +02:00
|
|
|
} json_find_paths_t;
|
|
|
|
|
|
|
|
|
|
|
|
int json_find_paths_first(json_engine_t *je, json_find_paths_t *state,
|
|
|
|
uint n_paths, json_path_t *paths, uint *path_depths);
|
|
|
|
int json_find_paths_next(json_engine_t *je, json_find_paths_t *state);
|
|
|
|
|
|
|
|
|
2021-10-10 10:51:04 +02:00
|
|
|
#define JSON_ERROR_OUT_OF_SPACE (-1)
|
|
|
|
#define JSON_ERROR_ILLEGAL_SYMBOL (-2)
|
|
|
|
|
2016-10-19 12:10:03 +02:00
|
|
|
/*
|
2022-12-19 22:08:20 +01:00
|
|
|
Convert JSON string constant into ordinary string constant
|
2016-10-19 12:10:03 +02:00
|
|
|
which can involve unpacking json escapes and changing character set.
|
|
|
|
Returns negative integer in the case of an error,
|
|
|
|
the length of the result otherwise.
|
|
|
|
*/
|
|
|
|
int json_unescape(CHARSET_INFO *json_cs,
|
|
|
|
const uchar *json_str, const uchar *json_end,
|
|
|
|
CHARSET_INFO *res_cs,
|
|
|
|
uchar *res, uchar *res_end);
|
|
|
|
|
|
|
|
/*
|
2021-10-10 10:51:04 +02:00
|
|
|
Convert a string constant into JSON string constant.
|
|
|
|
This can involve appropriate escaping and changing the character set.
|
|
|
|
Returns the length of the result on success,
|
|
|
|
on error returns a negative error code.
|
|
|
|
Some error codes:
|
|
|
|
JSON_ERROR_OUT_OF_SPACE Not enough space in the provided buffer
|
|
|
|
JSON_ERROR_ILLEGAL_SYMBOL Source symbol cannot be represented in JSON
|
2016-10-19 12:10:03 +02:00
|
|
|
*/
|
|
|
|
int json_escape(CHARSET_INFO *str_cs, const uchar *str, const uchar *str_end,
|
|
|
|
CHARSET_INFO *json_cs, uchar *json, uchar *json_end);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Appends the ASCII string to the json with the charset conversion.
|
|
|
|
*/
|
|
|
|
int json_append_ascii(CHARSET_INFO *json_cs,
|
|
|
|
uchar *json, uchar *json_end,
|
|
|
|
const uchar *ascii, const uchar *ascii_end);
|
|
|
|
|
2017-01-24 14:34:44 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Scan the JSON and return paths met one-by-one.
|
|
|
|
json_get_path_start(&p)
|
|
|
|
while (json_get_path_next(&p))
|
|
|
|
{
|
|
|
|
handle_the_next_path();
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
int json_get_path_start(json_engine_t *je, CHARSET_INFO *i_cs,
|
|
|
|
const uchar *str, const uchar *end,
|
|
|
|
json_path_t *p);
|
|
|
|
|
|
|
|
|
|
|
|
int json_get_path_next(json_engine_t *je, json_path_t *p);
|
|
|
|
|
2017-02-06 03:47:48 +01:00
|
|
|
int json_path_compare(const json_path_t *a, const json_path_t *b,
|
2021-11-22 18:29:30 +01:00
|
|
|
enum json_value_types vt, const int* array_size_counter);
|
2017-01-24 14:34:44 +01:00
|
|
|
|
2018-11-28 18:16:46 +01:00
|
|
|
int json_valid(const char *js, size_t js_len, CHARSET_INFO *cs);
|
2017-01-24 14:34:44 +01:00
|
|
|
|
2019-01-18 00:18:02 +01:00
|
|
|
int json_locate_key(const char *js, const char *js_end,
|
|
|
|
const char *kname,
|
|
|
|
const char **key_start, const char **key_end,
|
|
|
|
int *comma_pos);
|
|
|
|
|
2021-07-02 07:59:56 +02:00
|
|
|
int json_normalize(DYNAMIC_STRING *result,
|
|
|
|
const char *s, size_t size, CHARSET_INFO *cs);
|
|
|
|
|
2021-11-22 18:29:30 +01:00
|
|
|
int json_skip_array_and_count(json_engine_t *j, int* n_item);
|
|
|
|
|
2023-04-25 10:24:05 +02:00
|
|
|
inline static int json_scan_ended(json_engine_t *j)
|
|
|
|
{
|
|
|
|
return (j->state == JST_ARRAY_END && j->stack_p == 0);
|
|
|
|
}
|
|
|
|
|
2016-10-19 12:10:03 +02:00
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif /* JSON_LIB_INCLUDED */
|