From bb962b9732e7e0917d60cae63a5dccd25d1cc89a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20R=C3=B8nningstad?= Date: Thu, 25 Jul 2024 15:56:27 +0200 Subject: [PATCH] zcbor.py: Fixes for deeply nested types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + tests Signed-off-by: Øyvind Rønningstad --- tests/cases/corner_cases.cddl | 6 + .../decode/test5_corner_cases/CMakeLists.txt | 5 + tests/decode/test5_corner_cases/src/main.c | 131 ++++++++++++++++ .../encode/test3_corner_cases/CMakeLists.txt | 5 + tests/encode/test3_corner_cases/src/main.c | 145 ++++++++++++++++++ zcbor/zcbor.py | 26 ++-- 6 files changed, 307 insertions(+), 11 deletions(-) diff --git a/tests/cases/corner_cases.cddl b/tests/cases/corner_cases.cddl index 8cfd78ab..56b800c8 100644 --- a/tests/cases/corner_cases.cddl +++ b/tests/cases/corner_cases.cddl @@ -361,3 +361,9 @@ SingleElemList = [ [true], {1: bstr}, ] + +Choice1 = nil / [ * [tstr] ] +Choice2 = nil / ([ * [tstr] ]) +Choice3 = nil / ([ * ([tstr])/nil ]) +Choice4 = nil / ([ * ([[Choice3]])/nil ]) +Choice5 = nil / ([ * {*int=>[tstr]}/nil ]) diff --git a/tests/decode/test5_corner_cases/CMakeLists.txt b/tests/decode/test5_corner_cases/CMakeLists.txt index 3f615e71..c8d88be5 100644 --- a/tests/decode/test5_corner_cases/CMakeLists.txt +++ b/tests/decode/test5_corner_cases/CMakeLists.txt @@ -57,6 +57,11 @@ set(py_command Keywords EmptyContainer SingleElemList + Choice1 + Choice2 + Choice3 + Choice4 + Choice5 --decode --git-sha-header --short-names diff --git a/tests/decode/test5_corner_cases/src/main.c b/tests/decode/test5_corner_cases/src/main.c index e37257a0..a1975061 100644 --- a/tests/decode/test5_corner_cases/src/main.c +++ b/tests/decode/test5_corner_cases/src/main.c @@ -11,6 +11,12 @@ #include #include +static void zassert_zcbor_string(struct zcbor_string *str, uint8_t *expected, size_t len) +{ + zassert_equal(len, str->len, "exp: %s\n", expected); + zassert_mem_equal(expected, str->value, len, "exp: %s\n", expected); +} + ZTEST(cbor_decode_test5, test_numbers) { @@ -2256,4 +2262,129 @@ ZTEST(cbor_decode_test5, test_single_elem_list) } +ZTEST(cbor_decode_test5, test_nested_choices) +{ + uint8_t nested_choices_payload1[] = {0xf6}; + uint8_t nested_choices_payload2[] = {LIST(2), + LIST(1), 0x65, 'h', 'e', 'l', 'l', 'o', END + LIST(1), 0x65, 'w', 'o', 'r', 'l', 'd', END + END + }; + uint8_t nested_choices_payload3[] = {LIST(3), + LIST(1), 0x65, 'h', 'e', 'l', 'l', 'o', END + 0xf6, + LIST(1), 0x65, 'w', 'o', 'r', 'l', 'd', END + END + }; + uint8_t nested_choices_payload4[] = {LIST(2), + LIST(1), LIST(1), + LIST(3), + LIST(1), 0x65, 'h', 'e', 'l', 'l', 'o', END + 0xf6, + LIST(1), 0x65, 'w', 'o', 'r', 'l', 'd', END + END + END END + 0xf6, + END + }; + uint8_t nested_choices_payload5[] = {LIST(2), + MAP(2), + 0, LIST(1), 0x65, 'h', 'e', 'l', 'l', 'o', END + 1, LIST(1), 0x65, 'w', 'o', 'r', 'l', 'd', END + END + 0xf6, + END + }; + struct Choice1_r result1; + struct Choice2_r result2; + struct Choice3_r result3; + struct Choice4_r result4; + struct Choice5_r result5; + + size_t num_decode; + + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice1(nested_choices_payload1, + sizeof(nested_choices_payload1), &result1, &num_decode), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice2(nested_choices_payload1, + sizeof(nested_choices_payload1), &result2, &num_decode), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice3(nested_choices_payload1, + sizeof(nested_choices_payload1), &result3, &num_decode), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice4(nested_choices_payload1, + sizeof(nested_choices_payload1), &result4, &num_decode), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice5(nested_choices_payload1, + sizeof(nested_choices_payload1), &result5, &num_decode), NULL); + zassert_equal(result1.Choice1_choice, Choice1_nil_c); + zassert_equal(result2.Choice2_choice, Choice2_nil_c); + zassert_equal(result3.Choice3_choice, Choice3_nil_c); + zassert_equal(result4.Choice4_choice, Choice4_nil_c); + zassert_equal(result5.Choice5_choice, Choice5_nil_c); + + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice1(nested_choices_payload2, + sizeof(nested_choices_payload2), &result1, &num_decode), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice2(nested_choices_payload2, + sizeof(nested_choices_payload2), &result2, &num_decode), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice3(nested_choices_payload2, + sizeof(nested_choices_payload2), &result3, &num_decode), NULL); + + zassert_equal(result1.Choice1_choice, Choice1_tstr_l_l_c); + zassert_equal(result2.Choice2_choice, Choice2_tstr_l_l_c); + zassert_equal(result3.Choice3_choice, Choice3_union_l_c); + + zassert_equal(2, result1.tstr_count); + zassert_equal(2, result2.tstr_count); + zassert_equal(2, result3.Union_count); + zassert_equal(result3.Union[0].Union_choice, union_tstr_l_tstr_c); + zassert_equal(result3.Union[1].Union_choice, union_tstr_l_tstr_c); + zassert_zcbor_string(&result1.tstr[0], "hello", 5); + zassert_zcbor_string(&result2.tstr[0], "hello", 5); + zassert_zcbor_string(&result3.Union[0].tstr, "hello", 5); + zassert_zcbor_string(&result1.tstr[1], "world", 5); + zassert_zcbor_string(&result2.tstr[1], "world", 5); + zassert_zcbor_string(&result3.Union[1].tstr, "world", 5); + + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice3(nested_choices_payload3, + sizeof(nested_choices_payload3), &result3, &num_decode), NULL); + + zassert_equal(result3.Choice3_choice, Choice3_union_l_c); + + zassert_equal(3, result3.Union_count); + zassert_equal(result3.Union[0].Union_choice, union_tstr_l_tstr_c); + zassert_equal(result3.Union[1].Union_choice, Choice3_union_l_union_nil_c); + zassert_equal(result3.Union[2].Union_choice, union_tstr_l_tstr_c); + zassert_zcbor_string(&result3.Union[0].tstr, "hello", 5); + zassert_zcbor_string(&result3.Union[2].tstr, "world", 5); + + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice4(nested_choices_payload4, + sizeof(nested_choices_payload4), &result4, &num_decode), NULL); + + zassert_equal(result4.Choice4_choice, Choice4_union_l_c); + zassert_equal(2, result4.Union_count); + zassert_equal(result4.Union_count, 2); + zassert_equal(result4.Union[0].Union_choice, Choice3_m_l_l_Choice3_m_l_Choice3_m_c); + + zassert_equal(3, result4.Union[0].Choice3_m.Union_count); + zassert_equal(result4.Union[0].Choice3_m.Choice3_choice, Choice3_union_l_c); + zassert_equal(result4.Union[0].Choice3_m.Union[0].Union_choice, union_tstr_l_tstr_c); + zassert_equal(result4.Union[0].Choice3_m.Union[1].Union_choice, Choice3_union_l_union_nil_c); + zassert_equal(result4.Union[0].Choice3_m.Union[2].Union_choice, union_tstr_l_tstr_c); + zassert_zcbor_string(&result4.Union[0].Choice3_m.Union[0].tstr, "hello", 5); + zassert_zcbor_string(&result4.Union[0].Choice3_m.Union[2].tstr, "world", 5); + + zassert_equal(result4.Union[1].Union_choice, Choice4_union_l_union_nil_c); + + zassert_equal(ZCBOR_SUCCESS, cbor_decode_Choice5(nested_choices_payload5, + sizeof(nested_choices_payload5), &result5, &num_decode), NULL); + + zassert_equal(result5.Choice5_choice, Choice5_union_l_c); + zassert_equal(2, result5.Union_count); + zassert_equal(result5.Union[0].Union_choice, union_map_c); + zassert_equal(result5.Union[0].tstr_l_count, 2); + zassert_equal(result5.Union[0].tstr_l[0].tstr_l_key, 0); + zassert_zcbor_string(&result5.Union[0].tstr_l[0].tstr, "hello", 5); + zassert_equal(result5.Union[0].tstr_l[1].tstr_l_key, 1); + zassert_zcbor_string(&result5.Union[0].tstr_l[1].tstr, "world", 5); + zassert_equal(result5.Union[1].Union_choice, Choice5_union_l_union_nil_c); +} + + ZTEST_SUITE(cbor_decode_test5, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/encode/test3_corner_cases/CMakeLists.txt b/tests/encode/test3_corner_cases/CMakeLists.txt index 9b523590..34ff5076 100644 --- a/tests/encode/test3_corner_cases/CMakeLists.txt +++ b/tests/encode/test3_corner_cases/CMakeLists.txt @@ -48,6 +48,11 @@ set(py_command MapUnionPrimAlias EmptyContainer SingleElemList + Choice1 + Choice2 + Choice3 + Choice4 + Choice5 -e ${bit_arg} --short-names diff --git a/tests/encode/test3_corner_cases/src/main.c b/tests/encode/test3_corner_cases/src/main.c index 00a970cb..d6e21ab1 100644 --- a/tests/encode/test3_corner_cases/src/main.c +++ b/tests/encode/test3_corner_cases/src/main.c @@ -1649,4 +1649,149 @@ ZTEST(cbor_encode_test3, test_single_elem_list) } +ZTEST(cbor_encode_test3, test_nested_choices) +{ + uint8_t nested_choices_exp_payload1[] = {0xf6}; + uint8_t nested_choices_exp_payload2[] = {LIST(2), + LIST(1), 0x65, 'h', 'e', 'l', 'l', 'o', END + LIST(1), 0x65, 'w', 'o', 'r', 'l', 'd', END + END + }; + uint8_t nested_choices_exp_payload3[] = {LIST(3), + LIST(1), 0x65, 'h', 'e', 'l', 'l', 'o', END + 0xf6, + LIST(1), 0x65, 'w', 'o', 'r', 'l', 'd', END + END + }; + uint8_t nested_choices_exp_payload4[] = {LIST(2), + LIST(1), LIST(1), + LIST(3), + LIST(1), 0x65, 'h', 'e', 'l', 'l', 'o', END + 0xf6, + LIST(1), 0x65, 'w', 'o', 'r', 'l', 'd', END + END + END END + 0xf6, + END + }; + uint8_t nested_choices_exp_payload5[] = {LIST(2), + MAP(2), + 0, LIST(1), 0x65, 'h', 'e', 'l', 'l', 'o', END + 1, LIST(1), 0x65, 'w', 'o', 'r', 'l', 'd', END + END + 0xf6, + END + }; + struct Choice1_r input1 = {.Choice1_choice = Choice1_nil_c}; + struct Choice2_r input2 = {.Choice2_choice = Choice2_nil_c}; + struct Choice3_r input3 = {.Choice3_choice = Choice3_nil_c}; + struct Choice4_r input4 = {.Choice4_choice = Choice4_nil_c}; + struct Choice5_r input5 = {.Choice5_choice = Choice5_nil_c}; + + uint8_t payload[50]; + + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice1(payload, + sizeof(payload), &input1, NULL), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice2(payload, + sizeof(payload), &input2, NULL), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice3(payload, + sizeof(payload), &input3, NULL), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice4(payload, + sizeof(payload), &input4, NULL), NULL); + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice5(payload, + sizeof(payload), &input5, NULL), NULL); + zassert_mem_equal(payload, nested_choices_exp_payload1, sizeof(nested_choices_exp_payload1)); + zassert_mem_equal(payload, nested_choices_exp_payload1, sizeof(nested_choices_exp_payload1)); + zassert_mem_equal(payload, nested_choices_exp_payload1, sizeof(nested_choices_exp_payload1)); + zassert_mem_equal(payload, nested_choices_exp_payload1, sizeof(nested_choices_exp_payload1)); + zassert_mem_equal(payload, nested_choices_exp_payload1, sizeof(nested_choices_exp_payload1)); + + input1.Choice1_choice = Choice1_tstr_l_l_c; + input1.tstr_count = 2; + input1.tstr[0].value = "hello"; + input1.tstr[0].len = 5; + input1.tstr[1].value = "world"; + input1.tstr[1].len = 5; + + input2.Choice2_choice = Choice2_tstr_l_l_c; + input2.tstr_count = 2; + input2.tstr[0].value = "hello"; + input2.tstr[0].len = 5; + input2.tstr[1].value = "world"; + input2.tstr[1].len = 5; + + input3.Choice3_choice = Choice3_union_l_c; + input3.Union_count = 2; + input3.Union[0].Union_choice = union_tstr_l_tstr_c; + input3.Union[1].Union_choice = union_tstr_l_tstr_c; + input3.Union[0].tstr.value = "hello"; + input3.Union[0].tstr.len = 5; + input3.Union[1].tstr.value = "world"; + input3.Union[1].tstr.len = 5; + + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice1(payload, + sizeof(payload), &input1, NULL), NULL); + zassert_mem_equal(payload, nested_choices_exp_payload2, sizeof(nested_choices_exp_payload2)); + + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice2(payload, + sizeof(payload), &input2, NULL), NULL); + zassert_mem_equal(payload, nested_choices_exp_payload2, sizeof(nested_choices_exp_payload2)); + + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice3(payload, + sizeof(payload), &input3, NULL), NULL); + zassert_mem_equal(payload, nested_choices_exp_payload2, sizeof(nested_choices_exp_payload2)); + + input3.Choice3_choice = Choice3_union_l_c; + input3.Union_count = 3; + input3.Union[0].Union_choice = union_tstr_l_tstr_c; + input3.Union[1].Union_choice = Choice3_union_l_union_nil_c; + input3.Union[2].Union_choice = union_tstr_l_tstr_c; + input3.Union[0].tstr.value = "hello"; + input3.Union[0].tstr.len = 5; + input3.Union[2].tstr.value = "world"; + input3.Union[2].tstr.len = 5; + + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice3(payload, + sizeof(payload), &input3, NULL), NULL); + zassert_mem_equal(payload, nested_choices_exp_payload3, sizeof(nested_choices_exp_payload3)); + + input4.Choice4_choice = Choice4_union_l_c; + input4.Union_count = 2; + input4.Union[0].Union_choice = Choice3_m_l_l_Choice3_m_l_Choice3_m_c; + + input4.Union[0].Choice3_m.Choice3_choice = Choice3_union_l_c; + input4.Union[0].Choice3_m.Union_count = 3; + input4.Union[0].Choice3_m.Union[0].Union_choice = union_tstr_l_tstr_c; + input4.Union[0].Choice3_m.Union[1].Union_choice = Choice3_union_l_union_nil_c; + input4.Union[0].Choice3_m.Union[2].Union_choice = union_tstr_l_tstr_c; + input4.Union[0].Choice3_m.Union[0].tstr.value = "hello"; + input4.Union[0].Choice3_m.Union[0].tstr.len = 5; + input4.Union[0].Choice3_m.Union[2].tstr.value = "world"; + input4.Union[0].Choice3_m.Union[2].tstr.len = 5; + + input4.Union[1].Union_choice = Choice4_union_l_union_nil_c; + + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice4(payload, + sizeof(payload), &input4, NULL), NULL); + + zassert_mem_equal(payload, nested_choices_exp_payload4, sizeof(nested_choices_exp_payload4)); + + input5.Choice5_choice = Choice5_union_l_c; + input5.Union_count = 2; + input5.Union[0].Union_choice = union_map_c; + input5.Union[0].tstr_l_count = 2; + input5.Union[0].tstr_l[0].tstr_l_key = 0; + input5.Union[0].tstr_l[0].tstr.value = "hello"; + input5.Union[0].tstr_l[0].tstr.len = 5; + input5.Union[0].tstr_l[1].tstr_l_key = 1; + input5.Union[0].tstr_l[1].tstr.value = "world"; + input5.Union[0].tstr_l[1].tstr.len = 5; + input5.Union[1].Union_choice = Choice5_union_l_union_nil_c; + + zassert_equal(ZCBOR_SUCCESS, cbor_encode_Choice5(payload, + sizeof(payload), &input5, NULL), NULL); + zassert_mem_equal(payload, nested_choices_exp_payload5, sizeof(nested_choices_exp_payload5)); +} + + ZTEST_SUITE(cbor_encode_test3, NULL, NULL, NULL, NULL, NULL); diff --git a/zcbor/zcbor.py b/zcbor/zcbor.py index c709801f..05c17c65 100755 --- a/zcbor/zcbor.py +++ b/zcbor/zcbor.py @@ -1046,8 +1046,11 @@ def __init__(self, *args, **kwargs): self.dependsOnCall = False self.skipped = False - def var_name(self, with_prefix=False): + def var_name(self, with_prefix=False, observe_skipped=True): """Name of variables and enum members for this element.""" + if (observe_skipped and self.skip_condition() + and self.type in ["LIST", "MAP", "GROUP"] and self.value): + return self.value[0].var_name(with_prefix) name = self.id(with_prefix=with_prefix) if name in c_keywords: name = name.capitalize() @@ -1060,7 +1063,7 @@ def skip_condition(self): if self.skipped: return True if self.type in ["LIST", "MAP", "GROUP"]: - return not self.multi_val_condition() + return not self.repeated_multi_var_condition() if self.type == "OTHER": return ((not self.repeated_multi_var_condition()) and (not self.multi_var_condition()) @@ -1087,8 +1090,8 @@ def is_delegated_type(self): def set_access_prefix(self, prefix, is_delegated=False): """Recursively set the access prefix for this element and all its children.""" self.accessPrefix = prefix - self.is_delegated = is_delegated if self.type in ["LIST", "MAP", "GROUP", "UNION"]: + self.set_skipped(self.skip_condition()) list(map(lambda child: child.set_skipped(child.skip_condition()), self.value)) list(map(lambda child: child.set_access_prefix( @@ -1096,12 +1099,14 @@ def set_access_prefix(self, prefix, is_delegated=False): is_delegated=(self.delegate_type_condition() or (is_delegated and self.skip_condition()))), self.value)) - elif self in self.my_types.values() and self.type != "OTHER": + elif self in self.my_types.values(): self.set_skipped(not self.multi_member()) if self.key is not None: self.key.set_access_prefix(self.var_access()) if self.cbor_var_condition(): self.cbor.set_access_prefix(self.var_access()) + self.is_delegated = is_delegated and not self.skip_condition() + return def multi_member(self): """Whether this type has multiple member variables.""" @@ -1889,11 +1894,10 @@ def init_args(self): def delegate_type_condition(self): """Whether to use the C type of the first child as this type's C type""" - ret = (self.type in ["LIST", "MAP", "GROUP"] - and not self.multi_var_condition() - and not self.multi_val_condition() - and not self.self_repeated_multi_var_condition() - and self in self.my_types.values()) + ret = self.skip_condition() and (self.multi_var_condition() + or self.self_repeated_multi_var_condition() + or self.range_check_condition() + or (self in self.my_types.values())) return ret def is_delegated_type(self): @@ -2218,11 +2222,11 @@ def single_func_prim_prefix(self): def xcode_func_name(self): """Name of the encoder/decoder function for this element.""" - return f"{self.mode}_{self.var_name(with_prefix=True)}" + return f"{self.mode}_{self.var_name(with_prefix=True, observe_skipped=False)}" def repeated_xcode_func_name(self): """Name of the encoder/decoder function for the repeated part of this element.""" - return f"{self.mode}_repeated_{self.var_name(with_prefix=True)}" + return f"{self.mode}_repeated_{self.var_name(with_prefix=True, observe_skipped=False)}" def single_func_prim_name(self, union_int=None, ptr_result=False): """Function name for xcoding this type, when it is a primitive type"""