diff --git a/include/openvswitch/json.h b/include/openvswitch/json.h index 519289148e1..fbdb3523362 100644 --- a/include/openvswitch/json.h +++ b/include/openvswitch/json.h @@ -32,6 +32,7 @@ #include #include "openvswitch/shash.h" +#include "openvswitch/util.h" #ifdef __cplusplus extern "C" { @@ -66,9 +67,19 @@ struct json_array { /* Maximum string length that can be stored inline ('\0' is not included). */ #define JSON_STRING_INLINE_LEN (sizeof(struct json_array) - 1) +#define JSON_ARRAY_INLINE_LEN 3 +BUILD_ASSERT_DECL(sizeof(struct json_array) / sizeof(struct json *) + >= JSON_ARRAY_INLINE_LEN); + enum json_storage_type { - JSON_STRING_DYNAMIC = 0, /* JSON_STRING is stored via 'str_ptr'. */ - JSON_STRING_INLINE, /* JSON_STRING is stored in 'str' array. */ + /* JSON_STRING storage types. */ + JSON_STRING_DYNAMIC = 0, /* Stored via 'str_ptr'. */ + JSON_STRING_INLINE, /* Stored in 'str' array. */ + /* JSON_ARRAY storage types.*/ + JSON_ARRAY_DYNAMIC, /* 'elems' is a dynamically allocated array. */ + JSON_ARRAY_INLINE_1, /* Static 'elems' with exactly 1 element. */ + JSON_ARRAY_INLINE_2, /* Static 'elems' with exactly 2 elements. */ + JSON_ARRAY_INLINE_3, /* Static 'elems' with exactly 3 elements. */ }; /* A JSON value. */ @@ -78,7 +89,10 @@ struct json { size_t count; union { struct shash *object; /* Contains "struct json *"s. */ - struct json_array array; + union { + struct json *elements[JSON_ARRAY_INLINE_LEN]; + struct json_array array; + }; /* JSON_ARRAY. */ long long int integer; double real; union { diff --git a/lib/json.c b/lib/json.c index 27f1d014ee8..bbf3055cb37 100644 --- a/lib/json.c +++ b/lib/json.c @@ -221,6 +221,7 @@ struct json * json_array_create_empty(void) { struct json *json = json_create(JSON_ARRAY); + json->storage_type = JSON_ARRAY_DYNAMIC; json->array.elements = NULL; json->array.size = 0; json->array.allocated = 0; @@ -230,6 +231,39 @@ json_array_create_empty(void) void json_array_add(struct json *array_, struct json *element) { + switch (array_->storage_type) { + case JSON_ARRAY_DYNAMIC: + if (!array_->array.size) { + array_->storage_type = JSON_ARRAY_INLINE_1; + array_->elements[0] = element; + return; + } + break; + + case JSON_ARRAY_INLINE_1: + case JSON_ARRAY_INLINE_2: + array_->elements[array_->storage_type - JSON_ARRAY_DYNAMIC] = element; + array_->storage_type++; + return; + + case JSON_ARRAY_INLINE_3: { + struct json **elements = xmalloc(4 * sizeof *elements); + + memcpy(elements, array_->elements, 3 * sizeof array_->elements[0]); + array_->array.elements = elements; + array_->array.elements[3] = element; + array_->array.size = 4; + array_->array.allocated = 4; + array_->storage_type = JSON_ARRAY_DYNAMIC; + return; + } + + case JSON_STRING_DYNAMIC: + case JSON_STRING_INLINE: + default: + OVS_NOT_REACHED(); + } + struct json_array *array = &array_->array; if (array->size >= array->allocated) { array->elements = x2nrealloc(array->elements, &array->allocated, @@ -242,7 +276,11 @@ void json_array_set(struct json *json, size_t index, struct json *element) { ovs_assert(json_array_size(json) > index); - json->array.elements[index] = element; + if (json->storage_type == JSON_ARRAY_DYNAMIC) { + json->array.elements[index] = element; + } else { + json->elements[index] = element; + } } struct json * @@ -250,12 +288,33 @@ json_array_pop(struct json *json) { size_t n = json_array_size(json); - return n ? json->array.elements[--n] : NULL; + if (!n) { + return NULL; + } + if (json->storage_type == JSON_ARRAY_DYNAMIC) { + return json->array.elements[--n]; + } + if (json->storage_type > JSON_ARRAY_INLINE_1) { + return json->elements[--json->storage_type - JSON_ARRAY_DYNAMIC]; + } + + /* Need to fall back to an empty array in case it's the last + * inline element. */ + struct json *element = json->elements[0]; + json->storage_type = JSON_ARRAY_DYNAMIC; + json->array.elements = NULL; + json->array.size = 0; + json->array.allocated = 0; + return element; } void json_array_trim(struct json *array_) { + if (array_->storage_type != JSON_ARRAY_DYNAMIC) { + return; + } + struct json_array *array = &array_->array; if (array->size < array->allocated) { array->allocated = array->size; @@ -268,6 +327,7 @@ struct json * json_array_create(struct json **elements, size_t n) { struct json *json = json_create(JSON_ARRAY); + json->storage_type = JSON_ARRAY_DYNAMIC; json->array.elements = elements; json->array.size = n; json->array.allocated = n; @@ -277,28 +337,37 @@ json_array_create(struct json **elements, size_t n) struct json * json_array_create_1(struct json *elem0) { - struct json **elements = xmalloc(sizeof *elements); - elements[0] = elem0; - return json_array_create(elements, 1); + struct json *json = json_create(JSON_ARRAY); + + json->storage_type = JSON_ARRAY_INLINE_1; + json->elements[0] = elem0; + + return json; } struct json * json_array_create_2(struct json *elem0, struct json *elem1) { - struct json **elements = xmalloc(2 * sizeof *elements); - elements[0] = elem0; - elements[1] = elem1; - return json_array_create(elements, 2); + struct json *json = json_create(JSON_ARRAY); + + json->storage_type = JSON_ARRAY_INLINE_2; + json->elements[0] = elem0; + json->elements[1] = elem1; + + return json; } struct json * json_array_create_3(struct json *elem0, struct json *elem1, struct json *elem2) { - struct json **elements = xmalloc(3 * sizeof *elements); - elements[0] = elem0; - elements[1] = elem1; - elements[2] = elem2; - return json_array_create(elements, 3); + struct json *json = json_create(JSON_ARRAY); + + json->storage_type = JSON_ARRAY_INLINE_3; + json->elements[0] = elem0; + json->elements[1] = elem1; + json->elements[2] = elem2; + + return json; } bool @@ -390,14 +459,28 @@ size_t json_array_size(const struct json *json) { ovs_assert(json->type == JSON_ARRAY); - return json->array.size; + if (json->storage_type == JSON_ARRAY_DYNAMIC) { + return json->array.size; + } + return json->storage_type - JSON_ARRAY_DYNAMIC; } const struct json * json_array_at(const struct json *json, size_t index) { ovs_assert(json->type == JSON_ARRAY); - return (json->array.size > index) ? json->array.elements[index] : NULL; + + if (json->storage_type == JSON_ARRAY_DYNAMIC) { + if (json->array.size <= index) { + return NULL; + } + return json->array.elements[index]; + } + + if (json->storage_type - JSON_ARRAY_DYNAMIC <= index) { + return NULL; + } + return json->elements[index]; } struct shash * @@ -507,7 +590,9 @@ json_destroy_array(struct json *json, bool yield) json_destroy(CONST_CAST(struct json *, json_array_at(json, i))); } } - free(json->array.elements); + if (json->storage_type == JSON_ARRAY_DYNAMIC) { + free(json->array.elements); + } } static struct json *json_deep_clone_object(const struct shash *object);