diff --git a/include/aws/common/private/xml_parser_impl.h b/include/aws/common/private/xml_parser_impl.h index eea061b1e..d9763e88e 100644 --- a/include/aws/common/private/xml_parser_impl.h +++ b/include/aws/common/private/xml_parser_impl.h @@ -14,6 +14,7 @@ struct aws_xml_node { struct aws_array_list attributes; struct aws_byte_cursor doc_at_body; bool processed; + bool is_empty; }; struct aws_xml_parser { diff --git a/include/aws/common/ref_count.h b/include/aws/common/ref_count.h index 5166eb3f3..43fbde5e5 100644 --- a/include/aws/common/ref_count.h +++ b/include/aws/common/ref_count.h @@ -8,11 +8,10 @@ #include #include +#include AWS_PUSH_SANE_WARNING_LEVEL -typedef void(aws_simple_completion_callback)(void *); - /* * A utility type for making ref-counted types, reminiscent of std::shared_ptr in C++ */ @@ -22,11 +21,6 @@ struct aws_ref_count { aws_simple_completion_callback *on_zero_fn; }; -struct aws_shutdown_callback_options { - aws_simple_completion_callback *shutdown_callback_fn; - void *shutdown_callback_user_data; -}; - AWS_EXTERN_C_BEGIN /** diff --git a/include/aws/common/shutdown_types.h b/include/aws/common/shutdown_types.h new file mode 100644 index 000000000..4ade53a0c --- /dev/null +++ b/include/aws/common/shutdown_types.h @@ -0,0 +1,34 @@ +#ifndef AWS_COMMON_SHUTDOWN_TYPES_H +#define AWS_COMMON_SHUTDOWN_TYPES_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +AWS_PUSH_SANE_WARNING_LEVEL + +typedef void(aws_simple_completion_callback)(void *); + +/** + * Configuration for a callback to invoke when something has been completely + * cleaned up. Primarily used in async cleanup control flows. + */ +struct aws_shutdown_callback_options { + + /** + * Function to invoke when the associated object is fully destroyed. + */ + aws_simple_completion_callback *shutdown_callback_fn; + + /** + * User data to invoke the shutdown callback with. + */ + void *shutdown_callback_user_data; +}; + +AWS_POP_SANE_WARNING_LEVEL + +#endif /* AWS_COMMON_SHUTDOWN_TYPES_H */ diff --git a/source/xml_parser.c b/source/xml_parser.c index e1b580740..7675bff30 100644 --- a/source/xml_parser.c +++ b/source/xml_parser.c @@ -40,6 +40,8 @@ static int s_load_node_decl( AWS_PRECONDITION(decl_body); AWS_PRECONDITION(node); + node->is_empty = decl_body->ptr[decl_body->len - 1] == '/'; + struct aws_array_list splits; AWS_ZERO_STRUCT(splits); @@ -158,6 +160,14 @@ int s_advance_to_closing_tag( AWS_PRECONDITION(parser); AWS_PRECONDITION(node); + if (node->is_empty) { + if (out_body) { + out_body->ptr = NULL; + out_body->len = 0; + } + return AWS_OP_SUCCESS; + } + /* currently the max node name is 256 characters. This is arbitrary, but should be enough * for our uses. If we ever generalize this, we'll have to come back and rethink this. */ uint8_t name_close[MAX_NAME_LEN + NODE_CLOSE_OVERHEAD] = {0}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d3556c5f7..36feaa367 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -467,6 +467,7 @@ add_test_case(xml_parser_nested_node_same_name_test) add_test_case(xml_parser_nested_node_deep_recursion_test) add_test_case(xml_parser_too_many_attributes_test) add_test_case(xml_parser_name_too_long_test) +add_test_case(xml_parser_child_empty_tag) add_test_case(test_thread_scheduler_ordering) add_test_case(test_thread_scheduler_happy_path_cancellation) diff --git a/tests/xml_parser_test.c b/tests/xml_parser_test.c index 9a3b33041..06d01107d 100644 --- a/tests/xml_parser_test.c +++ b/tests/xml_parser_test.c @@ -165,6 +165,42 @@ static int s_xml_parser_siblings_with_text_test(struct aws_allocator *allocator, AWS_TEST_CASE(xml_parser_siblings_with_text, s_xml_parser_siblings_with_text_test) +const char *siblings_with_empty_tag = "TestBody2"; + +static int s_xml_parser_child_empty_tag(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + struct sibling_text_capture capture; + AWS_ZERO_STRUCT(capture); + + capture.capture1 = aws_byte_cursor_from_c_str("random values"); + + struct aws_xml_parser_options options = { + .doc = aws_byte_cursor_from_c_str(siblings_with_empty_tag), + .on_root_encountered = s_root_with_child_siblings, + .user_data = &capture, + }; + ASSERT_SUCCESS(aws_xml_parse(allocator, &options)); + + const char expected_name1[] = "child1"; + + const char expected_name2[] = "child2"; + const char expected_value2[] = "TestBody2"; + + ASSERT_BIN_ARRAYS_EQUALS( + expected_name1, sizeof(expected_name1) - 1, capture.node_name1.ptr, capture.node_name1.len); + ASSERT_PTR_EQUALS(capture.capture1.ptr, NULL); + ASSERT_UINT_EQUALS(capture.capture1.len, 0); + + ASSERT_BIN_ARRAYS_EQUALS( + expected_name2, sizeof(expected_name2) - 1, capture.node_name2.ptr, capture.node_name2.len); + ASSERT_BIN_ARRAYS_EQUALS(expected_value2, sizeof(expected_value2) - 1, capture.capture2.ptr, capture.capture2.len); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(xml_parser_child_empty_tag, s_xml_parser_child_empty_tag) + const char *preamble_and_attributes = "\n" "