Skip to content

Commit

Permalink
json: Use JSON maps instead of string hashes for objects.
Browse files Browse the repository at this point in the history
Use of shash requires copying of the keys every time a new JSON object
is constructed.  We're spending a lot of CPU and memory resources on
copying column names into objects for every database row, we copy
all the sets between database datum and JSON objects, and we copy all
the keys in maps.

Let's use jsmap instead of an shash as a base data structure for JSON
objects.  That will allow sharing keys between the ovsdb_datum and
JSON objects using reference counting, because keys will be now JSON
structures (JSON_STRING) instead of plain byte arrays.

This commit, however, is only a ground work replacing the base data
structure and updating all the users.  No functional changes are
done here.  If anything, it can make performance a little worse until
the optimizations are done in the next commits.

Signed-off-by: Ilya Maximets <[email protected]>
  • Loading branch information
igsilya committed Dec 17, 2024
1 parent db6ea3c commit a13e21b
Show file tree
Hide file tree
Showing 23 changed files with 358 additions and 350 deletions.
8 changes: 5 additions & 3 deletions include/openvswitch/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
*/

#include <stdio.h>
#include "openvswitch/shash.h"
#include "openvswitch/util.h"

#ifdef __cplusplus
Expand All @@ -40,6 +39,7 @@ extern "C" {

struct ds;
struct uuid;
struct jsmap;

/* Type of a JSON value. */
enum json_type {
Expand Down Expand Up @@ -88,7 +88,7 @@ struct json {
enum json_storage_type storage_type;
size_t count;
union {
struct shash *object; /* Contains "struct json *"s. */
struct jsmap *object; /* Contains "struct json *"s. */
union {
struct json *elements[JSON_ARRAY_INLINE_LEN];
struct json_array array;
Expand Down Expand Up @@ -129,12 +129,14 @@ void json_object_put_string(struct json *,
void json_object_put_format(struct json *,
const char *name, const char *format, ...)
OVS_PRINTF_FORMAT(3, 4);
struct json *json_object_find(const struct json *json, const char *);
struct json *json_object_find_and_delete(struct json *json, const char *);

const char *json_string(const struct json *);
const char *json_serialized_object(const struct json *);
size_t json_array_size(const struct json *);
const struct json *json_array_at(const struct json *, size_t index);
struct shash *json_object(const struct json *);
struct jsmap *json_object(const struct json *);
bool json_boolean(const struct json *);
double json_real(const struct json *);
int64_t json_integer(const struct json *);
Expand Down
130 changes: 61 additions & 69 deletions lib/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
#include <string.h>

#include "cooperative-multitasking.h"
#include "openvswitch/dynamic-string.h"
#include "hash.h"
#include "json.h"
#include "openvswitch/shash.h"
#include "openvswitch/dynamic-string.h"
#include "openvswitch/jsmap.h"
#include "unicode.h"
#include "util.h"
#include "uuid.h"
Expand Down Expand Up @@ -390,7 +390,7 @@ json_object_create(void)
{
struct json *json = json_create(JSON_OBJECT);
json->object = xmalloc(sizeof *json->object);
shash_init(json->object);
jsmap_init(json->object);
return json;
}

Expand All @@ -413,13 +413,21 @@ json_real_create(double real)
void
json_object_put(struct json *json, const char *name, struct json *value)
{
json_destroy(shash_replace(json->object, name, value));
struct json *key = json_string_create(name);

jsmap_replace(json->object, key, value, false);
json_destroy(key);
json_destroy(value);
}

void
json_object_put_nocopy(struct json *json, char *name, struct json *value)
{
json_destroy(shash_replace_nocopy(json->object, name, value));
struct json *key = json_string_create_nocopy(name);

jsmap_replace(json->object, key, value, false);
json_destroy(key);
json_destroy(value);
}

void
Expand All @@ -439,6 +447,26 @@ json_object_put_format(struct json *json,
va_end(args);
}

struct json *
json_object_find(const struct json *json, const char *name)
{
struct json *key = json_string_create(name);
struct jsmap_node *node = jsmap_get_node(json->object, key);

json_destroy(key);
return node ? node->value : NULL;
}

struct json *
json_object_find_and_delete(struct json *json, const char *name)
{
struct json *key = json_string_create(name);
struct json *value = jsmap_find_and_delete(json->object, key);

json_destroy(key);
return value;
}

const char *
json_string(const struct json *json)
{
Expand Down Expand Up @@ -483,11 +511,11 @@ json_array_at(const struct json *json, size_t index)
return json->elements[index];
}

struct shash *
struct jsmap *
json_object(const struct json *json)
{
ovs_assert(json->type == JSON_OBJECT);
return CONST_CAST(struct shash *, json->object);
return CONST_CAST(struct jsmap *, json->object);
}

bool
Expand All @@ -511,7 +539,7 @@ json_integer(const struct json *json)
return json->integer;
}

static void json_destroy_object(struct shash *object, bool yield);
static void json_destroy_object(struct jsmap *object, bool yield);
static void json_destroy_array(struct json *json, bool yield);

/* Frees 'json' and everything it points to, recursively. */
Expand Down Expand Up @@ -551,25 +579,12 @@ json_destroy__(struct json *json, bool yield)
}

static void
json_destroy_object(struct shash *object, bool yield)
json_destroy_object(struct jsmap *object, bool yield)
{
struct shash_node *node;

if (yield) {
cooperative_multitasking_yield();
}

SHASH_FOR_EACH_SAFE (node, object) {
struct json *value = node->data;

if (yield) {
json_destroy_with_yield(value);
} else {
json_destroy(value);
}
shash_delete(object, node);
}
shash_destroy(object);
jsmap_destroy(object, yield);
free(object);
}

Expand All @@ -595,7 +610,7 @@ json_destroy_array(struct json *json, bool yield)
}
}

static struct json *json_deep_clone_object(const struct shash *object);
static struct json *json_deep_clone_object(const struct jsmap *object);
static struct json *json_deep_clone_array(const struct json *);

/* Returns a deep copy of 'json'. */
Expand Down Expand Up @@ -639,16 +654,11 @@ json_nullable_clone(const struct json *json)
}

static struct json *
json_deep_clone_object(const struct shash *object)
json_deep_clone_object(const struct jsmap *object)
{
struct shash_node *node;
struct json *json;
struct json *json = json_object_create();

json = json_object_create();
SHASH_FOR_EACH (node, object) {
struct json *value = node->data;
json_object_put(json, node->name, json_deep_clone(value));
}
jsmap_clone(json->object, object, true);
return json;
}

Expand Down Expand Up @@ -683,17 +693,18 @@ json_deep_clone_array(const struct json *json)
}

static size_t
json_hash_object(const struct shash *object, size_t basis)
json_hash_object(const struct jsmap *object, size_t basis)
{
const struct shash_node **nodes;
const struct jsmap_node **nodes;
size_t n, i;

nodes = shash_sort(object);
n = shash_count(object);
nodes = jsmap_sort(object);
n = jsmap_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);
const struct jsmap_node *node = nodes[i];

basis = json_hash(node->key, basis);
basis = json_hash(node->value, basis);
}
free(nodes);
return basis;
Expand Down Expand Up @@ -744,25 +755,6 @@ json_hash(const struct json *json, size_t basis)
}
}

static bool
json_equal_object(const struct shash *a, const struct shash *b)
{
struct shash_node *a_node;

if (shash_count(a) != shash_count(b)) {
return false;
}

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)) {
return false;
}
}

return true;
}

static bool
json_equal_array(const struct json *a, const struct json *b)
{
Expand Down Expand Up @@ -794,7 +786,7 @@ json_equal(const struct json *a, const struct json *b)

switch (a->type) {
case JSON_OBJECT:
return json_equal_object(a->object, b->object);
return jsmap_equal(a->object, b->object);

case JSON_ARRAY:
return json_equal_array(a, b);
Expand Down Expand Up @@ -1718,7 +1710,7 @@ struct json_serializer {
};

static void json_serialize(const struct json *, struct json_serializer *);
static void json_serialize_object(const struct shash *object,
static void json_serialize_object(const struct jsmap *object,
struct json_serializer *);
static void json_serialize_array(const struct json *,
struct json_serializer *);
Expand Down Expand Up @@ -1818,7 +1810,7 @@ indent_line(struct json_serializer *s)
}

static void
json_serialize_object_member(size_t i, const struct shash_node *node,
json_serialize_object_member(size_t i, const struct jsmap_node *node,
struct json_serializer *s)
{
struct ds *ds = s->ds;
Expand All @@ -1828,16 +1820,16 @@ json_serialize_object_member(size_t i, const struct shash_node *node,
indent_line(s);
}

json_serialize_string(node->name, ds);
json_serialize(node->key, s);
ds_put_char(ds, ':');
if (s->flags & JSSF_PRETTY) {
ds_put_char(ds, ' ');
}
json_serialize(node->data, s);
json_serialize(node->value, s);
}

static void
json_serialize_object(const struct shash *object, struct json_serializer *s)
json_serialize_object(const struct jsmap *object, struct json_serializer *s)
{
struct ds *ds = s->ds;

Expand All @@ -1851,21 +1843,21 @@ json_serialize_object(const struct shash *object, struct json_serializer *s)
}

if (s->flags & JSSF_SORT) {
const struct shash_node **nodes;
const struct jsmap_node **nodes;
size_t n, i;

nodes = shash_sort(object);
n = shash_count(object);
nodes = jsmap_sort(object);
n = jsmap_count(object);
for (i = 0; i < n; i++) {
json_serialize_object_member(i, nodes[i], s);
}
free(nodes);
} else {
struct shash_node *node;
struct jsmap_node *node;
size_t i;

i = 0;
SHASH_FOR_EACH (node, object) {
JSMAP_FOR_EACH (node, object) {
json_serialize_object_member(i++, node, s);
}
}
Expand Down
21 changes: 12 additions & 9 deletions lib/jsonrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "byteq.h"
#include "openvswitch/dynamic-string.h"
#include "fatal-signal.h"
#include "openvswitch/jsmap.h"
#include "openvswitch/json.h"
#include "openvswitch/list.h"
#include "openvswitch/ofpbuf.h"
Expand Down Expand Up @@ -713,34 +714,36 @@ jsonrpc_msg_from_json(struct json *json, struct jsonrpc_msg **msgp)
{
struct json *method = NULL;
struct jsonrpc_msg *msg = NULL;
struct shash *object;
char *error;

if (json->type != JSON_OBJECT) {
error = xstrdup("message is not a JSON object");
goto exit;
}
object = json_object(json);

method = shash_find_and_delete(object, "method");
method = json_object_find_and_delete(json, "method");
if (method && method->type != JSON_STRING) {
error = xstrdup("method is not a JSON string");
goto exit;
}

msg = xzalloc(sizeof *msg);
msg->method = method ? xstrdup(json_string(method)) : NULL;
msg->params = null_from_json_null(shash_find_and_delete(object, "params"));
msg->result = null_from_json_null(shash_find_and_delete(object, "result"));
msg->error = null_from_json_null(shash_find_and_delete(object, "error"));
msg->id = null_from_json_null(shash_find_and_delete(object, "id"));
msg->params = null_from_json_null(
json_object_find_and_delete(json, "params"));
msg->result = null_from_json_null(
json_object_find_and_delete(json, "result"));
msg->error = null_from_json_null(
json_object_find_and_delete(json, "error"));
msg->id = null_from_json_null(
json_object_find_and_delete(json, "id"));
msg->type = (msg->result ? JSONRPC_REPLY
: msg->error ? JSONRPC_ERROR
: msg->id ? JSONRPC_REQUEST
: JSONRPC_NOTIFY);
if (!shash_is_empty(object)) {
if (!jsmap_is_empty(json_object(json))) {
error = xasprintf("message has unexpected member \"%s\"",
shash_first(object)->name);
json_string(jsmap_first(json_object(json))->key));
goto exit;
}
error = jsonrpc_msg_is_valid(msg);
Expand Down
Loading

0 comments on commit a13e21b

Please sign in to comment.