From f419334a32a728a7d1457bcd0ea758437628a68e Mon Sep 17 00:00:00 2001 From: Krzysztof Gabis Date: Thu, 29 Dec 2016 23:50:20 +0100 Subject: [PATCH] Adds links to parent values and values used to wrap objects/arrays. Assigning a value to 2 objects/arrays returns an error now. Addresses issues #66 and #30. --- parson.c | 66 ++++++++++++++++++++++++++++++++++++++------------------ parson.h | 5 ++++- tests.c | 23 ++++++++++++++++++-- 3 files changed, 70 insertions(+), 24 deletions(-) diff --git a/parson.c b/parson.c index 70fbde4..0d87d6e 100644 --- a/parson.c +++ b/parson.c @@ -64,11 +64,13 @@ typedef union json_value_value { } JSON_Value_Value; struct json_value_t { - JSON_Value_Type type; - JSON_Value_Value value; + JSON_Value *parent; + JSON_Value_Type type; + JSON_Value_Value value; }; struct json_object_t { + JSON_Value *wrapping_value; char **names; JSON_Value **values; size_t count; @@ -76,6 +78,7 @@ struct json_object_t { }; struct json_array_t { + JSON_Value *wrapping_value; JSON_Value **items; size_t count; size_t capacity; @@ -93,14 +96,14 @@ static int is_valid_utf8(const char *string, size_t string_len); static int is_decimal(const char *string, size_t length); /* JSON Object */ -static JSON_Object * json_object_init(void); +static JSON_Object * json_object_init(JSON_Value *wrapping_value); static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_Value *value); static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity); static JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n); static void json_object_free(JSON_Object *object); /* JSON Array */ -static JSON_Array * json_array_init(void); +static JSON_Array * json_array_init(JSON_Value *wrapping_value); static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value); static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity); static void json_array_free(JSON_Array *array); @@ -222,10 +225,11 @@ static int is_decimal(const char *string, size_t length) { if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') { return 0; } - while (length--) + while (length--) { if (strchr("xX", string[length])) { return 0; } + } return 1; } @@ -298,11 +302,12 @@ static void remove_comments(char *string, const char *start_token, const char *e } /* JSON Object */ -static JSON_Object * json_object_init(void) { +static JSON_Object * json_object_init(JSON_Value *wrapping_value) { JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object)); - if (!new_obj) { + if (new_obj == NULL) { return NULL; } + new_obj->wrapping_value = wrapping_value; new_obj->names = (char**)NULL; new_obj->values = (JSON_Value**)NULL; new_obj->capacity = 0; @@ -332,6 +337,7 @@ static JSON_Status json_object_add(JSON_Object *object, const char *name, JSON_V if (object->names[index] == NULL) { return JSONFailure; } + value->parent = json_object_get_wrapping_value(object); object->values[index] = value; object->count++; return JSONSuccess; @@ -346,18 +352,15 @@ static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity) new_capacity == 0) { return JSONFailure; /* Shouldn't happen */ } - temp_names = (char**)parson_malloc(new_capacity * sizeof(char*)); if (temp_names == NULL) { return JSONFailure; } - temp_values = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*)); if (temp_values == NULL) { parson_free(temp_names); return JSONFailure; } - if (object->names != NULL && object->values != NULL && object->count > 0) { memcpy(temp_names, object->names, object->count * sizeof(char*)); memcpy(temp_values, object->values, object->count * sizeof(JSON_Value*)); @@ -395,11 +398,12 @@ static void json_object_free(JSON_Object *object) { } /* JSON Array */ -static JSON_Array * json_array_init(void) { +static JSON_Array * json_array_init(JSON_Value *wrapping_value) { JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array)); - if (!new_array) { + if (new_array == NULL) { return NULL; } + new_array->wrapping_value = wrapping_value; new_array->items = (JSON_Value**)NULL; new_array->capacity = 0; new_array->count = 0; @@ -416,6 +420,7 @@ static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) { return JSONFailure; } } + value->parent = json_array_get_wrapping_value(array); array->items[array->count] = value; array->count++; return JSONSuccess; @@ -440,8 +445,9 @@ static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) { } static void json_array_free(JSON_Array *array) { - while (array->count--) + while (array->count--) { json_value_free(array->items[array->count]); + } parson_free(array->items); parson_free(array); } @@ -452,6 +458,7 @@ static JSON_Value * json_value_init_string_no_copy(char *string) { if (!new_value) { return NULL; } + new_value->parent = NULL; new_value->type = JSONString; new_value->value.string = string; return new_value; @@ -1101,6 +1108,10 @@ JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index) { return object->values[index]; } +JSON_Value *json_object_get_wrapping_value(const JSON_Object *object) { + return object->wrapping_value; +} + int json_object_has_value (const JSON_Object *object, const char *name) { return json_object_get_value(object, name) != NULL; } @@ -1151,6 +1162,10 @@ size_t json_array_get_count(const JSON_Array *array) { return array ? array->count : 0; } +JSON_Value * json_array_get_wrapping_value(const JSON_Array *array) { + return array->wrapping_value; +} + /* JSON Value API */ JSON_Value_Type json_value_get_type(const JSON_Value *value) { return value ? value->type : JSONError; @@ -1176,15 +1191,17 @@ int json_value_get_boolean(const JSON_Value *value) { return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1; } +JSON_Value * json_value_get_parent (const JSON_Value *value) { + return value ? value->parent : NULL; +} + void json_value_free(JSON_Value *value) { switch (json_value_get_type(value)) { case JSONObject: json_object_free(value->value.object); break; case JSONString: - if (value->value.string) { - parson_free(value->value.string); - } + parson_free(value->value.string); break; case JSONArray: json_array_free(value->value.array); @@ -1200,8 +1217,9 @@ JSON_Value * json_value_init_object(void) { if (!new_value) { return NULL; } + new_value->parent = NULL; new_value->type = JSONObject; - new_value->value.object = json_object_init(); + new_value->value.object = json_object_init(new_value); if (!new_value->value.object) { parson_free(new_value); return NULL; @@ -1214,8 +1232,9 @@ JSON_Value * json_value_init_array(void) { if (!new_value) { return NULL; } + new_value->parent = NULL; new_value->type = JSONArray; - new_value->value.array = json_array_init(); + new_value->value.array = json_array_init(new_value); if (!new_value->value.array) { parson_free(new_value); return NULL; @@ -1250,6 +1269,7 @@ JSON_Value * json_value_init_number(double number) { if (!new_value) { return NULL; } + new_value->parent = NULL; new_value->type = JSONNumber; new_value->value.number = number; return new_value; @@ -1260,6 +1280,7 @@ JSON_Value * json_value_init_boolean(int boolean) { if (!new_value) { return NULL; } + new_value->parent = NULL; new_value->type = JSONBoolean; new_value->value.boolean = boolean ? 1 : 0; return new_value; @@ -1270,6 +1291,7 @@ JSON_Value * json_value_init_null(void) { if (!new_value) { return NULL; } + new_value->parent = NULL; new_value->type = JSONNull; return new_value; } @@ -1494,10 +1516,11 @@ JSON_Status json_array_remove(JSON_Array *array, size_t ix) { } JSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) { - if (array == NULL || value == NULL || ix >= json_array_get_count(array)) { + if (array == NULL || value == NULL || value->parent != NULL || ix >= json_array_get_count(array)) { return JSONFailure; } json_value_free(json_array_get_value(array, ix)); + value->parent = json_array_get_wrapping_value(array); array->items[ix] = value; return JSONSuccess; } @@ -1563,7 +1586,7 @@ JSON_Status json_array_clear(JSON_Array *array) { } JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) { - if (array == NULL || value == NULL) { + if (array == NULL || value == NULL || value->parent != NULL) { return JSONFailure; } return json_array_add(array, value); @@ -1620,7 +1643,7 @@ JSON_Status json_array_append_null(JSON_Array *array) { JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) { size_t i = 0; JSON_Value *old_value; - if (object == NULL || name == NULL || value == NULL) { + if (object == NULL || name == NULL || value == NULL || value->parent != NULL) { return JSONFailure; } old_value = json_object_get_value(object, name); @@ -1628,6 +1651,7 @@ JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Va json_value_free(old_value); for (i = 0; i < json_object_get_count(object); i++) { if (strcmp(object->names[i], name) == 0) { + value->parent = json_object_get_wrapping_value(object); object->values[i] = value; return JSONSuccess; } diff --git a/parson.h b/parson.h index 6a89982..e4cd21e 100644 --- a/parson.h +++ b/parson.h @@ -128,6 +128,7 @@ int json_object_dotget_boolean(const JSON_Object *object, const char * size_t json_object_get_count (const JSON_Object *object); const char * json_object_get_name (const JSON_Object *object, size_t index); JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index); +JSON_Value * json_object_get_wrapping_value(const JSON_Object *object); /* Functions to check if object has a value with a specific name. Returned value is 1 if object has * a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */ @@ -172,7 +173,8 @@ JSON_Array * json_array_get_array (const JSON_Array *array, size_t index); double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */ int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */ size_t json_array_get_count (const JSON_Array *array); - +JSON_Value * json_array_get_wrapping_value(const JSON_Array *array); + /* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist. * Order of values in array may change during execution. */ JSON_Status json_array_remove(JSON_Array *array, size_t i); @@ -215,6 +217,7 @@ JSON_Array * json_value_get_array (const JSON_Value *value); const char * json_value_get_string (const JSON_Value *value); double json_value_get_number (const JSON_Value *value); int json_value_get_boolean(const JSON_Value *value); +JSON_Value * json_value_get_parent (const JSON_Value *value); /* Same as above, but shorter */ JSON_Value_Type json_type (const JSON_Value *value); diff --git a/tests.c b/tests.c index 819711d..0bdd834 100644 --- a/tests.c +++ b/tests.c @@ -115,6 +115,7 @@ void test_suite_1(void) { void test_suite_2(JSON_Value *root_value) { JSON_Object *root_object; JSON_Array *array; + JSON_Value *array_value; size_t i; TEST(root_value); TEST(json_value_get_type(root_value) == JSONObject); @@ -207,7 +208,13 @@ void test_suite_2(JSON_Value *root_value) { TEST(json_object_get_object(root_object, "empty object") != NULL); TEST(json_object_get_array(root_object, "empty array") != NULL); - + + TEST(json_object_get_wrapping_value(root_object) == root_value); + array = json_object_get_array(root_object, "string array"); + array_value = json_object_get_value(root_object, "string array"); + TEST(json_array_get_wrapping_value(array) == array_value); + TEST(json_value_get_parent(array_value) == root_value); + TEST(json_value_get_parent(root_value) == NULL); } void test_suite_2_no_comments(void) { @@ -300,7 +307,7 @@ void test_suite_4() { void test_suite_5(void) { JSON_Value *val_from_file = json_parse_file("tests/test_5.txt"); - JSON_Value *val = NULL; + JSON_Value *val = NULL, *val_parent; JSON_Object *obj = NULL; JSON_Array *interests_arr = NULL; @@ -359,6 +366,18 @@ void test_suite_5(void) { TEST(json_array_remove(interests_arr, 0) == JSONSuccess); TEST(json_array_remove(interests_arr, 0) == JSONFailure); /* should be empty by now */ + val_parent = json_value_init_null(); + TEST(json_object_set_value(obj, "x", val_parent) == JSONSuccess); + TEST(json_object_set_value(obj, "x", val_parent) == JSONFailure); + + val_parent = json_value_init_null(); + TEST(json_array_append_value(interests_arr, val_parent) == JSONSuccess); + TEST(json_array_append_value(interests_arr, val_parent) == JSONFailure); + + val_parent = json_value_init_null(); + TEST(json_array_replace_value(interests_arr, 0, val_parent) == JSONSuccess); + TEST(json_array_replace_value(interests_arr, 0, val_parent) == JSONFailure); + TEST(json_object_remove(obj, "interests") == JSONSuccess); /* UTF-8 tests */