コード例 #1
0
ファイル: json_parse.c プロジェクト: gongchuang/spdk
ssize_t
spdk_json_parse(void *json, size_t size, struct spdk_json_val *values, size_t num_values,
		void **end, uint32_t flags)
{
	uint8_t *json_end = json + size;
	enum spdk_json_val_type containers[SPDK_JSON_MAX_NESTING_DEPTH];
	size_t con_value[SPDK_JSON_MAX_NESTING_DEPTH];
	enum spdk_json_val_type con_type = SPDK_JSON_VAL_INVALID;
	bool trailing_comma = false;
	size_t depth = 0; /* index into containers */
	size_t cur_value = 0; /* index into values */
	size_t con_start_value;
	uint8_t *data = json;
	uint8_t *new_data;
	int rc;
	const struct json_literal *lit;
	enum {
		STATE_VALUE, /* initial state */
		STATE_VALUE_SEPARATOR, /* value separator (comma) */
		STATE_NAME, /* "name": value */
		STATE_NAME_SEPARATOR, /* colon */
		STATE_END, /* parsed the complete value, so only whitespace is valid */
	} state = STATE_VALUE;

#define ADD_VALUE(t, val_start_ptr, val_end_ptr) \
	if (values && cur_value < num_values) { \
		values[cur_value].type = t; \
		values[cur_value].start = val_start_ptr; \
		values[cur_value].len = val_end_ptr - val_start_ptr; \
	} \
	cur_value++

	while (data < json_end) {
		uint8_t c = *data;

		switch (c) {
		case ' ':
		case '\t':
		case '\r':
		case '\n':
			/* Whitespace is allowed between any tokens. */
			data++;
			break;

		case 't':
		case 'f':
		case 'n':
			/* true, false, or null */
			if (state != STATE_VALUE) return SPDK_JSON_PARSE_INVALID;
			lit = &g_json_literals[(c >> 3) & 3]; /* See comment above g_json_literals[] */
			assert(lit->str[0] == c);
			rc = match_literal(data, json_end, lit->str, lit->len);
			if (rc < 0) return rc;
			ADD_VALUE(lit->type, data, data + rc);
			data += rc;
			state = depth ? STATE_VALUE_SEPARATOR : STATE_END;
			trailing_comma = false;
			break;

		case '"':
			if (state != STATE_VALUE && state != STATE_NAME) return SPDK_JSON_PARSE_INVALID;
			rc = json_decode_string(data, json_end, &new_data, flags);
			if (rc < 0) return rc;
			/*
			 * Start is data + 1 to skip initial quote.
			 * Length is data + rc - 1 to skip both quotes.
			 */
			ADD_VALUE(state == STATE_VALUE ? SPDK_JSON_VAL_STRING : SPDK_JSON_VAL_NAME,
				  data + 1, data + rc - 1);
			data = new_data;
			if (state == STATE_NAME) {
				state = STATE_NAME_SEPARATOR;
			} else {
				state = depth ? STATE_VALUE_SEPARATOR : STATE_END;
			}
			trailing_comma = false;
			break;

		case '-':
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			if (state != STATE_VALUE) return SPDK_JSON_PARSE_INVALID;
			rc = json_valid_number(data, json_end);
			if (rc < 0) return rc;
			ADD_VALUE(SPDK_JSON_VAL_NUMBER, data, data + rc);
			data += rc;
			state = depth ? STATE_VALUE_SEPARATOR : STATE_END;
			trailing_comma = false;
			break;

		case '{':
		case '[':
			if (state != STATE_VALUE) return SPDK_JSON_PARSE_INVALID;
			if (depth == SPDK_JSON_MAX_NESTING_DEPTH) {
				return SPDK_JSON_PARSE_MAX_DEPTH_EXCEEDED;
			}
			if (c == '{') {
				con_type = SPDK_JSON_VAL_OBJECT_BEGIN;
				state = STATE_NAME;
			} else {
				con_type = SPDK_JSON_VAL_ARRAY_BEGIN;
				state = STATE_VALUE;
			}
			con_value[depth] = cur_value;
			containers[depth++] = con_type;
			ADD_VALUE(con_type, data, data + 1);
			data++;
			trailing_comma = false;
			break;

		case '}':
		case ']':
			if (trailing_comma) return SPDK_JSON_PARSE_INVALID;
			if (depth == 0) return SPDK_JSON_PARSE_INVALID;
			con_type = containers[--depth];
			con_start_value = con_value[depth];
			if (values && con_start_value < num_values) {
				values[con_start_value].len = cur_value - con_start_value - 1;
			}
			if (c == '}') {
				if (state != STATE_NAME && state != STATE_VALUE_SEPARATOR) {
					return SPDK_JSON_PARSE_INVALID;
				}
				if (con_type != SPDK_JSON_VAL_OBJECT_BEGIN) {
					return SPDK_JSON_PARSE_INVALID;
				}
				ADD_VALUE(SPDK_JSON_VAL_OBJECT_END, data, data + 1);
			} else {
				if (state != STATE_VALUE && state != STATE_VALUE_SEPARATOR) {
					return SPDK_JSON_PARSE_INVALID;
				}
				if (con_type != SPDK_JSON_VAL_ARRAY_BEGIN) {
					return SPDK_JSON_PARSE_INVALID;
				}
				ADD_VALUE(SPDK_JSON_VAL_ARRAY_END, data, data + 1);
			}
			con_type = depth == 0 ? SPDK_JSON_VAL_INVALID : containers[depth - 1];
			data++;
			state = depth ? STATE_VALUE_SEPARATOR : STATE_END;
			trailing_comma = false;
			break;

		case ',':
			if (state != STATE_VALUE_SEPARATOR) return SPDK_JSON_PARSE_INVALID;
			data++;
			assert(con_type == SPDK_JSON_VAL_ARRAY_BEGIN ||
			       con_type == SPDK_JSON_VAL_OBJECT_BEGIN);
			state = con_type == SPDK_JSON_VAL_ARRAY_BEGIN ? STATE_VALUE : STATE_NAME;
			trailing_comma = true;
			break;

		case ':':
			if (state != STATE_NAME_SEPARATOR) return SPDK_JSON_PARSE_INVALID;
			data++;
			state = STATE_VALUE;
			break;

		case '/':
			if (!(flags & SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS)) {
				return SPDK_JSON_PARSE_INVALID;
			}
			rc = json_valid_comment(data, json_end);
			if (rc < 0) return rc;
			/* Skip over comment */
			data += rc;
			break;

		default:
			return SPDK_JSON_PARSE_INVALID;
		}

		if (state == STATE_END) {
			break;
		}
	}

	if (state == STATE_END) {
		/* Skip trailing whitespace */
		while (data < json_end) {
			uint8_t c = *data;

			if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
				data++;
			} else {
				break;
			}
		}

		/*
		 * These asserts are just for sanity checking - they are guaranteed by the allowed
		 *  state transitions.
		 */
		assert(depth == 0);
		assert(trailing_comma == false);
		assert(data <= json_end);
		if (end) {
			*end = data;
		}
		return cur_value;
	}

	/* Invalid end state - ran out of data */
	if (end) {
		*end = data;
	}
	return SPDK_JSON_PARSE_INCOMPLETE;
}
コード例 #2
0
ファイル: json.c プロジェクト: KamarajuKusumanchi/vim
/*
 * Decode one item and put it in "res".  If "res" is NULL only advance.
 * Must already have skipped white space.
 *
 * Return FAIL for a decoding error (and give an error).
 * Return MAYBE for an incomplete message.
 */
    static int
json_decode_item(js_read_T *reader, typval_T *res, int options)
{
    char_u	*p;
    int		len;
    int		retval;
    garray_T	stack;
    typval_T	item;
    typval_T	*cur_item;
    json_dec_item_T *top_item;
    char_u	key_buf[NUMBUFLEN];

    ga_init2(&stack, sizeof(json_dec_item_T), 100);
    cur_item = res;
    init_tv(&item);
    if (res != NULL)
    init_tv(res);

    fill_numbuflen(reader);
    p = reader->js_buf + reader->js_used;
    for (;;)
    {
	top_item = NULL;
	if (stack.ga_len > 0)
	{
	    top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len - 1;
	    json_skip_white(reader);
	    p = reader->js_buf + reader->js_used;
	    if (*p == NUL)
	    {
		retval = MAYBE;
		if (top_item->jd_type == JSON_OBJECT)
		    /* did get the key, clear it */
		    clear_tv(&top_item->jd_key_tv);
		goto theend;
	    }
	    if (top_item->jd_type == JSON_OBJECT_KEY
					    || top_item->jd_type == JSON_ARRAY)
	    {
		/* Check for end of object or array. */
		if (*p == (top_item->jd_type == JSON_ARRAY ? ']' : '}'))
		{
		    ++reader->js_used; /* consume the ']' or '}' */
		    --stack.ga_len;
		    if (stack.ga_len == 0)
		    {
			retval = OK;
			goto theend;
		    }
		    if (cur_item != NULL)
			cur_item = &top_item->jd_tv;
		    goto item_end;
		}
	    }
	}

	if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY
		&& (options & JSON_JS)
		&& reader->js_buf[reader->js_used] != '"'
		&& reader->js_buf[reader->js_used] != '\''
		&& reader->js_buf[reader->js_used] != '['
		&& reader->js_buf[reader->js_used] != '{')
	{
	    char_u *key;

	    /* accept an object key that is not in quotes */
	    key = p = reader->js_buf + reader->js_used;
	    while (*p != NUL && *p != ':' && *p > ' ')
		++p;
	    if (cur_item != NULL)
	    {
		cur_item->v_type = VAR_STRING;
		cur_item->vval.v_string = vim_strnsave(key, (int)(p - key));
		top_item->jd_key = cur_item->vval.v_string;
	    }
	    reader->js_used += (int)(p - key);
	}
	else
	{
	    switch (*p)
	    {
		case '[': /* start of array */
		    if (top_item && top_item->jd_type == JSON_OBJECT_KEY)
		    {
			retval = FAIL;
			break;
		    }
		    if (ga_grow(&stack, 1) == FAIL)
		    {
			retval = FAIL;
			break;
		    }
		    if (cur_item != NULL && rettv_list_alloc(cur_item) == FAIL)
		    {
			cur_item->v_type = VAR_SPECIAL;
			cur_item->vval.v_number = VVAL_NONE;
			retval = FAIL;
			break;
		    }

		    ++reader->js_used; /* consume the '[' */
		    top_item = ((json_dec_item_T *)stack.ga_data)
								+ stack.ga_len;
		    top_item->jd_type = JSON_ARRAY;
		    ++stack.ga_len;
		    if (cur_item != NULL)
		    {
			top_item->jd_tv = *cur_item;
			cur_item = &item;
		    }
		    continue;

		case '{': /* start of object */
		    if (top_item && top_item->jd_type == JSON_OBJECT_KEY)
		    {
			retval = FAIL;
			break;
		    }
		    if (ga_grow(&stack, 1) == FAIL)
		    {
			retval = FAIL;
			break;
		    }
		    if (cur_item != NULL && rettv_dict_alloc(cur_item) == FAIL)
		    {
			cur_item->v_type = VAR_SPECIAL;
			cur_item->vval.v_number = VVAL_NONE;
			retval = FAIL;
			break;
		    }

		    ++reader->js_used; /* consume the '{' */
		    top_item = ((json_dec_item_T *)stack.ga_data)
								+ stack.ga_len;
		    top_item->jd_type = JSON_OBJECT_KEY;
		    ++stack.ga_len;
		    if (cur_item != NULL)
		    {
			top_item->jd_tv = *cur_item;
			cur_item = &top_item->jd_key_tv;
		    }
		    continue;

		case '"': /* string */
		    retval = json_decode_string(reader, cur_item, *p);
		    break;

		case '\'':
		    if (options & JSON_JS)
			retval = json_decode_string(reader, cur_item, *p);
		    else
		    {
			EMSG(_(e_invarg));
			retval = FAIL;
		    }
		    break;

		case ',': /* comma: empty item */
		    if ((options & JSON_JS) == 0)
		    {
			EMSG(_(e_invarg));
			retval = FAIL;
			break;
		    }
		    /* FALLTHROUGH */
		case NUL: /* empty */
		    if (cur_item != NULL)
		    {
			cur_item->v_type = VAR_SPECIAL;
			cur_item->vval.v_number = VVAL_NONE;
		    }
		    retval = OK;
		    break;

		default:
		    if (VIM_ISDIGIT(*p) || *p == '-')
		    {
#ifdef FEAT_FLOAT
			char_u  *sp = p;

			if (*sp == '-')
			{
			    ++sp;
			    if (*sp == NUL)
			    {
				retval = MAYBE;
				break;
			    }
			    if (!VIM_ISDIGIT(*sp))
			    {
				EMSG(_(e_invarg));
				retval = FAIL;
				break;
			    }
			}
			sp = skipdigits(sp);
			if (*sp == '.' || *sp == 'e' || *sp == 'E')
			{
			    if (cur_item == NULL)
			    {
				float_T f;

				len = string2float(p, &f);
			    }
			    else
			    {
				cur_item->v_type = VAR_FLOAT;
				len = string2float(p, &cur_item->vval.v_float);
			    }
			}
			else
#endif
			{
			    varnumber_T nr;

			    vim_str2nr(reader->js_buf + reader->js_used,
				    NULL, &len, 0, /* what */
				    &nr, NULL, 0);
			    if (cur_item != NULL)
			    {
				cur_item->v_type = VAR_NUMBER;
				cur_item->vval.v_number = nr;
			    }
			}
			reader->js_used += len;
			retval = OK;
			break;
		    }
		    if (STRNICMP((char *)p, "false", 5) == 0)
		    {
			reader->js_used += 5;
			if (cur_item != NULL)
			{
			    cur_item->v_type = VAR_SPECIAL;
			    cur_item->vval.v_number = VVAL_FALSE;
			}
			retval = OK;
			break;
		    }
		    if (STRNICMP((char *)p, "true", 4) == 0)
		    {
			reader->js_used += 4;
			if (cur_item != NULL)
			{
			    cur_item->v_type = VAR_SPECIAL;
			    cur_item->vval.v_number = VVAL_TRUE;
			}
			retval = OK;
			break;
		    }
		    if (STRNICMP((char *)p, "null", 4) == 0)
		    {
			reader->js_used += 4;
			if (cur_item != NULL)
			{
			    cur_item->v_type = VAR_SPECIAL;
			    cur_item->vval.v_number = VVAL_NULL;
			}
			retval = OK;
			break;
		    }
#ifdef FEAT_FLOAT
		    if (STRNICMP((char *)p, "NaN", 3) == 0)
		    {
			reader->js_used += 3;
			if (cur_item != NULL)
			{
			    cur_item->v_type = VAR_FLOAT;
			    cur_item->vval.v_float = NAN;
			}
			retval = OK;
			break;
		    }
		    if (STRNICMP((char *)p, "Infinity", 8) == 0)
		    {
			reader->js_used += 8;
			if (cur_item != NULL)
			{
			    cur_item->v_type = VAR_FLOAT;
			    cur_item->vval.v_float = INFINITY;
			}
			retval = OK;
			break;
		    }
#endif
		    /* check for truncated name */
		    len = (int)(reader->js_end - (reader->js_buf + reader->js_used));
		    if (
			    (len < 5 && STRNICMP((char *)p, "false", len) == 0)
#ifdef FEAT_FLOAT
			    || (len < 8 && STRNICMP((char *)p, "Infinity", len) == 0)
			    || (len < 3 && STRNICMP((char *)p, "NaN", len) == 0)
#endif
			    || (len < 4 && (STRNICMP((char *)p, "true", len) == 0
				       ||  STRNICMP((char *)p, "null", len) == 0)))

			retval = MAYBE;
		    else
			retval = FAIL;
		    break;
	    }

	    /* We are finished when retval is FAIL or MAYBE and when at the
	     * toplevel. */
	    if (retval == FAIL)
		break;
	    if (retval == MAYBE || stack.ga_len == 0)
		goto theend;

	    if (top_item != NULL && top_item->jd_type == JSON_OBJECT_KEY
		    && cur_item != NULL)
	    {
		top_item->jd_key = get_tv_string_buf_chk(cur_item, key_buf);
		if (top_item->jd_key == NULL)
		{
		    clear_tv(cur_item);
		    EMSG(_(e_invarg));
		    retval = FAIL;
		    goto theend;
		}
	    }
	}

item_end:
	top_item = ((json_dec_item_T *)stack.ga_data) + stack.ga_len - 1;
	switch (top_item->jd_type)
	{
	    case JSON_ARRAY:
		if (res != NULL)
		{
		    listitem_T	*li = listitem_alloc();

		    if (li == NULL)
		    {
			clear_tv(cur_item);
			retval = FAIL;
			goto theend;
		    }
		    li->li_tv = *cur_item;
		    list_append(top_item->jd_tv.vval.v_list, li);
		}
		if (cur_item != NULL)
		    cur_item = &item;

		json_skip_white(reader);
		p = reader->js_buf + reader->js_used;
		if (*p == ',')
		    ++reader->js_used;
		else if (*p != ']')
		{
		    if (*p == NUL)
			retval = MAYBE;
		    else
		    {
			EMSG(_(e_invarg));
			retval = FAIL;
		    }
		    goto theend;
		}
		break;

	    case JSON_OBJECT_KEY:
		json_skip_white(reader);
		p = reader->js_buf + reader->js_used;
		if (*p != ':')
		{
		    if (cur_item != NULL)
			clear_tv(cur_item);
		    if (*p == NUL)
			retval = MAYBE;
		    else
		    {
			EMSG(_(e_invarg));
			retval = FAIL;
		    }
		    goto theend;
		}
		++reader->js_used;
		json_skip_white(reader);
		top_item->jd_type = JSON_OBJECT;
		if (cur_item != NULL)
		    cur_item = &item;
		break;

	    case JSON_OBJECT:
		if (cur_item != NULL
			&& dict_find(top_item->jd_tv.vval.v_dict,
						 top_item->jd_key, -1) != NULL)
		{
		    EMSG2(_("E938: Duplicate key in JSON: \"%s\""),
							     top_item->jd_key);
		    clear_tv(&top_item->jd_key_tv);
		    clear_tv(cur_item);
		    retval = FAIL;
		    goto theend;
		}

		if (cur_item != NULL)
		{
		    dictitem_T *di = dictitem_alloc(top_item->jd_key);

		    clear_tv(&top_item->jd_key_tv);
		    if (di == NULL)
		    {
			clear_tv(cur_item);
			retval = FAIL;
			goto theend;
		    }
		    di->di_tv = *cur_item;
		    di->di_tv.v_lock = 0;
		    if (dict_add(top_item->jd_tv.vval.v_dict, di) == FAIL)
		    {
			dictitem_free(di);
			retval = FAIL;
			goto theend;
		    }
		}

		json_skip_white(reader);
		p = reader->js_buf + reader->js_used;
		if (*p == ',')
		    ++reader->js_used;
		else if (*p != '}')
		{
		    if (*p == NUL)
			retval = MAYBE;
		    else
		    {
			EMSG(_(e_invarg));
			retval = FAIL;
		    }
		    goto theend;
		}
		top_item->jd_type = JSON_OBJECT_KEY;
		if (cur_item != NULL)
		    cur_item = &top_item->jd_key_tv;
		break;
	}
    }

    /* Get here when parsing failed. */
    if (res != NULL)
    {
	clear_tv(res);
	res->v_type = VAR_SPECIAL;
	res->vval.v_number = VVAL_NONE;
    }
    EMSG(_(e_invarg));

theend:
    ga_clear(&stack);
    return retval;
}
コード例 #3
0
ファイル: json.c プロジェクト: Qubit0-1/vim
/*
 * Decode one item and put it in "res".  If "res" is NULL only advance.
 * Must already have skipped white space.
 *
 * Return FAIL for a decoding error.
 * Return MAYBE for an incomplete message.
 */
    static int
json_decode_item(js_read_T *reader, typval_T *res, int options)
{
    char_u	*p;
    int		len;

    fill_numbuflen(reader);
    p = reader->js_buf + reader->js_used;
    switch (*p)
    {
	case '[': /* array */
	    return json_decode_array(reader, res, options);

	case '{': /* object */
	    return json_decode_object(reader, res, options);

	case '"': /* string */
	    return json_decode_string(reader, res);

	case ',': /* comma: empty item */
	    if ((options & JSON_JS) == 0)
		return FAIL;
	    /* FALLTHROUGH */
	case NUL: /* empty */
	    if (res != NULL)
	    {
		res->v_type = VAR_SPECIAL;
		res->vval.v_number = VVAL_NONE;
	    }
	    return OK;

	default:
	    if (VIM_ISDIGIT(*p) || *p == '-')
	    {
		char_u  *sp = p;

#ifdef FEAT_FLOAT
		if (*sp == '-')
		{
		    ++sp;
		    if (*sp == NUL)
			return MAYBE;
		    if (!VIM_ISDIGIT(*sp))
			return FAIL;
		}
		sp = skipdigits(sp);
		if (*sp == '.' || *sp == 'e' || *sp == 'E')
		{
		    if (res == NULL)
		    {
			float_T f;

			len = string2float(p, &f);
		    }
		    else
		    {
			res->v_type = VAR_FLOAT;
			len = string2float(p, &res->vval.v_float);
		    }
		}
		else
#endif
		{
		    long nr;

		    vim_str2nr(reader->js_buf + reader->js_used,
			    NULL, &len, 0, /* what */
			    &nr, NULL, 0);
		    if (res != NULL)
		    {
			res->v_type = VAR_NUMBER;
			res->vval.v_number = nr;
		    }
		}
		reader->js_used += len;
		return OK;
	    }
	    if (STRNICMP((char *)p, "false", 5) == 0)
	    {
		reader->js_used += 5;
		if (res != NULL)
		{
		    res->v_type = VAR_SPECIAL;
		    res->vval.v_number = VVAL_FALSE;
		}
		return OK;
	    }
	    if (STRNICMP((char *)p, "true", 4) == 0)
	    {
		reader->js_used += 4;
		if (res != NULL)
		{
		    res->v_type = VAR_SPECIAL;
		    res->vval.v_number = VVAL_TRUE;
		}
		return OK;
	    }
	    if (STRNICMP((char *)p, "null", 4) == 0)
	    {
		reader->js_used += 4;
		if (res != NULL)
		{
		    res->v_type = VAR_SPECIAL;
		    res->vval.v_number = VVAL_NULL;
		}
		return OK;
	    }
	    /* check for truncated name */
	    len = (int)(reader->js_end - (reader->js_buf + reader->js_used));
	    if ((len < 5 && STRNICMP((char *)p, "false", len) == 0)
		    || (len < 4 && (STRNICMP((char *)p, "true", len) == 0
			       ||  STRNICMP((char *)p, "null", len) == 0)))
		return MAYBE;
	    break;
    }

    if (res != NUL)
    {
	res->v_type = VAR_SPECIAL;
	res->vval.v_number = VVAL_NONE;
    }
    return FAIL;
}