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.
This commit is contained in:
Krzysztof Gabis 2016-12-29 23:50:20 +01:00
parent dcf85b88c8
commit f419334a32
3 changed files with 70 additions and 24 deletions

View File

@ -64,11 +64,13 @@ typedef union json_value_value {
} JSON_Value_Value; } JSON_Value_Value;
struct json_value_t { struct json_value_t {
JSON_Value_Type type; JSON_Value *parent;
JSON_Value_Value value; JSON_Value_Type type;
JSON_Value_Value value;
}; };
struct json_object_t { struct json_object_t {
JSON_Value *wrapping_value;
char **names; char **names;
JSON_Value **values; JSON_Value **values;
size_t count; size_t count;
@ -76,6 +78,7 @@ struct json_object_t {
}; };
struct json_array_t { struct json_array_t {
JSON_Value *wrapping_value;
JSON_Value **items; JSON_Value **items;
size_t count; size_t count;
size_t capacity; 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); static int is_decimal(const char *string, size_t length);
/* JSON Object */ /* 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_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_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 JSON_Value * json_object_nget_value(const JSON_Object *object, const char *name, size_t n);
static void json_object_free(JSON_Object *object); static void json_object_free(JSON_Object *object);
/* JSON Array */ /* 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_add(JSON_Array *array, JSON_Value *value);
static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity); static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity);
static void json_array_free(JSON_Array *array); 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] != '.') { if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') {
return 0; return 0;
} }
while (length--) while (length--) {
if (strchr("xX", string[length])) { if (strchr("xX", string[length])) {
return 0; return 0;
} }
}
return 1; return 1;
} }
@ -298,11 +302,12 @@ static void remove_comments(char *string, const char *start_token, const char *e
} }
/* JSON Object */ /* 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)); JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));
if (!new_obj) { if (new_obj == NULL) {
return NULL; return NULL;
} }
new_obj->wrapping_value = wrapping_value;
new_obj->names = (char**)NULL; new_obj->names = (char**)NULL;
new_obj->values = (JSON_Value**)NULL; new_obj->values = (JSON_Value**)NULL;
new_obj->capacity = 0; 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) { if (object->names[index] == NULL) {
return JSONFailure; return JSONFailure;
} }
value->parent = json_object_get_wrapping_value(object);
object->values[index] = value; object->values[index] = value;
object->count++; object->count++;
return JSONSuccess; return JSONSuccess;
@ -346,18 +352,15 @@ static JSON_Status json_object_resize(JSON_Object *object, size_t new_capacity)
new_capacity == 0) { new_capacity == 0) {
return JSONFailure; /* Shouldn't happen */ return JSONFailure; /* Shouldn't happen */
} }
temp_names = (char**)parson_malloc(new_capacity * sizeof(char*)); temp_names = (char**)parson_malloc(new_capacity * sizeof(char*));
if (temp_names == NULL) { if (temp_names == NULL) {
return JSONFailure; return JSONFailure;
} }
temp_values = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*)); temp_values = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*));
if (temp_values == NULL) { if (temp_values == NULL) {
parson_free(temp_names); parson_free(temp_names);
return JSONFailure; return JSONFailure;
} }
if (object->names != NULL && object->values != NULL && object->count > 0) { if (object->names != NULL && object->values != NULL && object->count > 0) {
memcpy(temp_names, object->names, object->count * sizeof(char*)); memcpy(temp_names, object->names, object->count * sizeof(char*));
memcpy(temp_values, object->values, object->count * sizeof(JSON_Value*)); memcpy(temp_values, object->values, object->count * sizeof(JSON_Value*));
@ -395,11 +398,12 @@ static void json_object_free(JSON_Object *object) {
} }
/* JSON Array */ /* 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)); JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array));
if (!new_array) { if (new_array == NULL) {
return NULL; return NULL;
} }
new_array->wrapping_value = wrapping_value;
new_array->items = (JSON_Value**)NULL; new_array->items = (JSON_Value**)NULL;
new_array->capacity = 0; new_array->capacity = 0;
new_array->count = 0; new_array->count = 0;
@ -416,6 +420,7 @@ static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) {
return JSONFailure; return JSONFailure;
} }
} }
value->parent = json_array_get_wrapping_value(array);
array->items[array->count] = value; array->items[array->count] = value;
array->count++; array->count++;
return JSONSuccess; 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) { static void json_array_free(JSON_Array *array) {
while (array->count--) while (array->count--) {
json_value_free(array->items[array->count]); json_value_free(array->items[array->count]);
}
parson_free(array->items); parson_free(array->items);
parson_free(array); parson_free(array);
} }
@ -452,6 +458,7 @@ static JSON_Value * json_value_init_string_no_copy(char *string) {
if (!new_value) { if (!new_value) {
return NULL; return NULL;
} }
new_value->parent = NULL;
new_value->type = JSONString; new_value->type = JSONString;
new_value->value.string = string; new_value->value.string = string;
return new_value; 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]; 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) { int json_object_has_value (const JSON_Object *object, const char *name) {
return json_object_get_value(object, name) != NULL; 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; 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 API */
JSON_Value_Type json_value_get_type(const JSON_Value *value) { JSON_Value_Type json_value_get_type(const JSON_Value *value) {
return value ? value->type : JSONError; 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; 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) { void json_value_free(JSON_Value *value) {
switch (json_value_get_type(value)) { switch (json_value_get_type(value)) {
case JSONObject: case JSONObject:
json_object_free(value->value.object); json_object_free(value->value.object);
break; break;
case JSONString: case JSONString:
if (value->value.string) { parson_free(value->value.string);
parson_free(value->value.string);
}
break; break;
case JSONArray: case JSONArray:
json_array_free(value->value.array); json_array_free(value->value.array);
@ -1200,8 +1217,9 @@ JSON_Value * json_value_init_object(void) {
if (!new_value) { if (!new_value) {
return NULL; return NULL;
} }
new_value->parent = NULL;
new_value->type = JSONObject; 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) { if (!new_value->value.object) {
parson_free(new_value); parson_free(new_value);
return NULL; return NULL;
@ -1214,8 +1232,9 @@ JSON_Value * json_value_init_array(void) {
if (!new_value) { if (!new_value) {
return NULL; return NULL;
} }
new_value->parent = NULL;
new_value->type = JSONArray; 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) { if (!new_value->value.array) {
parson_free(new_value); parson_free(new_value);
return NULL; return NULL;
@ -1250,6 +1269,7 @@ JSON_Value * json_value_init_number(double number) {
if (!new_value) { if (!new_value) {
return NULL; return NULL;
} }
new_value->parent = NULL;
new_value->type = JSONNumber; new_value->type = JSONNumber;
new_value->value.number = number; new_value->value.number = number;
return new_value; return new_value;
@ -1260,6 +1280,7 @@ JSON_Value * json_value_init_boolean(int boolean) {
if (!new_value) { if (!new_value) {
return NULL; return NULL;
} }
new_value->parent = NULL;
new_value->type = JSONBoolean; new_value->type = JSONBoolean;
new_value->value.boolean = boolean ? 1 : 0; new_value->value.boolean = boolean ? 1 : 0;
return new_value; return new_value;
@ -1270,6 +1291,7 @@ JSON_Value * json_value_init_null(void) {
if (!new_value) { if (!new_value) {
return NULL; return NULL;
} }
new_value->parent = NULL;
new_value->type = JSONNull; new_value->type = JSONNull;
return new_value; 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) { 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; return JSONFailure;
} }
json_value_free(json_array_get_value(array, ix)); json_value_free(json_array_get_value(array, ix));
value->parent = json_array_get_wrapping_value(array);
array->items[ix] = value; array->items[ix] = value;
return JSONSuccess; 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) { 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 JSONFailure;
} }
return json_array_add(array, value); 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) { JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) {
size_t i = 0; size_t i = 0;
JSON_Value *old_value; JSON_Value *old_value;
if (object == NULL || name == NULL || value == NULL) { if (object == NULL || name == NULL || value == NULL || value->parent != NULL) {
return JSONFailure; return JSONFailure;
} }
old_value = json_object_get_value(object, name); 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); json_value_free(old_value);
for (i = 0; i < json_object_get_count(object); i++) { for (i = 0; i < json_object_get_count(object); i++) {
if (strcmp(object->names[i], name) == 0) { if (strcmp(object->names[i], name) == 0) {
value->parent = json_object_get_wrapping_value(object);
object->values[i] = value; object->values[i] = value;
return JSONSuccess; return JSONSuccess;
} }

View File

@ -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); size_t json_object_get_count (const JSON_Object *object);
const char * json_object_get_name (const JSON_Object *object, size_t index); 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_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 /* 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. */ * 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 */ 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 */ 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); 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. /* 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. */ * Order of values in array may change during execution. */
JSON_Status json_array_remove(JSON_Array *array, size_t i); 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); const char * json_value_get_string (const JSON_Value *value);
double json_value_get_number (const JSON_Value *value); double json_value_get_number (const JSON_Value *value);
int json_value_get_boolean(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 */ /* Same as above, but shorter */
JSON_Value_Type json_type (const JSON_Value *value); JSON_Value_Type json_type (const JSON_Value *value);

23
tests.c
View File

@ -115,6 +115,7 @@ void test_suite_1(void) {
void test_suite_2(JSON_Value *root_value) { void test_suite_2(JSON_Value *root_value) {
JSON_Object *root_object; JSON_Object *root_object;
JSON_Array *array; JSON_Array *array;
JSON_Value *array_value;
size_t i; size_t i;
TEST(root_value); TEST(root_value);
TEST(json_value_get_type(root_value) == JSONObject); 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_object(root_object, "empty object") != NULL);
TEST(json_object_get_array(root_object, "empty array") != 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) { void test_suite_2_no_comments(void) {
@ -300,7 +307,7 @@ void test_suite_4() {
void test_suite_5(void) { void test_suite_5(void) {
JSON_Value *val_from_file = json_parse_file("tests/test_5.txt"); 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_Object *obj = NULL;
JSON_Array *interests_arr = 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) == JSONSuccess);
TEST(json_array_remove(interests_arr, 0) == JSONFailure); /* should be empty by now */ 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); TEST(json_object_remove(obj, "interests") == JSONSuccess);
/* UTF-8 tests */ /* UTF-8 tests */