Skip to content

Commit

Permalink
Add no copy api variants to json interface (#1138)
Browse files Browse the repository at this point in the history
  • Loading branch information
DmitriyMusatkin authored Jul 30, 2024
1 parent c9ead75 commit 0a98aa0
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 49 deletions.
69 changes: 68 additions & 1 deletion include/aws/common/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,26 @@ AWS_EXTERN_C_BEGIN
*
* Note: You will need to free the memory for the aws_json_value using aws_json_destroy on the aws_json_value or
* on the object/array containing the aws_json_value.
* @param string A byte pointer to the string you want to store in the aws_json_value
* Note: might be slower than c_str version due to internal copy
* @param string A byte cursor you want to store in the aws_json_value
* @param allocator The allocator to use when creating the value
* @return A new string aws_json_value
*/
AWS_COMMON_API
struct aws_json_value *aws_json_value_new_string(struct aws_allocator *allocator, struct aws_byte_cursor string);

/**
* Creates a new string aws_json_value with the given string and returns a pointer to it.
*
* Note: You will need to free the memory for the aws_json_value using aws_json_destroy on the aws_json_value or
* on the object/array containing the aws_json_value.
* @param string c string pointer you want to store in the aws_json_value
* @param allocator The allocator to use when creating the value
* @return A new string aws_json_value
*/
AWS_COMMON_API
struct aws_json_value *aws_json_value_new_string_from_c_str(struct aws_allocator *allocator, const char *string);

/**
* Creates a new number aws_json_value with the given number and returns a pointer to it.
*
Expand Down Expand Up @@ -129,6 +142,7 @@ int aws_json_value_get_boolean(const struct aws_json_value *value, bool *output)
*
* Note that the aws_json_value will be destroyed when the aws_json_value object is destroyed
* by calling "aws_json_destroy()"
* Note: might be slower than c_str version due to internal copy
* @param object The object aws_json_value you want to add a value to.
* @param key The key to add the aws_json_value at.
* @param value The aws_json_value you want to add.
Expand All @@ -142,26 +156,66 @@ int aws_json_value_add_to_object(
struct aws_byte_cursor key,
struct aws_json_value *value);

/**
* Adds a aws_json_value to a object aws_json_value.
*
* Note that the aws_json_value will be destroyed when the aws_json_value object is destroyed
* by calling "aws_json_destroy()"
* @param object The object aws_json_value you want to add a value to.
* @param key The key to add the aws_json_value at.
* @param value The aws_json_value you want to add.
* @return AWS_OP_SUCCESS if adding was successful.
* Will return AWS_OP_ERROR if the object passed is invalid or if the passed key
* is already in use in the object.
*/
AWS_COMMON_API
int aws_json_value_add_to_object_c_str(struct aws_json_value *object, const char *key, struct aws_json_value *value);

/**
* Returns the aws_json_value at the given key.
* Note: might be slower than c_str version due to internal copy
* @param object The object aws_json_value you want to get the value from.
* @param key The key that the aws_json_value is at. Is case sensitive.
* @return The aws_json_value at the given key, otherwise NULL.
*/
AWS_COMMON_API
struct aws_json_value *aws_json_value_get_from_object(const struct aws_json_value *object, struct aws_byte_cursor key);

/**
* Returns the aws_json_value at the given key.
* Note: same as aws_json_value_get_from_object but with key as const char *.
* Prefer this method is you have a key thats already a valid char * as it is likely to be faster.
* @param object The object aws_json_value you want to get the value from.
* @param key The key that the aws_json_value is at. Is case sensitive.
* @return The aws_json_value at the given key, otherwise NULL.
*/
AWS_COMMON_API
struct aws_json_value *aws_json_value_get_from_object_c_str(const struct aws_json_value *object, const char *key);

/**
* Checks if there is a aws_json_value at the given key.
* Note: might be slower than c_str version due to internal copy
* @param object The value aws_json_value you want to check a key in.
* @param key The key that you want to check. Is case sensitive.
* @return True if a aws_json_value is found.
*/
AWS_COMMON_API
bool aws_json_value_has_key(const struct aws_json_value *object, struct aws_byte_cursor key);

/**
* Checks if there is a aws_json_value at the given key.
* Note: same as aws_json_value_has_key but with key as const char *.
* Prefer this method is you have a key thats already a valid char * as it is likely to be faster.
* @param object The value aws_json_value you want to check a key in.
* @param key The key that you want to check. Is case sensitive.
* @return True if a aws_json_value is found.
*/
AWS_COMMON_API
bool aws_json_value_has_key_c_str(const struct aws_json_value *object, const char *key);

/**
* Removes the aws_json_value at the given key.
* Note: might be slower than c_str version due to internal copy
* @param object The object aws_json_value you want to remove a aws_json_value in.
* @param key The key that the aws_json_value is at. Is case sensitive.
* @return AWS_OP_SUCCESS if the aws_json_value was removed.
Expand All @@ -171,6 +225,19 @@ bool aws_json_value_has_key(const struct aws_json_value *object, struct aws_byte
AWS_COMMON_API
int aws_json_value_remove_from_object(struct aws_json_value *object, struct aws_byte_cursor key);

/**
* Removes the aws_json_value at the given key.
* Note: same as aws_json_value_remove_from_object but with key as const char *.
* Prefer this method is you have a key thats already a valid char * as it is likely to be faster.
* @param object The object aws_json_value you want to remove a aws_json_value in.
* @param key The key that the aws_json_value is at. Is case sensitive.
* @return AWS_OP_SUCCESS if the aws_json_value was removed.
* Will return AWS_OP_ERR if the object passed is invalid or if the value
* at the key cannot be found.
*/
AWS_COMMON_API
int aws_json_value_remove_from_object_c_str(struct aws_json_value *object, const char *key);

/**
* @brief callback for iterating members of an object
* Iteration can be controlled as follows:
Expand Down
91 changes: 50 additions & 41 deletions source/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ struct aws_json_value *aws_json_value_new_string(struct aws_allocator *allocator
return ret_val;
}

struct aws_json_value *aws_json_value_new_string_from_c_str(struct aws_allocator *allocator, const char *string) {
(void)allocator; /* No need for allocator. It is overriden through hooks. */
void *ret_val = cJSON_CreateString(string);
return ret_val;
}

struct aws_json_value *aws_json_value_new_number(struct aws_allocator *allocator, double number) {
(void)allocator; // prevent warnings over unused parameter
return (void *)cJSON_CreateNumber(number);
Expand Down Expand Up @@ -78,92 +84,95 @@ int aws_json_value_add_to_object(
struct aws_byte_cursor key,
struct aws_json_value *value) {

int result = AWS_OP_ERR;
struct aws_string *tmp = aws_string_new_from_cursor(s_aws_json_module_allocator, &key);
int result = aws_json_value_add_to_object_c_str(object, aws_string_c_str(tmp), value);

aws_string_destroy_secure(tmp);
return result;
}

int aws_json_value_add_to_object_c_str(struct aws_json_value *object, const char *key, struct aws_json_value *value) {

struct cJSON *cjson = (struct cJSON *)object;
if (!cJSON_IsObject(cjson)) {
aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
goto done;
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}

struct cJSON *cjson_value = (struct cJSON *)value;
if (cJSON_IsInvalid(cjson_value)) {
result = aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
goto done;
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}
if (cJSON_HasObjectItem(cjson, aws_string_c_str(tmp))) {
goto done;
if (cJSON_HasObjectItem(cjson, key)) {
return AWS_OP_ERR;
}

cJSON_AddItemToObject(cjson, aws_string_c_str(tmp), cjson_value);
result = AWS_OP_SUCCESS;

done:
aws_string_destroy_secure(tmp);
return result;
cJSON_AddItemToObject(cjson, key, cjson_value);
return AWS_OP_SUCCESS;
}

struct aws_json_value *aws_json_value_get_from_object(const struct aws_json_value *object, struct aws_byte_cursor key) {

void *return_value = NULL;
struct aws_string *tmp = aws_string_new_from_cursor(s_aws_json_module_allocator, &key);
void *return_value = aws_json_value_get_from_object_c_str(object, aws_string_c_str(tmp));

aws_string_destroy_secure(tmp);
return return_value;
}

struct aws_json_value *aws_json_value_get_from_object_c_str(const struct aws_json_value *object, const char *key) {
const struct cJSON *cjson = (const struct cJSON *)object;
if (!cJSON_IsObject(cjson)) {
aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
goto done;
return NULL;
}
if (!cJSON_HasObjectItem(cjson, aws_string_c_str(tmp))) {
goto done;
if (!cJSON_HasObjectItem(cjson, key)) {
return NULL;
}

return_value = (void *)cJSON_GetObjectItem(cjson, aws_string_c_str(tmp));

done:
aws_string_destroy_secure(tmp);
return return_value;
return (void *)cJSON_GetObjectItem(cjson, key);
}

bool aws_json_value_has_key(const struct aws_json_value *object, struct aws_byte_cursor key) {

struct aws_string *tmp = aws_string_new_from_cursor(s_aws_json_module_allocator, &key);
bool result = false;
bool result = aws_json_value_has_key_c_str(object, aws_string_c_str(tmp));

aws_string_destroy_secure(tmp);
return result;
}

bool aws_json_value_has_key_c_str(const struct aws_json_value *object, const char *key) {
const struct cJSON *cjson = (const struct cJSON *)object;
if (!cJSON_IsObject(cjson)) {
goto done;
return false;
}
if (!cJSON_HasObjectItem(cjson, aws_string_c_str(tmp))) {
goto done;
if (!cJSON_HasObjectItem(cjson, key)) {
return false;
}
result = true;

done:
aws_string_destroy_secure(tmp);
return result;
return true;
}

int aws_json_value_remove_from_object(struct aws_json_value *object, struct aws_byte_cursor key) {

int result = AWS_OP_ERR;
struct aws_string *tmp = aws_string_new_from_cursor(s_aws_json_module_allocator, &key);
int result = aws_json_value_remove_from_object_c_str(object, aws_string_c_str(tmp));

aws_string_destroy_secure(tmp);
return result;
}

int aws_json_value_remove_from_object_c_str(struct aws_json_value *object, const char *key) {
struct cJSON *cjson = (struct cJSON *)object;
if (!cJSON_IsObject(cjson)) {
aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
goto done;
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}
if (!cJSON_HasObjectItem(cjson, aws_string_c_str(tmp))) {
goto done;
if (!cJSON_HasObjectItem(cjson, key)) {
return AWS_OP_ERR;
}

cJSON_DeleteItemFromObject(cjson, aws_string_c_str(tmp));
result = AWS_OP_SUCCESS;

done:
aws_string_destroy_secure(tmp);
return result;
cJSON_DeleteItemFromObject(cjson, key);
return AWS_OP_SUCCESS;
}

int aws_json_const_iterate_object(
Expand Down
19 changes: 12 additions & 7 deletions tests/json_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ static int s_test_json_parse_from_string(struct aws_allocator *allocator, void *

// Testing valid array
struct aws_json_value *array_node = aws_json_value_get_from_object(root, aws_byte_cursor_from_c_str("array"));
ASSERT_PTR_EQUALS(array_node, aws_json_value_get_from_object_c_str(root, "array"));
ASSERT_NOT_NULL(array_node);
ASSERT_TRUE(aws_json_value_is_array(array_node));
ASSERT_TRUE(aws_json_get_array_size(array_node) == 3);
Expand Down Expand Up @@ -163,12 +164,14 @@ static int s_test_json_parse_from_string(struct aws_allocator *allocator, void *
aws_string_destroy_secure(tmp_str);

// Testing valid number
struct aws_json_value *number_node = aws_json_value_get_from_object(root, aws_byte_cursor_from_c_str("number"));
struct aws_json_value *number_node = aws_json_value_get_from_object_c_str(root, "number");
ASSERT_NOT_NULL(number_node);
ASSERT_TRUE(aws_json_value_is_number(number_node));
double double_test_two = 0;
aws_json_value_get_number(number_node, &double_test_two);
ASSERT_TRUE(double_test_two == (double)123);
ASSERT_TRUE(aws_json_value_has_key_c_str(root, "number"));
ASSERT_TRUE(aws_json_value_has_key(root, aws_byte_cursor_from_c_str("number")));

// Testing valid object
struct aws_json_value *object_node = aws_json_value_get_from_object(root, aws_byte_cursor_from_c_str("object"));
Expand Down Expand Up @@ -210,6 +213,12 @@ static int s_test_json_parse_from_string(struct aws_allocator *allocator, void *
// Test getting invalid type of data
ASSERT_INT_EQUALS(aws_json_value_get_number(string_node, NULL), AWS_OP_ERR);

ASSERT_SUCCESS(aws_json_value_remove_from_object(root, aws_byte_cursor_from_c_str("number")));
ASSERT_FALSE(aws_json_value_has_key_c_str(root, "number"));

ASSERT_SUCCESS(aws_json_value_remove_from_object_c_str(root, "object"));
ASSERT_FALSE(aws_json_value_has_key_c_str(root, "object"));

aws_json_value_destroy(root);

// Make sure that destroying NULL does not have any bad effects.
Expand All @@ -233,12 +242,8 @@ static int s_test_json_parse_to_string(struct aws_allocator *allocator, void *ct
aws_json_value_add_array_element(array, aws_json_value_new_number(allocator, 3));
aws_json_value_add_to_object(root, aws_byte_cursor_from_c_str("array"), array);

aws_json_value_add_to_object(
root, aws_byte_cursor_from_c_str("boolean"), aws_json_value_new_boolean(allocator, true));
aws_json_value_add_to_object(
root,
aws_byte_cursor_from_c_str("color"),
aws_json_value_new_string(allocator, aws_byte_cursor_from_c_str("gold")));
aws_json_value_add_to_object_c_str(root, "boolean", aws_json_value_new_boolean(allocator, true));
aws_json_value_add_to_object_c_str(root, "color", aws_json_value_new_string_from_c_str(allocator, "gold"));
aws_json_value_add_to_object(root, aws_byte_cursor_from_c_str("null"), aws_json_value_new_null(allocator));
aws_json_value_add_to_object(root, aws_byte_cursor_from_c_str("number"), aws_json_value_new_number(allocator, 123));

Expand Down

0 comments on commit 0a98aa0

Please sign in to comment.