2009-11-04 14:55:53 -08:00
|
|
|
|
/*
|
2017-03-22 14:36:57 -07:00
|
|
|
|
* Copyright (c) 2009-2012, 2014-2017 Nicira, Inc.
|
2009-11-04 14:55:53 -08:00
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
2016-07-12 16:37:34 -05:00
|
|
|
|
#include "openvswitch/json.h"
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <float.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
2016-03-03 10:20:46 -08:00
|
|
|
|
#include "openvswitch/dynamic-string.h"
|
2009-11-04 14:55:53 -08:00
|
|
|
|
#include "hash.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
|
#include "openvswitch/shash.h"
|
2009-11-04 14:55:53 -08:00
|
|
|
|
#include "unicode.h"
|
|
|
|
|
#include "util.h"
|
2017-09-13 15:50:25 -07:00
|
|
|
|
#include "uuid.h"
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
/* The type of a JSON token. */
|
|
|
|
|
enum json_token_type {
|
|
|
|
|
T_EOF = 0,
|
|
|
|
|
T_BEGIN_ARRAY = '[',
|
|
|
|
|
T_END_ARRAY = ']',
|
|
|
|
|
T_BEGIN_OBJECT = '{',
|
|
|
|
|
T_END_OBJECT = '}',
|
|
|
|
|
T_NAME_SEPARATOR = ':',
|
|
|
|
|
T_VALUE_SEPARATOR = ',',
|
|
|
|
|
T_FALSE = UCHAR_MAX + 1,
|
|
|
|
|
T_NULL,
|
|
|
|
|
T_TRUE,
|
|
|
|
|
T_INTEGER,
|
|
|
|
|
T_REAL,
|
|
|
|
|
T_STRING
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* A JSON token.
|
|
|
|
|
*
|
|
|
|
|
* RFC 4627 doesn't define a lexical structure for JSON but I believe this to
|
|
|
|
|
* be compliant with the standard.
|
|
|
|
|
*/
|
|
|
|
|
struct json_token {
|
|
|
|
|
enum json_token_type type;
|
|
|
|
|
union {
|
|
|
|
|
double real;
|
|
|
|
|
long long int integer;
|
|
|
|
|
const char *string;
|
2018-05-24 10:32:59 -07:00
|
|
|
|
};
|
2009-11-04 14:55:53 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum json_lex_state {
|
|
|
|
|
JSON_LEX_START, /* Not inside a token. */
|
|
|
|
|
JSON_LEX_NUMBER, /* Reading a number. */
|
|
|
|
|
JSON_LEX_KEYWORD, /* Reading a keyword. */
|
|
|
|
|
JSON_LEX_STRING, /* Reading a quoted string. */
|
|
|
|
|
JSON_LEX_ESCAPE /* In a quoted string just after a "\". */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum json_parse_state {
|
|
|
|
|
JSON_PARSE_START, /* Beginning of input. */
|
|
|
|
|
JSON_PARSE_END, /* End of input. */
|
|
|
|
|
|
|
|
|
|
/* Objects. */
|
|
|
|
|
JSON_PARSE_OBJECT_INIT, /* Expecting '}' or an object name. */
|
|
|
|
|
JSON_PARSE_OBJECT_NAME, /* Expecting an object name. */
|
|
|
|
|
JSON_PARSE_OBJECT_COLON, /* Expecting ':'. */
|
|
|
|
|
JSON_PARSE_OBJECT_VALUE, /* Expecting an object value. */
|
|
|
|
|
JSON_PARSE_OBJECT_NEXT, /* Expecting ',' or '}'. */
|
|
|
|
|
|
|
|
|
|
/* Arrays. */
|
|
|
|
|
JSON_PARSE_ARRAY_INIT, /* Expecting ']' or a value. */
|
|
|
|
|
JSON_PARSE_ARRAY_VALUE, /* Expecting a value. */
|
|
|
|
|
JSON_PARSE_ARRAY_NEXT /* Expecting ',' or ']'. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct json_parser_node {
|
|
|
|
|
struct json *json;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* A JSON parser. */
|
|
|
|
|
struct json_parser {
|
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
|
|
/* Lexical analysis. */
|
|
|
|
|
enum json_lex_state lex_state;
|
|
|
|
|
struct ds buffer; /* Buffer for accumulating token text. */
|
2009-12-03 16:08:34 -08:00
|
|
|
|
int line_number;
|
|
|
|
|
int column_number;
|
|
|
|
|
int byte_number;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
/* Parsing. */
|
|
|
|
|
enum json_parse_state parse_state;
|
|
|
|
|
#define JSON_MAX_HEIGHT 1000
|
|
|
|
|
struct json_parser_node *stack;
|
|
|
|
|
size_t height, allocated_height;
|
|
|
|
|
char *member_name;
|
|
|
|
|
|
|
|
|
|
/* Parse status. */
|
|
|
|
|
bool done;
|
|
|
|
|
char *error; /* Error message, if any, null if none yet. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct json *json_create(enum json_type type);
|
|
|
|
|
static void json_parser_input(struct json_parser *, struct json_token *);
|
|
|
|
|
|
|
|
|
|
static void json_error(struct json_parser *p, const char *format, ...)
|
2014-12-15 14:10:38 +01:00
|
|
|
|
OVS_PRINTF_FORMAT(2, 3);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
json_type_to_string(enum json_type type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case JSON_NULL:
|
|
|
|
|
return "null";
|
|
|
|
|
|
|
|
|
|
case JSON_FALSE:
|
|
|
|
|
return "false";
|
|
|
|
|
|
|
|
|
|
case JSON_TRUE:
|
|
|
|
|
return "true";
|
|
|
|
|
|
|
|
|
|
case JSON_OBJECT:
|
|
|
|
|
return "object";
|
|
|
|
|
|
|
|
|
|
case JSON_ARRAY:
|
|
|
|
|
return "array";
|
|
|
|
|
|
|
|
|
|
case JSON_INTEGER:
|
|
|
|
|
case JSON_REAL:
|
|
|
|
|
return "number";
|
|
|
|
|
|
|
|
|
|
case JSON_STRING:
|
|
|
|
|
return "string";
|
|
|
|
|
|
2021-08-24 21:00:37 +02:00
|
|
|
|
case JSON_SERIALIZED_OBJECT:
|
2009-11-04 14:55:53 -08:00
|
|
|
|
case JSON_N_TYPES:
|
|
|
|
|
default:
|
|
|
|
|
return "<invalid>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Functions for manipulating struct json. */
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_null_create(void)
|
|
|
|
|
{
|
|
|
|
|
return json_create(JSON_NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_boolean_create(bool b)
|
|
|
|
|
{
|
|
|
|
|
return json_create(b ? JSON_TRUE : JSON_FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_string_create_nocopy(char *s)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = json_create(JSON_STRING);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json->string = s;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_string_create(const char *s)
|
|
|
|
|
{
|
|
|
|
|
return json_string_create_nocopy(xstrdup(s));
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-24 21:00:37 +02:00
|
|
|
|
struct json *
|
|
|
|
|
json_serialized_object_create(const struct json *src)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = json_create(JSON_SERIALIZED_OBJECT);
|
|
|
|
|
json->string = json_to_string(src, JSSF_SORT);
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
struct json *
|
|
|
|
|
json_array_create_empty(void)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = json_create(JSON_ARRAY);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json->array.elems = NULL;
|
|
|
|
|
json->array.n = 0;
|
|
|
|
|
json->array.n_allocated = 0;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
json_array_add(struct json *array_, struct json *element)
|
|
|
|
|
{
|
|
|
|
|
struct json_array *array = json_array(array_);
|
|
|
|
|
if (array->n >= array->n_allocated) {
|
|
|
|
|
array->elems = x2nrealloc(array->elems, &array->n_allocated,
|
|
|
|
|
sizeof *array->elems);
|
|
|
|
|
}
|
|
|
|
|
array->elems[array->n++] = element;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
json_array_trim(struct json *array_)
|
|
|
|
|
{
|
|
|
|
|
struct json_array *array = json_array(array_);
|
|
|
|
|
if (array->n < array->n_allocated){
|
|
|
|
|
array->n_allocated = array->n;
|
|
|
|
|
array->elems = xrealloc(array->elems, array->n * sizeof *array->elems);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_array_create(struct json **elements, size_t n)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = json_create(JSON_ARRAY);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json->array.elems = elements;
|
|
|
|
|
json->array.n = n;
|
|
|
|
|
json->array.n_allocated = n;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-07 17:10:09 -08:00
|
|
|
|
struct json *
|
|
|
|
|
json_array_create_1(struct json *elem0)
|
|
|
|
|
{
|
|
|
|
|
struct json **elems = xmalloc(sizeof *elems);
|
|
|
|
|
elems[0] = elem0;
|
|
|
|
|
return json_array_create(elems, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
struct json *
|
|
|
|
|
json_array_create_2(struct json *elem0, struct json *elem1)
|
|
|
|
|
{
|
|
|
|
|
struct json **elems = xmalloc(2 * sizeof *elems);
|
|
|
|
|
elems[0] = elem0;
|
|
|
|
|
elems[1] = elem1;
|
|
|
|
|
return json_array_create(elems, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_array_create_3(struct json *elem0, struct json *elem1, struct json *elem2)
|
|
|
|
|
{
|
|
|
|
|
struct json **elems = xmalloc(3 * sizeof *elems);
|
|
|
|
|
elems[0] = elem0;
|
|
|
|
|
elems[1] = elem1;
|
|
|
|
|
elems[2] = elem2;
|
|
|
|
|
return json_array_create(elems, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_object_create(void)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = json_create(JSON_OBJECT);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json->object = xmalloc(sizeof *json->object);
|
|
|
|
|
shash_init(json->object);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_integer_create(long long int integer)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = json_create(JSON_INTEGER);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json->integer = integer;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_real_create(double real)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = json_create(JSON_REAL);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json->real = real;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
json_object_put(struct json *json, const char *name, struct json *value)
|
|
|
|
|
{
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json_destroy(shash_replace(json->object, name, value));
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-23 15:46:58 -07:00
|
|
|
|
void
|
|
|
|
|
json_object_put_nocopy(struct json *json, char *name, struct json *value)
|
|
|
|
|
{
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json_destroy(shash_replace_nocopy(json->object, name, value));
|
2018-03-23 15:46:58 -07:00
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
void
|
|
|
|
|
json_object_put_string(struct json *json, const char *name, const char *value)
|
|
|
|
|
{
|
|
|
|
|
json_object_put(json, name, json_string_create(value));
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-13 15:50:25 -07:00
|
|
|
|
void OVS_PRINTF_FORMAT(3, 4)
|
|
|
|
|
json_object_put_format(struct json *json,
|
|
|
|
|
const char *name, const char *format, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, format);
|
|
|
|
|
json_object_put(json, name,
|
|
|
|
|
json_string_create_nocopy(xvasprintf(format, args)));
|
|
|
|
|
va_end(args);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
const char *
|
|
|
|
|
json_string(const struct json *json)
|
|
|
|
|
{
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(json->type == JSON_STRING);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json->string;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-24 21:00:37 +02:00
|
|
|
|
const char *
|
|
|
|
|
json_serialized_object(const struct json *json)
|
|
|
|
|
{
|
|
|
|
|
ovs_assert(json->type == JSON_SERIALIZED_OBJECT);
|
|
|
|
|
return json->string;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
struct json_array *
|
|
|
|
|
json_array(const struct json *json)
|
|
|
|
|
{
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(json->type == JSON_ARRAY);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return CONST_CAST(struct json_array *, &json->array);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct shash *
|
|
|
|
|
json_object(const struct json *json)
|
|
|
|
|
{
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(json->type == JSON_OBJECT);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return CONST_CAST(struct shash *, json->object);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
json_boolean(const struct json *json)
|
|
|
|
|
{
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(json->type == JSON_TRUE || json->type == JSON_FALSE);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return json->type == JSON_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double
|
|
|
|
|
json_real(const struct json *json)
|
|
|
|
|
{
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(json->type == JSON_REAL || json->type == JSON_INTEGER);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json->type == JSON_REAL ? json->real : json->integer;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int64_t
|
|
|
|
|
json_integer(const struct json *json)
|
|
|
|
|
{
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(json->type == JSON_INTEGER);
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json->integer;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void json_destroy_object(struct shash *object);
|
|
|
|
|
static void json_destroy_array(struct json_array *array);
|
|
|
|
|
|
|
|
|
|
/* Frees 'json' and everything it points to, recursively. */
|
|
|
|
|
void
|
2021-11-22 01:09:31 +01:00
|
|
|
|
json_destroy__(struct json *json)
|
|
|
|
|
{
|
|
|
|
|
switch (json->type) {
|
|
|
|
|
case JSON_OBJECT:
|
|
|
|
|
json_destroy_object(json->object);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_ARRAY:
|
|
|
|
|
json_destroy_array(&json->array);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_STRING:
|
|
|
|
|
case JSON_SERIALIZED_OBJECT:
|
|
|
|
|
free(json->string);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_NULL:
|
|
|
|
|
case JSON_FALSE:
|
|
|
|
|
case JSON_TRUE:
|
|
|
|
|
case JSON_INTEGER:
|
|
|
|
|
case JSON_REAL:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_N_TYPES:
|
|
|
|
|
OVS_NOT_REACHED();
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
2021-11-22 01:09:31 +01:00
|
|
|
|
free(json);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_destroy_object(struct shash *object)
|
|
|
|
|
{
|
2022-03-23 12:56:17 +01:00
|
|
|
|
struct shash_node *node;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
2022-03-23 12:56:17 +01:00
|
|
|
|
SHASH_FOR_EACH_SAFE (node, object) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
struct json *value = node->data;
|
|
|
|
|
|
|
|
|
|
json_destroy(value);
|
|
|
|
|
shash_delete(object, node);
|
|
|
|
|
}
|
|
|
|
|
shash_destroy(object);
|
|
|
|
|
free(object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_destroy_array(struct json_array *array)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < array->n; i++) {
|
|
|
|
|
json_destroy(array->elems[i]);
|
|
|
|
|
}
|
|
|
|
|
free(array->elems);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-01 13:11:16 +02:00
|
|
|
|
static struct json *json_deep_clone_object(const struct shash *object);
|
|
|
|
|
static struct json *json_deep_clone_array(const struct json_array *array);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
/* Returns a deep copy of 'json'. */
|
|
|
|
|
struct json *
|
2016-10-04 19:31:48 +00:00
|
|
|
|
json_deep_clone(const struct json *json)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
|
|
|
|
switch (json->type) {
|
|
|
|
|
case JSON_OBJECT:
|
2022-07-01 13:11:16 +02:00
|
|
|
|
return json_deep_clone_object(json->object);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_ARRAY:
|
2022-07-01 13:11:16 +02:00
|
|
|
|
return json_deep_clone_array(&json->array);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_STRING:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json_string_create(json->string);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
2021-08-24 21:00:37 +02:00
|
|
|
|
case JSON_SERIALIZED_OBJECT:
|
|
|
|
|
return json_serialized_object_create(json);
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
case JSON_NULL:
|
|
|
|
|
case JSON_FALSE:
|
|
|
|
|
case JSON_TRUE:
|
|
|
|
|
return json_create(json->type);
|
|
|
|
|
|
|
|
|
|
case JSON_INTEGER:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json_integer_create(json->integer);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_REAL:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json_real_create(json->real);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_N_TYPES:
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-22 14:36:57 -07:00
|
|
|
|
struct json *
|
|
|
|
|
json_nullable_clone(const struct json *json)
|
|
|
|
|
{
|
|
|
|
|
return json ? json_clone(json) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
static struct json *
|
2022-07-01 13:11:16 +02:00
|
|
|
|
json_deep_clone_object(const struct shash *object)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
|
|
|
|
struct shash_node *node;
|
|
|
|
|
struct json *json;
|
|
|
|
|
|
|
|
|
|
json = json_object_create();
|
|
|
|
|
SHASH_FOR_EACH (node, object) {
|
|
|
|
|
struct json *value = node->data;
|
2022-07-01 13:11:16 +02:00
|
|
|
|
json_object_put(json, node->name, json_deep_clone(value));
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct json *
|
2022-07-01 13:11:16 +02:00
|
|
|
|
json_deep_clone_array(const struct json_array *array)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
|
|
|
|
struct json **elems;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
elems = xmalloc(array->n * sizeof *elems);
|
|
|
|
|
for (i = 0; i < array->n; i++) {
|
2022-07-01 13:11:16 +02:00
|
|
|
|
elems[i] = json_deep_clone(array->elems[i]);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
return json_array_create(elems, array->n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
json_hash_object(const struct shash *object, size_t basis)
|
|
|
|
|
{
|
|
|
|
|
const struct shash_node **nodes;
|
|
|
|
|
size_t n, i;
|
|
|
|
|
|
|
|
|
|
nodes = shash_sort(object);
|
|
|
|
|
n = shash_count(object);
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
const struct shash_node *node = nodes[i];
|
|
|
|
|
basis = hash_string(node->name, basis);
|
|
|
|
|
basis = json_hash(node->data, basis);
|
|
|
|
|
}
|
2014-08-26 12:23:03 +02:00
|
|
|
|
free(nodes);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return basis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
json_hash_array(const struct json_array *array, size_t basis)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
basis = hash_int(array->n, basis);
|
|
|
|
|
for (i = 0; i < array->n; i++) {
|
|
|
|
|
basis = json_hash(array->elems[i], basis);
|
|
|
|
|
}
|
|
|
|
|
return basis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t
|
|
|
|
|
json_hash(const struct json *json, size_t basis)
|
|
|
|
|
{
|
|
|
|
|
switch (json->type) {
|
|
|
|
|
case JSON_OBJECT:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json_hash_object(json->object, basis);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_ARRAY:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json_hash_array(&json->array, basis);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_STRING:
|
2021-08-24 21:00:37 +02:00
|
|
|
|
case JSON_SERIALIZED_OBJECT:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return hash_string(json->string, basis);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_NULL:
|
|
|
|
|
case JSON_FALSE:
|
|
|
|
|
case JSON_TRUE:
|
|
|
|
|
return hash_int(json->type << 8, basis);
|
|
|
|
|
|
|
|
|
|
case JSON_INTEGER:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return hash_int(json->integer, basis);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_REAL:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return hash_double(json->real, basis);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_N_TYPES:
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
json_equal_object(const struct shash *a, const struct shash *b)
|
|
|
|
|
{
|
2009-11-06 12:24:44 -08:00
|
|
|
|
struct shash_node *a_node;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
if (shash_count(a) != shash_count(b)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-06 12:24:44 -08:00
|
|
|
|
SHASH_FOR_EACH (a_node, a) {
|
|
|
|
|
struct shash_node *b_node = shash_find(b, a_node->name);
|
|
|
|
|
if (!b_node || !json_equal(a_node->data, b_node->data)) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
json_equal_array(const struct json_array *a, const struct json_array *b)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
if (a->n != b->n) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < a->n; i++) {
|
|
|
|
|
if (!json_equal(a->elems[i], b->elems[i])) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
json_equal(const struct json *a, const struct json *b)
|
|
|
|
|
{
|
2016-10-04 19:31:48 +00:00
|
|
|
|
if (a == b) {
|
|
|
|
|
return true;
|
2017-12-30 17:02:22 -08:00
|
|
|
|
} else if (!a || !b) {
|
|
|
|
|
return false;
|
|
|
|
|
} else if (a->type != b->type) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (a->type) {
|
|
|
|
|
case JSON_OBJECT:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json_equal_object(a->object, b->object);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_ARRAY:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return json_equal_array(&a->array, &b->array);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_STRING:
|
2021-08-24 21:00:37 +02:00
|
|
|
|
case JSON_SERIALIZED_OBJECT:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return !strcmp(a->string, b->string);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_NULL:
|
|
|
|
|
case JSON_FALSE:
|
|
|
|
|
case JSON_TRUE:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case JSON_INTEGER:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return a->integer == b->integer;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_REAL:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
return a->real == b->real;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
case JSON_N_TYPES:
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Lexical analysis. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_lex_keyword(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
struct json_token token;
|
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
|
|
s = ds_cstr(&p->buffer);
|
|
|
|
|
if (!strcmp(s, "false")) {
|
|
|
|
|
token.type = T_FALSE;
|
|
|
|
|
} else if (!strcmp(s, "true")) {
|
|
|
|
|
token.type = T_TRUE;
|
|
|
|
|
} else if (!strcmp(s, "null")) {
|
|
|
|
|
token.type = T_NULL;
|
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "invalid keyword '%s'", s);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
json_parser_input(p, &token);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_lex_number(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
const char *cp = ds_cstr(&p->buffer);
|
|
|
|
|
unsigned long long int significand = 0;
|
2009-12-16 10:55:46 -08:00
|
|
|
|
struct json_token token;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
bool imprecise = false;
|
|
|
|
|
bool negative = false;
|
|
|
|
|
int pow10 = 0;
|
|
|
|
|
|
|
|
|
|
/* Leading minus sign. */
|
|
|
|
|
if (*cp == '-') {
|
|
|
|
|
negative = true;
|
|
|
|
|
cp++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* At least one integer digit, but 0 may not be used as a leading digit for
|
|
|
|
|
* a longer number. */
|
|
|
|
|
significand = 0;
|
|
|
|
|
if (*cp == '0') {
|
|
|
|
|
cp++;
|
2013-04-22 22:20:10 +09:00
|
|
|
|
if (isdigit((unsigned char) *cp)) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json_error(p, "leading zeros not allowed");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-04-22 22:20:10 +09:00
|
|
|
|
} else if (isdigit((unsigned char) *cp)) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
do {
|
|
|
|
|
if (significand <= ULLONG_MAX / 10) {
|
|
|
|
|
significand = significand * 10 + (*cp - '0');
|
|
|
|
|
} else {
|
|
|
|
|
pow10++;
|
|
|
|
|
if (*cp != '0') {
|
|
|
|
|
imprecise = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cp++;
|
2013-04-22 22:20:10 +09:00
|
|
|
|
} while (isdigit((unsigned char) *cp));
|
2009-11-04 14:55:53 -08:00
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "'-' must be followed by digit");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Optional fraction. */
|
|
|
|
|
if (*cp == '.') {
|
|
|
|
|
cp++;
|
2013-04-22 22:20:10 +09:00
|
|
|
|
if (!isdigit((unsigned char) *cp)) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json_error(p, "decimal point must be followed by digit");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
do {
|
|
|
|
|
if (significand <= ULLONG_MAX / 10) {
|
|
|
|
|
significand = significand * 10 + (*cp - '0');
|
|
|
|
|
pow10--;
|
|
|
|
|
} else if (*cp != '0') {
|
|
|
|
|
imprecise = true;
|
|
|
|
|
}
|
|
|
|
|
cp++;
|
2013-04-22 22:20:10 +09:00
|
|
|
|
} while (isdigit((unsigned char) *cp));
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Optional exponent. */
|
|
|
|
|
if (*cp == 'e' || *cp == 'E') {
|
|
|
|
|
bool negative_exponent = false;
|
|
|
|
|
int exponent;
|
|
|
|
|
|
|
|
|
|
cp++;
|
|
|
|
|
if (*cp == '+') {
|
|
|
|
|
cp++;
|
|
|
|
|
} else if (*cp == '-') {
|
|
|
|
|
negative_exponent = true;
|
|
|
|
|
cp++;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-22 22:20:10 +09:00
|
|
|
|
if (!isdigit((unsigned char) *cp)) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json_error(p, "exponent must contain at least one digit");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exponent = 0;
|
|
|
|
|
do {
|
|
|
|
|
if (exponent >= INT_MAX / 10) {
|
2018-06-25 11:23:36 -07:00
|
|
|
|
goto bad_exponent;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
exponent = exponent * 10 + (*cp - '0');
|
|
|
|
|
cp++;
|
2013-04-22 22:20:10 +09:00
|
|
|
|
} while (isdigit((unsigned char) *cp));
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
if (negative_exponent) {
|
2018-06-25 11:23:36 -07:00
|
|
|
|
if (pow10 < INT_MIN + exponent) {
|
|
|
|
|
goto bad_exponent;
|
|
|
|
|
}
|
2009-11-04 14:55:53 -08:00
|
|
|
|
pow10 -= exponent;
|
|
|
|
|
} else {
|
2018-06-25 11:23:36 -07:00
|
|
|
|
if (pow10 > INT_MAX - exponent) {
|
|
|
|
|
goto bad_exponent;
|
|
|
|
|
}
|
2009-11-04 14:55:53 -08:00
|
|
|
|
pow10 += exponent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*cp != '\0') {
|
|
|
|
|
json_error(p, "syntax error in number");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Figure out number.
|
|
|
|
|
*
|
|
|
|
|
* We suppress negative zeros as a matter of policy. */
|
|
|
|
|
if (!significand) {
|
|
|
|
|
token.type = T_INTEGER;
|
2018-05-24 10:32:59 -07:00
|
|
|
|
token.integer = 0;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json_parser_input(p, &token);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!imprecise) {
|
|
|
|
|
while (pow10 > 0 && significand < ULLONG_MAX / 10) {
|
|
|
|
|
significand *= 10;
|
|
|
|
|
pow10--;
|
|
|
|
|
}
|
|
|
|
|
while (pow10 < 0 && significand % 10 == 0) {
|
|
|
|
|
significand /= 10;
|
|
|
|
|
pow10++;
|
|
|
|
|
}
|
|
|
|
|
if (pow10 == 0
|
|
|
|
|
&& significand <= (negative
|
|
|
|
|
? (unsigned long long int) LLONG_MAX + 1
|
|
|
|
|
: LLONG_MAX)) {
|
|
|
|
|
token.type = T_INTEGER;
|
2018-05-24 10:32:59 -07:00
|
|
|
|
token.integer = negative ? -significand : significand;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json_parser_input(p, &token);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-16 10:55:46 -08:00
|
|
|
|
token.type = T_REAL;
|
2018-05-24 10:32:59 -07:00
|
|
|
|
if (!str_to_double(ds_cstr(&p->buffer), &token.real)) {
|
2009-12-16 10:55:46 -08:00
|
|
|
|
json_error(p, "number outside valid range");
|
|
|
|
|
return;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
2009-12-16 10:55:46 -08:00
|
|
|
|
/* Suppress negative zero. */
|
2018-05-24 10:32:59 -07:00
|
|
|
|
if (token.real == 0) {
|
|
|
|
|
token.real = 0;
|
2009-12-16 10:55:46 -08:00
|
|
|
|
}
|
|
|
|
|
json_parser_input(p, &token);
|
2018-06-25 11:23:36 -07:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bad_exponent:
|
|
|
|
|
json_error(p, "exponent outside valid range");
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
static const char *
|
|
|
|
|
json_lex_4hex(const char *cp, const char *end, int *valuep)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
2010-11-15 10:18:10 -08:00
|
|
|
|
unsigned int value;
|
2014-09-30 12:45:50 -07:00
|
|
|
|
bool ok;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
if (cp + 4 > end) {
|
|
|
|
|
return "quoted string ends within \\u escape";
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-30 12:45:50 -07:00
|
|
|
|
value = hexits_value(cp, 4, &ok);
|
|
|
|
|
if (!ok) {
|
2010-11-15 10:18:10 -08:00
|
|
|
|
return "malformed \\u escape";
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
if (!value) {
|
2010-01-26 09:47:54 -08:00
|
|
|
|
return "null bytes not supported in quoted strings";
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
*valuep = value;
|
2010-01-26 09:47:54 -08:00
|
|
|
|
return NULL;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
2010-01-26 09:47:54 -08:00
|
|
|
|
json_lex_unicode(const char *cp, const char *end, struct ds *out)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
2010-01-26 09:47:54 -08:00
|
|
|
|
const char *error;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
int c0, c1;
|
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
error = json_lex_4hex(cp, end, &c0);
|
|
|
|
|
if (error) {
|
|
|
|
|
ds_clear(out);
|
|
|
|
|
ds_put_cstr(out, error);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
cp += 4;
|
|
|
|
|
if (!uc_is_leading_surrogate(c0)) {
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_put_utf8(out, c0);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
if (cp + 2 > end || *cp++ != '\\' || *cp++ != 'u') {
|
|
|
|
|
ds_clear(out);
|
|
|
|
|
ds_put_cstr(out, "malformed escaped surrogate pair");
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
error = json_lex_4hex(cp, end, &c1);
|
|
|
|
|
if (error) {
|
|
|
|
|
ds_clear(out);
|
|
|
|
|
ds_put_cstr(out, error);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
cp += 4;
|
|
|
|
|
if (!uc_is_trailing_surrogate(c1)) {
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_clear(out);
|
|
|
|
|
ds_put_cstr(out, "second half of escaped surrogate pair is not "
|
|
|
|
|
"trailing surrogate");
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_put_utf8(out, utf16_decode_surrogate_pair(c0, c1));
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return cp;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
bool
|
|
|
|
|
json_string_unescape(const char *in, size_t in_len, char **outp)
|
|
|
|
|
{
|
|
|
|
|
const char *end = in + in_len;
|
|
|
|
|
bool ok = false;
|
|
|
|
|
struct ds out;
|
|
|
|
|
|
|
|
|
|
ds_init(&out);
|
|
|
|
|
ds_reserve(&out, in_len);
|
|
|
|
|
while (in < end) {
|
|
|
|
|
if (*in == '"') {
|
|
|
|
|
ds_clear(&out);
|
2010-03-02 13:38:47 -08:00
|
|
|
|
ds_put_cstr(&out, "quoted string may not include unescaped \"");
|
2010-01-26 09:47:54 -08:00
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
if (*in != '\\') {
|
|
|
|
|
ds_put_char(&out, *in++);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
in++;
|
2014-06-25 11:39:25 -07:00
|
|
|
|
if (in >= end) {
|
|
|
|
|
/* The JSON parser will never trigger this message, because its
|
|
|
|
|
* lexer will never pass in a string that ends in a single
|
|
|
|
|
* backslash, but json_string_unescape() has other callers that
|
|
|
|
|
* are not as careful.*/
|
2015-02-25 08:46:02 -08:00
|
|
|
|
ds_clear(&out);
|
2014-06-25 11:39:25 -07:00
|
|
|
|
ds_put_cstr(&out, "quoted string may not end with backslash");
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
2010-01-26 09:47:54 -08:00
|
|
|
|
switch (*in++) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
case '"': case '\\': case '/':
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_put_char(&out, in[-1]);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'b':
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_put_char(&out, '\b');
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'f':
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_put_char(&out, '\f');
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'n':
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_put_char(&out, '\n');
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'r':
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_put_char(&out, '\r');
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 't':
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_put_char(&out, '\t');
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'u':
|
2010-01-26 09:47:54 -08:00
|
|
|
|
in = json_lex_unicode(in, end, &out);
|
|
|
|
|
if (!in) {
|
2009-11-04 14:55:53 -08:00
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ds_clear(&out);
|
|
|
|
|
ds_put_format(&out, "bad escape \\%c", in[-1]);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-01-26 09:47:54 -08:00
|
|
|
|
ok = true;
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
*outp = ds_cstr(&out);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-02 22:36:31 -07:00
|
|
|
|
void
|
|
|
|
|
json_string_escape(const char *in, struct ds *out)
|
|
|
|
|
{
|
|
|
|
|
struct json json = {
|
|
|
|
|
.type = JSON_STRING,
|
2018-05-24 10:32:59 -07:00
|
|
|
|
.string = CONST_CAST(char *, in),
|
2015-04-02 22:36:31 -07:00
|
|
|
|
};
|
|
|
|
|
json_to_ds(&json, 0, out);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
static void
|
|
|
|
|
json_parser_input_string(struct json_parser *p, const char *s)
|
|
|
|
|
{
|
|
|
|
|
struct json_token token;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
token.type = T_STRING;
|
2018-05-24 10:32:59 -07:00
|
|
|
|
token.string = s;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json_parser_input(p, &token);
|
2010-01-26 09:47:54 -08:00
|
|
|
|
}
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
2010-01-26 09:47:54 -08:00
|
|
|
|
static void
|
|
|
|
|
json_lex_string(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
const char *raw = ds_cstr(&p->buffer);
|
|
|
|
|
if (!strchr(raw, '\\')) {
|
|
|
|
|
json_parser_input_string(p, raw);
|
|
|
|
|
} else {
|
|
|
|
|
char *cooked;
|
|
|
|
|
|
|
|
|
|
if (json_string_unescape(raw, strlen(raw), &cooked)) {
|
|
|
|
|
json_parser_input_string(p, cooked);
|
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "%s", cooked);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(cooked);
|
|
|
|
|
}
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Parsing. */
|
|
|
|
|
|
|
|
|
|
/* Parses 'string' as a JSON object or array and returns a newly allocated
|
|
|
|
|
* 'struct json'. The caller must free the returned structure with
|
|
|
|
|
* json_destroy() when it is no longer needed.
|
|
|
|
|
*
|
|
|
|
|
* 'string' must be encoded in UTF-8.
|
|
|
|
|
*
|
|
|
|
|
* If 'string' is valid JSON, then the returned 'struct json' will be either an
|
|
|
|
|
* object (JSON_OBJECT) or an array (JSON_ARRAY).
|
|
|
|
|
*
|
|
|
|
|
* If 'string' is not valid JSON, then the returned 'struct json' will be a
|
|
|
|
|
* string (JSON_STRING) that describes the particular error encountered during
|
|
|
|
|
* parsing. (This is an acceptable means of error reporting because at its top
|
|
|
|
|
* level JSON must be either an object or an array; a bare string is not
|
|
|
|
|
* valid.) */
|
|
|
|
|
struct json *
|
|
|
|
|
json_from_string(const char *string)
|
|
|
|
|
{
|
|
|
|
|
struct json_parser *p = json_parser_create(JSPF_TRAILER);
|
|
|
|
|
json_parser_feed(p, string, strlen(string));
|
|
|
|
|
return json_parser_finish(p);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-24 21:00:37 +02:00
|
|
|
|
/* Parses data of JSON_SERIALIZED_OBJECT to the real JSON. */
|
|
|
|
|
struct json *
|
|
|
|
|
json_from_serialized_object(const struct json *json)
|
|
|
|
|
{
|
|
|
|
|
ovs_assert(json->type == JSON_SERIALIZED_OBJECT);
|
|
|
|
|
return json_from_string(json->string);
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
/* Reads the file named 'file_name', parses its contents as a JSON object or
|
|
|
|
|
* array, and returns a newly allocated 'struct json'. The caller must free
|
|
|
|
|
* the returned structure with json_destroy() when it is no longer needed.
|
|
|
|
|
*
|
|
|
|
|
* The file must be encoded in UTF-8.
|
|
|
|
|
*
|
|
|
|
|
* See json_from_string() for return value semantics.
|
|
|
|
|
*/
|
|
|
|
|
struct json *
|
|
|
|
|
json_from_file(const char *file_name)
|
|
|
|
|
{
|
|
|
|
|
struct json *json;
|
|
|
|
|
FILE *stream;
|
|
|
|
|
|
|
|
|
|
stream = fopen(file_name, "r");
|
|
|
|
|
if (!stream) {
|
|
|
|
|
return json_string_create_nocopy(
|
2013-06-24 10:54:49 -07:00
|
|
|
|
xasprintf("error opening \"%s\": %s", file_name,
|
|
|
|
|
ovs_strerror(errno)));
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
2009-12-17 15:47:27 -08:00
|
|
|
|
json = json_from_stream(stream);
|
|
|
|
|
fclose(stream);
|
|
|
|
|
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parses the contents of 'stream' as a JSON object or array, and returns a
|
|
|
|
|
* newly allocated 'struct json'. The caller must free the returned structure
|
|
|
|
|
* with json_destroy() when it is no longer needed.
|
|
|
|
|
*
|
|
|
|
|
* The file must be encoded in UTF-8.
|
|
|
|
|
*
|
|
|
|
|
* See json_from_string() for return value semantics.
|
|
|
|
|
*/
|
|
|
|
|
struct json *
|
|
|
|
|
json_from_stream(FILE *stream)
|
|
|
|
|
{
|
|
|
|
|
struct json_parser *p;
|
|
|
|
|
struct json *json;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
p = json_parser_create(JSPF_TRAILER);
|
|
|
|
|
for (;;) {
|
|
|
|
|
char buffer[BUFSIZ];
|
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
|
|
n = fread(buffer, 1, sizeof buffer, stream);
|
|
|
|
|
if (!n || json_parser_feed(p, buffer, n) != n) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
json = json_parser_finish(p);
|
|
|
|
|
|
|
|
|
|
if (ferror(stream)) {
|
|
|
|
|
json_destroy(json);
|
|
|
|
|
json = json_string_create_nocopy(
|
2013-06-24 10:54:49 -07:00
|
|
|
|
xasprintf("error reading JSON stream: %s", ovs_strerror(errno)));
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json_parser *
|
|
|
|
|
json_parser_create(int flags)
|
|
|
|
|
{
|
|
|
|
|
struct json_parser *p = xzalloc(sizeof *p);
|
|
|
|
|
p->flags = flags;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
json: Improve string parsing.
To parse a json string prior to this change, json_lex_input is called
with each character of the string. If the character needs to be copied
to the buffer, it is copied individually. This is an expensive
operation, as often there are multiple characters in a row
that need to be copied, and copying memory in blocks is more efficient
than byte by byte. To improve this, the string is now copied
in blocks with an offset counter. A copy is performed when the parser
state equals done.
Functions that are called for each character use a lot of CPU cycles.
Making these functions inline greatly reduces the cycles used and
improves overall performance. Since json_lex_input was only needed in
one place, it doesn't have to be its own function.
There is also a conditional that checks if the current character is a
new line, which is quite unlikely. When this was examined with perf, the
comparison had a very high CPU cycle usage. To improve this, the
OVS_UNLIKELY macro was used, which forces the compiler to switch the
order of the instructions.
Here is the improvement seen in the json-string-benchmark test:
SIZE Q S BEFORE AFTER CHANGE
--------------------------------------------------------
100000 0 0 : 0.842 ms 0.489 ms -41.9 %
100000 2 1 : 0.917 ms 0.535 ms -41.7 %
100000 10 1 : 1.063 ms 0.656 ms -38.3 %
10000000 0 0 : 85.328 ms 49.878 ms -41.5 %
10000000 2 1 : 92.555 ms 54.778 ms -40.8 %
10000000 10 1 : 106.728 ms 66.735 ms -37.5 %
100000000 0 0 : 955.375 ms 621.950 ms -34.9 %
100000000 2 1 : 1031.700 ms 665.200 ms -35.5 %
100000000 10 1 : 1189.300 ms 796.050 ms -33.0 %
Here Q is probability (%) for a character to be a '\"' and
S is probability (%) to be a special character ( < 32).
Signed-off-by: Rosemarie O'Riorden <roriorden@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2022-03-29 12:51:11 -04:00
|
|
|
|
static inline void ALWAYS_INLINE
|
|
|
|
|
json_parser_account_byte(struct json_parser *p, unsigned char c)
|
|
|
|
|
{
|
|
|
|
|
p->byte_number++;
|
|
|
|
|
if (OVS_UNLIKELY(c == '\n')) {
|
|
|
|
|
p->column_number = 0;
|
|
|
|
|
p->line_number++;
|
|
|
|
|
} else {
|
|
|
|
|
p->column_number++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
size_t
|
|
|
|
|
json_parser_feed(struct json_parser *p, const char *input, size_t n)
|
|
|
|
|
{
|
json: Improve string parsing.
To parse a json string prior to this change, json_lex_input is called
with each character of the string. If the character needs to be copied
to the buffer, it is copied individually. This is an expensive
operation, as often there are multiple characters in a row
that need to be copied, and copying memory in blocks is more efficient
than byte by byte. To improve this, the string is now copied
in blocks with an offset counter. A copy is performed when the parser
state equals done.
Functions that are called for each character use a lot of CPU cycles.
Making these functions inline greatly reduces the cycles used and
improves overall performance. Since json_lex_input was only needed in
one place, it doesn't have to be its own function.
There is also a conditional that checks if the current character is a
new line, which is quite unlikely. When this was examined with perf, the
comparison had a very high CPU cycle usage. To improve this, the
OVS_UNLIKELY macro was used, which forces the compiler to switch the
order of the instructions.
Here is the improvement seen in the json-string-benchmark test:
SIZE Q S BEFORE AFTER CHANGE
--------------------------------------------------------
100000 0 0 : 0.842 ms 0.489 ms -41.9 %
100000 2 1 : 0.917 ms 0.535 ms -41.7 %
100000 10 1 : 1.063 ms 0.656 ms -38.3 %
10000000 0 0 : 85.328 ms 49.878 ms -41.5 %
10000000 2 1 : 92.555 ms 54.778 ms -40.8 %
10000000 10 1 : 106.728 ms 66.735 ms -37.5 %
100000000 0 0 : 955.375 ms 621.950 ms -34.9 %
100000000 2 1 : 1031.700 ms 665.200 ms -35.5 %
100000000 10 1 : 1189.300 ms 796.050 ms -33.0 %
Here Q is probability (%) for a character to be a '\"' and
S is probability (%) to be a special character ( < 32).
Signed-off-by: Rosemarie O'Riorden <roriorden@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2022-03-29 12:51:11 -04:00
|
|
|
|
size_t token_start = 0;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
size_t i;
|
json: Improve string parsing.
To parse a json string prior to this change, json_lex_input is called
with each character of the string. If the character needs to be copied
to the buffer, it is copied individually. This is an expensive
operation, as often there are multiple characters in a row
that need to be copied, and copying memory in blocks is more efficient
than byte by byte. To improve this, the string is now copied
in blocks with an offset counter. A copy is performed when the parser
state equals done.
Functions that are called for each character use a lot of CPU cycles.
Making these functions inline greatly reduces the cycles used and
improves overall performance. Since json_lex_input was only needed in
one place, it doesn't have to be its own function.
There is also a conditional that checks if the current character is a
new line, which is quite unlikely. When this was examined with perf, the
comparison had a very high CPU cycle usage. To improve this, the
OVS_UNLIKELY macro was used, which forces the compiler to switch the
order of the instructions.
Here is the improvement seen in the json-string-benchmark test:
SIZE Q S BEFORE AFTER CHANGE
--------------------------------------------------------
100000 0 0 : 0.842 ms 0.489 ms -41.9 %
100000 2 1 : 0.917 ms 0.535 ms -41.7 %
100000 10 1 : 1.063 ms 0.656 ms -38.3 %
10000000 0 0 : 85.328 ms 49.878 ms -41.5 %
10000000 2 1 : 92.555 ms 54.778 ms -40.8 %
10000000 10 1 : 106.728 ms 66.735 ms -37.5 %
100000000 0 0 : 955.375 ms 621.950 ms -34.9 %
100000000 2 1 : 1031.700 ms 665.200 ms -35.5 %
100000000 10 1 : 1189.300 ms 796.050 ms -33.0 %
Here Q is probability (%) for a character to be a '\"' and
S is probability (%) to be a special character ( < 32).
Signed-off-by: Rosemarie O'Riorden <roriorden@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2022-03-29 12:51:11 -04:00
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
for (i = 0; !p->done && i < n; ) {
|
json: Improve string parsing.
To parse a json string prior to this change, json_lex_input is called
with each character of the string. If the character needs to be copied
to the buffer, it is copied individually. This is an expensive
operation, as often there are multiple characters in a row
that need to be copied, and copying memory in blocks is more efficient
than byte by byte. To improve this, the string is now copied
in blocks with an offset counter. A copy is performed when the parser
state equals done.
Functions that are called for each character use a lot of CPU cycles.
Making these functions inline greatly reduces the cycles used and
improves overall performance. Since json_lex_input was only needed in
one place, it doesn't have to be its own function.
There is also a conditional that checks if the current character is a
new line, which is quite unlikely. When this was examined with perf, the
comparison had a very high CPU cycle usage. To improve this, the
OVS_UNLIKELY macro was used, which forces the compiler to switch the
order of the instructions.
Here is the improvement seen in the json-string-benchmark test:
SIZE Q S BEFORE AFTER CHANGE
--------------------------------------------------------
100000 0 0 : 0.842 ms 0.489 ms -41.9 %
100000 2 1 : 0.917 ms 0.535 ms -41.7 %
100000 10 1 : 1.063 ms 0.656 ms -38.3 %
10000000 0 0 : 85.328 ms 49.878 ms -41.5 %
10000000 2 1 : 92.555 ms 54.778 ms -40.8 %
10000000 10 1 : 106.728 ms 66.735 ms -37.5 %
100000000 0 0 : 955.375 ms 621.950 ms -34.9 %
100000000 2 1 : 1031.700 ms 665.200 ms -35.5 %
100000000 10 1 : 1189.300 ms 796.050 ms -33.0 %
Here Q is probability (%) for a character to be a '\"' and
S is probability (%) to be a special character ( < 32).
Signed-off-by: Rosemarie O'Riorden <roriorden@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2022-03-29 12:51:11 -04:00
|
|
|
|
bool consumed = true;
|
|
|
|
|
|
|
|
|
|
const char *start_p = &input[token_start];
|
|
|
|
|
unsigned char c = input[i];
|
|
|
|
|
struct json_token token;
|
|
|
|
|
|
|
|
|
|
switch (p->lex_state) {
|
|
|
|
|
case JSON_LEX_START:
|
|
|
|
|
switch (c) {
|
|
|
|
|
case ' ': case '\t': case '\n': case '\r':
|
|
|
|
|
/* Nothing to do. */
|
|
|
|
|
token_start = i + 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'a': case 'b': case 'c': case 'd': case 'e':
|
|
|
|
|
case 'f': case 'g': case 'h': case 'i': case 'j':
|
|
|
|
|
case 'k': case 'l': case 'm': case 'n': case 'o':
|
|
|
|
|
case 'p': case 'q': case 'r': case 's': case 't':
|
|
|
|
|
case 'u': case 'v': case 'w': case 'x': case 'y':
|
|
|
|
|
case 'z':
|
|
|
|
|
p->lex_state = JSON_LEX_KEYWORD;
|
|
|
|
|
token_start = i;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '[': case '{': case ']': case '}': case ':': case ',':
|
|
|
|
|
token.type = c;
|
|
|
|
|
json_parser_input(p, &token);
|
|
|
|
|
token_start = i + 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
|
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
|
|
|
p->lex_state = JSON_LEX_NUMBER;
|
|
|
|
|
token_start = i;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '"':
|
|
|
|
|
p->lex_state = JSON_LEX_STRING;
|
|
|
|
|
token_start = i + 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if (isprint(c)) {
|
|
|
|
|
json_error(p, "invalid character '%c'", c);
|
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "invalid character U+%04x", c);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_LEX_KEYWORD:
|
|
|
|
|
if (!isalpha((unsigned char) c)) {
|
|
|
|
|
ds_put_buffer(&p->buffer, start_p, i - token_start);
|
|
|
|
|
json_lex_keyword(p);
|
|
|
|
|
consumed = false;
|
|
|
|
|
break;
|
2012-04-26 09:48:28 -07:00
|
|
|
|
}
|
json: Improve string parsing.
To parse a json string prior to this change, json_lex_input is called
with each character of the string. If the character needs to be copied
to the buffer, it is copied individually. This is an expensive
operation, as often there are multiple characters in a row
that need to be copied, and copying memory in blocks is more efficient
than byte by byte. To improve this, the string is now copied
in blocks with an offset counter. A copy is performed when the parser
state equals done.
Functions that are called for each character use a lot of CPU cycles.
Making these functions inline greatly reduces the cycles used and
improves overall performance. Since json_lex_input was only needed in
one place, it doesn't have to be its own function.
There is also a conditional that checks if the current character is a
new line, which is quite unlikely. When this was examined with perf, the
comparison had a very high CPU cycle usage. To improve this, the
OVS_UNLIKELY macro was used, which forces the compiler to switch the
order of the instructions.
Here is the improvement seen in the json-string-benchmark test:
SIZE Q S BEFORE AFTER CHANGE
--------------------------------------------------------
100000 0 0 : 0.842 ms 0.489 ms -41.9 %
100000 2 1 : 0.917 ms 0.535 ms -41.7 %
100000 10 1 : 1.063 ms 0.656 ms -38.3 %
10000000 0 0 : 85.328 ms 49.878 ms -41.5 %
10000000 2 1 : 92.555 ms 54.778 ms -40.8 %
10000000 10 1 : 106.728 ms 66.735 ms -37.5 %
100000000 0 0 : 955.375 ms 621.950 ms -34.9 %
100000000 2 1 : 1031.700 ms 665.200 ms -35.5 %
100000000 10 1 : 1189.300 ms 796.050 ms -33.0 %
Here Q is probability (%) for a character to be a '\"' and
S is probability (%) to be a special character ( < 32).
Signed-off-by: Rosemarie O'Riorden <roriorden@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2022-03-29 12:51:11 -04:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_LEX_NUMBER:
|
|
|
|
|
if (!strchr(".0123456789eE-+", c)) {
|
|
|
|
|
ds_put_buffer(&p->buffer, start_p, i - token_start);
|
|
|
|
|
json_lex_number(p);
|
|
|
|
|
consumed = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_LEX_STRING:
|
|
|
|
|
if (c == '\\') {
|
|
|
|
|
p->lex_state = JSON_LEX_ESCAPE;
|
|
|
|
|
} else if (c == '"') {
|
|
|
|
|
ds_put_buffer(&p->buffer, start_p, i - token_start);
|
|
|
|
|
json_lex_string(p);
|
|
|
|
|
} else if (c < 0x20) {
|
|
|
|
|
json_error(p, "U+%04X must be escaped in quoted string", c);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_LEX_ESCAPE:
|
|
|
|
|
p->lex_state = JSON_LEX_STRING;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ovs_abort(0, "unexpected lexer state");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (consumed) {
|
|
|
|
|
json_parser_account_byte(p, c);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
json: Improve string parsing.
To parse a json string prior to this change, json_lex_input is called
with each character of the string. If the character needs to be copied
to the buffer, it is copied individually. This is an expensive
operation, as often there are multiple characters in a row
that need to be copied, and copying memory in blocks is more efficient
than byte by byte. To improve this, the string is now copied
in blocks with an offset counter. A copy is performed when the parser
state equals done.
Functions that are called for each character use a lot of CPU cycles.
Making these functions inline greatly reduces the cycles used and
improves overall performance. Since json_lex_input was only needed in
one place, it doesn't have to be its own function.
There is also a conditional that checks if the current character is a
new line, which is quite unlikely. When this was examined with perf, the
comparison had a very high CPU cycle usage. To improve this, the
OVS_UNLIKELY macro was used, which forces the compiler to switch the
order of the instructions.
Here is the improvement seen in the json-string-benchmark test:
SIZE Q S BEFORE AFTER CHANGE
--------------------------------------------------------
100000 0 0 : 0.842 ms 0.489 ms -41.9 %
100000 2 1 : 0.917 ms 0.535 ms -41.7 %
100000 10 1 : 1.063 ms 0.656 ms -38.3 %
10000000 0 0 : 85.328 ms 49.878 ms -41.5 %
10000000 2 1 : 92.555 ms 54.778 ms -40.8 %
10000000 10 1 : 106.728 ms 66.735 ms -37.5 %
100000000 0 0 : 955.375 ms 621.950 ms -34.9 %
100000000 2 1 : 1031.700 ms 665.200 ms -35.5 %
100000000 10 1 : 1189.300 ms 796.050 ms -33.0 %
Here Q is probability (%) for a character to be a '\"' and
S is probability (%) to be a special character ( < 32).
Signed-off-by: Rosemarie O'Riorden <roriorden@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2022-03-29 12:51:11 -04:00
|
|
|
|
|
|
|
|
|
if (!p->done) {
|
|
|
|
|
ds_put_buffer(&p->buffer, &input[token_start], i - token_start);
|
|
|
|
|
}
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
json_parser_is_done(const struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
return p->done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct json *
|
|
|
|
|
json_parser_finish(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
struct json *json;
|
|
|
|
|
|
|
|
|
|
switch (p->lex_state) {
|
|
|
|
|
case JSON_LEX_START:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_LEX_STRING:
|
|
|
|
|
case JSON_LEX_ESCAPE:
|
|
|
|
|
json_error(p, "unexpected end of input in quoted string");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_LEX_NUMBER:
|
|
|
|
|
case JSON_LEX_KEYWORD:
|
json: Improve string parsing.
To parse a json string prior to this change, json_lex_input is called
with each character of the string. If the character needs to be copied
to the buffer, it is copied individually. This is an expensive
operation, as often there are multiple characters in a row
that need to be copied, and copying memory in blocks is more efficient
than byte by byte. To improve this, the string is now copied
in blocks with an offset counter. A copy is performed when the parser
state equals done.
Functions that are called for each character use a lot of CPU cycles.
Making these functions inline greatly reduces the cycles used and
improves overall performance. Since json_lex_input was only needed in
one place, it doesn't have to be its own function.
There is also a conditional that checks if the current character is a
new line, which is quite unlikely. When this was examined with perf, the
comparison had a very high CPU cycle usage. To improve this, the
OVS_UNLIKELY macro was used, which forces the compiler to switch the
order of the instructions.
Here is the improvement seen in the json-string-benchmark test:
SIZE Q S BEFORE AFTER CHANGE
--------------------------------------------------------
100000 0 0 : 0.842 ms 0.489 ms -41.9 %
100000 2 1 : 0.917 ms 0.535 ms -41.7 %
100000 10 1 : 1.063 ms 0.656 ms -38.3 %
10000000 0 0 : 85.328 ms 49.878 ms -41.5 %
10000000 2 1 : 92.555 ms 54.778 ms -40.8 %
10000000 10 1 : 106.728 ms 66.735 ms -37.5 %
100000000 0 0 : 955.375 ms 621.950 ms -34.9 %
100000000 2 1 : 1031.700 ms 665.200 ms -35.5 %
100000000 10 1 : 1189.300 ms 796.050 ms -33.0 %
Here Q is probability (%) for a character to be a '\"' and
S is probability (%) to be a special character ( < 32).
Signed-off-by: Rosemarie O'Riorden <roriorden@redhat.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
2022-03-29 12:51:11 -04:00
|
|
|
|
json_parser_feed(p, " ", 1);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p->parse_state == JSON_PARSE_START) {
|
|
|
|
|
json_error(p, "empty input stream");
|
|
|
|
|
} else if (p->parse_state != JSON_PARSE_END) {
|
|
|
|
|
json_error(p, "unexpected end of input");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!p->error) {
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert(p->height == 1);
|
|
|
|
|
ovs_assert(p->stack[0].json != NULL);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json = p->stack[--p->height].json;
|
|
|
|
|
} else {
|
|
|
|
|
json = json_string_create_nocopy(p->error);
|
|
|
|
|
p->error = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_parser_abort(p);
|
|
|
|
|
|
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
json_parser_abort(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
if (p) {
|
|
|
|
|
ds_destroy(&p->buffer);
|
|
|
|
|
if (p->height) {
|
|
|
|
|
json_destroy(p->stack[0].json);
|
|
|
|
|
}
|
|
|
|
|
free(p->stack);
|
|
|
|
|
free(p->member_name);
|
|
|
|
|
free(p->error);
|
|
|
|
|
free(p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct json_parser_node *
|
|
|
|
|
json_parser_top(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
return &p->stack[p->height - 1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_parser_put_value(struct json_parser *p, struct json *value)
|
|
|
|
|
{
|
|
|
|
|
struct json_parser_node *node = json_parser_top(p);
|
|
|
|
|
if (node->json->type == JSON_OBJECT) {
|
2018-03-23 15:46:58 -07:00
|
|
|
|
json_object_put_nocopy(node->json, p->member_name, value);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
p->member_name = NULL;
|
|
|
|
|
} else if (node->json->type == JSON_ARRAY) {
|
|
|
|
|
json_array_add(node->json, value);
|
|
|
|
|
} else {
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-22 13:38:39 -07:00
|
|
|
|
static void
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json_parser_push(struct json_parser *p,
|
|
|
|
|
struct json *new_json, enum json_parse_state new_state)
|
|
|
|
|
{
|
|
|
|
|
if (p->height < JSON_MAX_HEIGHT) {
|
|
|
|
|
struct json_parser_node *node;
|
|
|
|
|
|
|
|
|
|
if (p->height >= p->allocated_height) {
|
|
|
|
|
p->stack = x2nrealloc(p->stack, &p->allocated_height,
|
|
|
|
|
sizeof *p->stack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p->height > 0) {
|
|
|
|
|
json_parser_put_value(p, new_json);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node = &p->stack[p->height++];
|
|
|
|
|
node->json = new_json;
|
|
|
|
|
p->parse_state = new_state;
|
|
|
|
|
} else {
|
2010-02-01 14:57:27 -08:00
|
|
|
|
json_destroy(new_json);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
json_error(p, "input exceeds maximum nesting depth %d",
|
|
|
|
|
JSON_MAX_HEIGHT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_parser_push_object(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
json_parser_push(p, json_object_create(), JSON_PARSE_OBJECT_INIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_parser_push_array(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
json_parser_push(p, json_array_create_empty(), JSON_PARSE_ARRAY_INIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_parse_value(struct json_parser *p, struct json_token *token,
|
|
|
|
|
enum json_parse_state next_state)
|
|
|
|
|
{
|
|
|
|
|
struct json *value;
|
|
|
|
|
|
|
|
|
|
switch (token->type) {
|
|
|
|
|
case T_FALSE:
|
|
|
|
|
value = json_boolean_create(false);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case T_NULL:
|
|
|
|
|
value = json_null_create();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case T_TRUE:
|
|
|
|
|
value = json_boolean_create(true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '{':
|
|
|
|
|
json_parser_push_object(p);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case '[':
|
|
|
|
|
json_parser_push_array(p);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case T_INTEGER:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
value = json_integer_create(token->integer);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case T_REAL:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
value = json_real_create(token->real);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case T_STRING:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
value = json_string_create(token->string);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case T_EOF:
|
|
|
|
|
case '}':
|
|
|
|
|
case ']':
|
|
|
|
|
case ':':
|
|
|
|
|
case ',':
|
|
|
|
|
default:
|
|
|
|
|
json_error(p, "syntax error expecting value");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_parser_put_value(p, value);
|
|
|
|
|
p->parse_state = next_state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_parser_pop(struct json_parser *p)
|
|
|
|
|
{
|
|
|
|
|
struct json_parser_node *node;
|
|
|
|
|
|
|
|
|
|
/* Conserve memory. */
|
|
|
|
|
node = json_parser_top(p);
|
|
|
|
|
if (node->json->type == JSON_ARRAY) {
|
|
|
|
|
json_array_trim(node->json);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pop off the top-of-stack. */
|
|
|
|
|
if (p->height == 1) {
|
|
|
|
|
p->parse_state = JSON_PARSE_END;
|
|
|
|
|
if (!(p->flags & JSPF_TRAILER)) {
|
|
|
|
|
p->done = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
p->height--;
|
|
|
|
|
node = json_parser_top(p);
|
|
|
|
|
if (node->json->type == JSON_ARRAY) {
|
|
|
|
|
p->parse_state = JSON_PARSE_ARRAY_NEXT;
|
|
|
|
|
} else if (node->json->type == JSON_OBJECT) {
|
|
|
|
|
p->parse_state = JSON_PARSE_OBJECT_NEXT;
|
|
|
|
|
} else {
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_parser_input(struct json_parser *p, struct json_token *token)
|
|
|
|
|
{
|
|
|
|
|
switch (p->parse_state) {
|
|
|
|
|
case JSON_PARSE_START:
|
|
|
|
|
if (token->type == '{') {
|
|
|
|
|
json_parser_push_object(p);
|
|
|
|
|
} else if (token->type == '[') {
|
|
|
|
|
json_parser_push_array(p);
|
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "syntax error at beginning of input");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_PARSE_END:
|
|
|
|
|
json_error(p, "trailing garbage at end of input");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_PARSE_OBJECT_INIT:
|
|
|
|
|
if (token->type == '}') {
|
|
|
|
|
json_parser_pop(p);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Fall through. */
|
|
|
|
|
case JSON_PARSE_OBJECT_NAME:
|
|
|
|
|
if (token->type == T_STRING) {
|
2018-05-24 10:32:59 -07:00
|
|
|
|
p->member_name = xstrdup(token->string);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
p->parse_state = JSON_PARSE_OBJECT_COLON;
|
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "syntax error parsing object expecting string");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_PARSE_OBJECT_COLON:
|
|
|
|
|
if (token->type == ':') {
|
|
|
|
|
p->parse_state = JSON_PARSE_OBJECT_VALUE;
|
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "syntax error parsing object expecting ':'");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_PARSE_OBJECT_VALUE:
|
|
|
|
|
json_parse_value(p, token, JSON_PARSE_OBJECT_NEXT);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_PARSE_OBJECT_NEXT:
|
|
|
|
|
if (token->type == ',') {
|
|
|
|
|
p->parse_state = JSON_PARSE_OBJECT_NAME;
|
|
|
|
|
} else if (token->type == '}') {
|
|
|
|
|
json_parser_pop(p);
|
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "syntax error expecting '}' or ','");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_PARSE_ARRAY_INIT:
|
|
|
|
|
if (token->type == ']') {
|
|
|
|
|
json_parser_pop(p);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Fall through. */
|
|
|
|
|
case JSON_PARSE_ARRAY_VALUE:
|
|
|
|
|
json_parse_value(p, token, JSON_PARSE_ARRAY_NEXT);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_PARSE_ARRAY_NEXT:
|
|
|
|
|
if (token->type == ',') {
|
|
|
|
|
p->parse_state = JSON_PARSE_ARRAY_VALUE;
|
|
|
|
|
} else if (token->type == ']') {
|
|
|
|
|
json_parser_pop(p);
|
|
|
|
|
} else {
|
|
|
|
|
json_error(p, "syntax error expecting ']' or ','");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p->lex_state = JSON_LEX_START;
|
|
|
|
|
ds_clear(&p->buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct json *
|
|
|
|
|
json_create(enum json_type type)
|
|
|
|
|
{
|
|
|
|
|
struct json *json = xmalloc(sizeof *json);
|
|
|
|
|
json->type = type;
|
2016-10-04 19:31:48 +00:00
|
|
|
|
json->count = 1;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
json_error(struct json_parser *p, const char *format, ...)
|
|
|
|
|
{
|
|
|
|
|
if (!p->error) {
|
2009-12-03 16:08:34 -08:00
|
|
|
|
struct ds msg;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
va_list args;
|
|
|
|
|
|
2009-12-03 16:08:34 -08:00
|
|
|
|
ds_init(&msg);
|
|
|
|
|
ds_put_format(&msg, "line %d, column %d, byte %d: ",
|
|
|
|
|
p->line_number, p->column_number, p->byte_number);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
va_start(args, format);
|
2009-12-03 16:08:34 -08:00
|
|
|
|
ds_put_format_valist(&msg, format, args);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
va_end(args);
|
|
|
|
|
|
2009-12-03 16:08:34 -08:00
|
|
|
|
p->error = ds_steal_cstr(&msg);
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
p->done = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SPACES_PER_LEVEL 2
|
|
|
|
|
|
|
|
|
|
struct json_serializer {
|
2010-01-22 14:57:18 -08:00
|
|
|
|
struct ds *ds;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
int depth;
|
|
|
|
|
int flags;
|
|
|
|
|
};
|
|
|
|
|
|
2010-01-22 14:57:18 -08:00
|
|
|
|
static void json_serialize(const struct json *, struct json_serializer *);
|
|
|
|
|
static void json_serialize_object(const struct shash *object,
|
|
|
|
|
struct json_serializer *);
|
|
|
|
|
static void json_serialize_array(const struct json_array *,
|
|
|
|
|
struct json_serializer *);
|
|
|
|
|
static void json_serialize_string(const char *, struct ds *);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
/* Converts 'json' to a string in JSON format, encoded in UTF-8, and returns
|
|
|
|
|
* that string. The caller is responsible for freeing the returned string,
|
|
|
|
|
* with free(), when it is no longer needed.
|
|
|
|
|
*
|
|
|
|
|
* If 'flags' contains JSSF_PRETTY, the output is pretty-printed with each
|
|
|
|
|
* nesting level introducing an additional indentation. Otherwise, the
|
|
|
|
|
* returned string does not contain any new-line characters.
|
|
|
|
|
*
|
|
|
|
|
* If 'flags' contains JSSF_SORT, members of objects in the output are sorted
|
|
|
|
|
* in bytewise lexicographic order for reproducibility. Otherwise, members of
|
|
|
|
|
* objects are output in an indeterminate order.
|
|
|
|
|
*
|
|
|
|
|
* The returned string is valid JSON only if 'json' represents an array or an
|
|
|
|
|
* object, since a bare literal does not satisfy the JSON grammar. */
|
|
|
|
|
char *
|
|
|
|
|
json_to_string(const struct json *json, int flags)
|
2010-01-22 14:57:18 -08:00
|
|
|
|
{
|
|
|
|
|
struct ds ds;
|
|
|
|
|
|
|
|
|
|
ds_init(&ds);
|
|
|
|
|
json_to_ds(json, flags, &ds);
|
|
|
|
|
return ds_steal_cstr(&ds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Same as json_to_string(), but the output is appended to 'ds'. */
|
|
|
|
|
void
|
|
|
|
|
json_to_ds(const struct json *json, int flags, struct ds *ds)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
|
|
|
|
struct json_serializer s;
|
2010-01-22 14:57:18 -08:00
|
|
|
|
|
|
|
|
|
s.ds = ds;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
s.depth = 0;
|
|
|
|
|
s.flags = flags;
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize(json, &s);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize(const struct json *json, struct json_serializer *s)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
2010-01-22 14:57:18 -08:00
|
|
|
|
struct ds *ds = s->ds;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
switch (json->type) {
|
|
|
|
|
case JSON_NULL:
|
|
|
|
|
ds_put_cstr(ds, "null");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_FALSE:
|
|
|
|
|
ds_put_cstr(ds, "false");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_TRUE:
|
|
|
|
|
ds_put_cstr(ds, "true");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_OBJECT:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json_serialize_object(json->object, s);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_ARRAY:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json_serialize_array(&json->array, s);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_INTEGER:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
ds_put_format(ds, "%lld", json->integer);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_REAL:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
ds_put_format(ds, "%.*g", DBL_DIG, json->real);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case JSON_STRING:
|
2018-05-24 10:32:59 -07:00
|
|
|
|
json_serialize_string(json->string, ds);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2021-08-24 21:00:37 +02:00
|
|
|
|
case JSON_SERIALIZED_OBJECT:
|
|
|
|
|
ds_put_cstr(ds, json->string);
|
|
|
|
|
break;
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
case JSON_N_TYPES:
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
indent_line(struct json_serializer *s)
|
|
|
|
|
{
|
|
|
|
|
if (s->flags & JSSF_PRETTY) {
|
2010-01-22 14:57:18 -08:00
|
|
|
|
ds_put_char(s->ds, '\n');
|
|
|
|
|
ds_put_char_multiple(s->ds, ' ', SPACES_PER_LEVEL * s->depth);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize_object_member(size_t i, const struct shash_node *node,
|
|
|
|
|
struct json_serializer *s)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
2010-01-22 14:57:18 -08:00
|
|
|
|
struct ds *ds = s->ds;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
if (i) {
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
indent_line(s);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize_string(node->name, ds);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
ds_put_char(ds, ':');
|
|
|
|
|
if (s->flags & JSSF_PRETTY) {
|
|
|
|
|
ds_put_char(ds, ' ');
|
|
|
|
|
}
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize(node->data, s);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize_object(const struct shash *object, struct json_serializer *s)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
2010-01-22 14:57:18 -08:00
|
|
|
|
struct ds *ds = s->ds;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
ds_put_char(ds, '{');
|
|
|
|
|
|
|
|
|
|
s->depth++;
|
|
|
|
|
indent_line(s);
|
|
|
|
|
|
|
|
|
|
if (s->flags & JSSF_SORT) {
|
|
|
|
|
const struct shash_node **nodes;
|
|
|
|
|
size_t n, i;
|
|
|
|
|
|
|
|
|
|
nodes = shash_sort(object);
|
|
|
|
|
n = shash_count(object);
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize_object_member(i, nodes[i], s);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
free(nodes);
|
|
|
|
|
} else {
|
|
|
|
|
struct shash_node *node;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
SHASH_FOR_EACH (node, object) {
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize_object_member(i++, node, s);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_char(ds, '}');
|
|
|
|
|
s->depth--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize_array(const struct json_array *array, struct json_serializer *s)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
2010-01-22 14:57:18 -08:00
|
|
|
|
struct ds *ds = s->ds;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
ds_put_char(ds, '[');
|
|
|
|
|
s->depth++;
|
|
|
|
|
|
|
|
|
|
if (array->n > 0) {
|
|
|
|
|
indent_line(s);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < array->n; i++) {
|
|
|
|
|
if (i) {
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
indent_line(s);
|
|
|
|
|
}
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize(array->elems[i], s);
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s->depth--;
|
|
|
|
|
ds_put_char(ds, ']');
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-06 11:33:39 +09:00
|
|
|
|
static const char *chars_escaping[256] = {
|
2016-10-05 16:47:21 +00:00
|
|
|
|
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007",
|
|
|
|
|
"\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f",
|
|
|
|
|
"\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017",
|
|
|
|
|
"\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f",
|
|
|
|
|
" ", "!", "\\\"", "#", "$", "%", "&", "'",
|
|
|
|
|
"(", ")", "*", "+", ",", "-", ".", "/",
|
|
|
|
|
"0", "1", "2", "3", "4", "5", "6", "7",
|
|
|
|
|
"8", "9", ":", ";", "<", "=", ">", "?",
|
|
|
|
|
"@", "A", "B", "C", "D", "E", "F", "G",
|
|
|
|
|
"H", "I", "J", "K", "L", "M", "N", "O",
|
|
|
|
|
"P", "Q", "R", "S", "T", "U", "V", "W",
|
|
|
|
|
"X", "Y", "Z", "[", "\\\\", "]", "^", "_",
|
|
|
|
|
"`", "a", "b", "c", "d", "e", "f", "g",
|
|
|
|
|
"h", "i", "j", "k", "l", "m", "n", "o",
|
|
|
|
|
"p", "q", "r", "s", "t", "u", "v", "w",
|
|
|
|
|
"x", "y", "z", "{", "|", "}", "~", "\x7f",
|
|
|
|
|
"\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87",
|
|
|
|
|
"\x88", "\x89", "\x8a", "\x8b", "\x8c", "\x8d", "\x8e", "\x8f",
|
|
|
|
|
"\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97",
|
|
|
|
|
"\x98", "\x99", "\x9a", "\x9b", "\x9c", "\x9d", "\x9e", "\x9f",
|
|
|
|
|
"\xa0", "\xa1", "\xa2", "\xa3", "\xa4", "\xa5", "\xa6", "\xa7",
|
|
|
|
|
"\xa8", "\xa9", "\xaa", "\xab", "\xac", "\xad", "\xae", "\xaf",
|
|
|
|
|
"\xb0", "\xb1", "\xb2", "\xb3", "\xb4", "\xb5", "\xb6", "\xb7",
|
|
|
|
|
"\xb8", "\xb9", "\xba", "\xbb", "\xbc", "\xbd", "\xbe", "\xbf",
|
|
|
|
|
"\xc0", "\xc1", "\xc2", "\xc3", "\xc4", "\xc5", "\xc6", "\xc7",
|
|
|
|
|
"\xc8", "\xc9", "\xca", "\xcb", "\xcc", "\xcd", "\xce", "\xcf",
|
|
|
|
|
"\xd0", "\xd1", "\xd2", "\xd3", "\xd4", "\xd5", "\xd6", "\xd7",
|
|
|
|
|
"\xd8", "\xd9", "\xda", "\xdb", "\xdc", "\xdd", "\xde", "\xdf",
|
|
|
|
|
"\xe0", "\xe1", "\xe2", "\xe3", "\xe4", "\xe5", "\xe6", "\xe7",
|
|
|
|
|
"\xe8", "\xe9", "\xea", "\xeb", "\xec", "\xed", "\xee", "\xef",
|
|
|
|
|
"\xf0", "\xf1", "\xf2", "\xf3", "\xf4", "\xf5", "\xf6", "\xf7",
|
|
|
|
|
"\xf8", "\xf9", "\xfa", "\xfb", "\xfc", "\xfd", "\xfe", "\xff"
|
|
|
|
|
};
|
|
|
|
|
|
2009-11-04 14:55:53 -08:00
|
|
|
|
static void
|
2010-01-22 14:57:18 -08:00
|
|
|
|
json_serialize_string(const char *string, struct ds *ds)
|
2009-11-04 14:55:53 -08:00
|
|
|
|
{
|
|
|
|
|
uint8_t c;
|
2016-10-05 16:47:21 +00:00
|
|
|
|
uint8_t c2;
|
2021-08-24 23:07:22 +02:00
|
|
|
|
size_t count;
|
2016-10-05 16:47:21 +00:00
|
|
|
|
const char *escape;
|
2021-08-24 23:07:22 +02:00
|
|
|
|
const char *start;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
|
|
|
|
|
ds_put_char(ds, '"');
|
2021-08-24 23:07:22 +02:00
|
|
|
|
count = 0;
|
|
|
|
|
start = string;
|
2009-11-04 14:55:53 -08:00
|
|
|
|
while ((c = *string++) != '\0') {
|
2021-08-24 23:07:22 +02:00
|
|
|
|
if (c >= ' ' && c != '"' && c != '\\') {
|
|
|
|
|
count++;
|
|
|
|
|
} else {
|
|
|
|
|
if (count) {
|
|
|
|
|
ds_put_buffer(ds, start, count);
|
|
|
|
|
count = 0;
|
|
|
|
|
}
|
|
|
|
|
start = string;
|
|
|
|
|
escape = chars_escaping[c];
|
|
|
|
|
while ((c2 = *escape++) != '\0') {
|
|
|
|
|
ds_put_char(ds, c2);
|
|
|
|
|
}
|
2009-11-04 14:55:53 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-24 23:07:22 +02:00
|
|
|
|
if (count) {
|
|
|
|
|
ds_put_buffer(ds, start, count);
|
|
|
|
|
}
|
2009-11-04 14:55:53 -08:00
|
|
|
|
ds_put_char(ds, '"');
|
|
|
|
|
}
|