Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zcbor.py: Add support for unordered maps in generated code #425

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
`zcbor_bool_*()`, `zcbor_nil_*()`, and `zcbor_undefined_*()`.
If a removed variant is strictly needed, add your own forward declaration in your code.

* Code generation naming:
* Code generation:

* `zcbor_entry_function()` gained a new argument.
Regenerate or manually edit your generated files to add a 0 parameter to all calls to `zcbor_entry_function()`.

* More C keywords are now capitalized to avoid naming collision.
You might have to capitalize some instances if your code was generated to have those names.
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ usage: zcbor code [-h] -c CDDL [--no-prelude] [-v]
ENTRY_TYPES [ENTRY_TYPES ...] [-d] [-e] [--time-header]
[--git-sha-header] [-b {32,64}]
[--include-prefix INCLUDE_PREFIX] [-s]
[--file-header FILE_HEADER]
[--file-header FILE_HEADER] [--unordered-maps]

Parse a CDDL file and produce C code that validates and xcodes CBOR.
The output from this script is a C file and a header file. The header file
Expand Down Expand Up @@ -543,6 +543,13 @@ options:
generated files, e.g. copyright. Can be a string or a
path to a file. If interpreted as a path to an
existing file, the file's contents will be used.
--unordered-maps Add support in the generated code for parsing maps
with unknown element order. When enabled, the
generated code will use the zcbor_unordered_map_*()
API to decode data whenever inside a map. zcbor
detects when ZCBOR_MAP_SMART_SEARCH is needed and
enable This places restrictions on the level of
ambiguity allowed between map keys in a map.

```

Expand Down
4 changes: 2 additions & 2 deletions include/zcbor_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ void zcbor_new_state(zcbor_state_t *state_array, size_t n_states,
*/
int zcbor_entry_function(const uint8_t *payload, size_t payload_len,
void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func,
size_t n_states, size_t elem_count);
size_t n_states, size_t elem_count, size_t n_flags);

#ifdef ZCBOR_STOP_ON_ERROR
/** Check stored error and fail if present, but only if stop_on_error is true.
Expand Down Expand Up @@ -530,7 +530,7 @@ static inline size_t zcbor_flags_to_states(size_t num_flags)
#define ZCBOR_FLAG_STATES(n_flags) zcbor_flags_to_states(n_flags)
oyvindronningstad marked this conversation as resolved.
Show resolved Hide resolved

#else
#define ZCBOR_FLAG_STATES(n_flags) 0
#define ZCBOR_FLAG_STATES(n_flags) (n_flags * 0)
#endif

size_t strnlen(const char *, size_t);
Expand Down
1 change: 1 addition & 0 deletions samples/pet/pet.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ target_include_directories(pet PUBLIC
${CMAKE_CURRENT_LIST_DIR}/../../include
${CMAKE_CURRENT_LIST_DIR}/include
)
target_compile_definitions(pet PUBLIC ZCBOR_MAP_SMART_SEARCH)
5 changes: 3 additions & 2 deletions samples/pet/src/pet_decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#error "The type file was generated with a different default_max_qty than this file"
#endif


#define log_result(state, result, func) do { \
if (!result) { \
zcbor_trace_file(state); \
Expand Down Expand Up @@ -62,8 +63,8 @@ int cbor_decode_Pet(
struct Pet *result,
size_t *payload_len_out)
{
zcbor_state_t states[4];
zcbor_state_t states[4 + 0];

return zcbor_entry_function(payload, payload_len, (void *)result, payload_len_out, states,
(zcbor_decoder_t *)decode_Pet, sizeof(states) / sizeof(zcbor_state_t), 1);
(zcbor_decoder_t *)decode_Pet, sizeof(states) / sizeof(zcbor_state_t), 1, 0);
}
5 changes: 3 additions & 2 deletions samples/pet/src/pet_encode.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#error "The type file was generated with a different default_max_qty than this file"
#endif


#define log_result(state, result, func) do { \
if (!result) { \
zcbor_trace_file(state); \
Expand Down Expand Up @@ -56,8 +57,8 @@ int cbor_encode_Pet(
const struct Pet *input,
size_t *payload_len_out)
{
zcbor_state_t states[4];
zcbor_state_t states[4 + 0];

return zcbor_entry_function(payload, payload_len, (void *)input, payload_len_out, states,
(zcbor_decoder_t *)encode_Pet, sizeof(states) / sizeof(zcbor_state_t), 1);
(zcbor_decoder_t *)encode_Pet, sizeof(states) / sizeof(zcbor_state_t), 1, 0);
}
8 changes: 6 additions & 2 deletions src/zcbor_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,13 @@ size_t zcbor_remaining_str_len(zcbor_state_t *state)

int zcbor_entry_function(const uint8_t *payload, size_t payload_len,
void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func,
size_t n_states, size_t elem_count)
size_t n_states, size_t elem_count, size_t n_flags)
{
zcbor_new_state(state, n_states, payload, payload_len, elem_count, NULL, 0);
zcbor_new_state(state, n_states, payload, payload_len, elem_count,
(uint8_t *)&state[n_states - 1 - ZCBOR_FLAG_STATES(n_flags)],
ZCBOR_FLAG_STATES(n_flags) * sizeof(zcbor_state_t));

state->constant_state->manually_process_elem = true;

bool ret = func(state, result);

Expand Down
6 changes: 5 additions & 1 deletion src/zcbor_decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,10 @@ bool zcbor_unordered_map_search(zcbor_decoder_t key_decoder, zcbor_state_t *stat
(void)old_flags;
}

if (!should_try_key(state)) {
zcbor_log("Skipping element at index %zu.\n", get_current_index(state, 0));
}

if (should_try_key(state) && try_key(state, key_result, key_decoder)) {
if (!ZCBOR_MANUALLY_PROCESS_ELEM(state)) {
ZCBOR_FAIL_IF(!zcbor_elem_processed(state));
Expand Down Expand Up @@ -1576,8 +1580,8 @@ bool zcbor_multi_decode(size_t min_decode,
*num_decode = i;
state->payload = payload_bak;
state->elem_count = elem_count_bak;
ZCBOR_ERR_IF(i < min_decode, ZCBOR_ERR_ITERATIONS);
zcbor_log("Found %zu elements.\r\n", i);
ZCBOR_ERR_IF(i < min_decode, ZCBOR_ERR_ITERATIONS);
return true;
}
}
Expand Down
14 changes: 6 additions & 8 deletions tests/cases/serial_recovery.cddl
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
; SPDX-License-Identifier: Apache-2.0
;

Member = ("image" => int) /
("data" => bstr) /
("len" => int) /
("off" => int) /
("sha" => bstr) /
(tstr => any)

Upload = {
3*8members: Member
? "image" => uint,
? "len" => uint,
"off" => uint,
? "sha" => bstr,
? "data" => bstr,
? "upgrade" => bool,
}
46 changes: 46 additions & 0 deletions tests/cases/unordered_map.cddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
;
; Copyright (c) 2023 Nordic Semiconductor ASA
;
; SPDX-License-Identifier: Apache-2.0
;

UnorderedMap1 = {
1 => "one",
2 => "two",
int => bstr,
"foo" => "bar",
bazboz,
*bstr => intlist,
}

bazboz = ("baz" => "boz")
intlist = [*int]

UnorderedMap2 = {
+typeUnion,
"map" => {
?-1 => bstr,
?-2 => bstr,
?bool => int,
}
}

typeUnion = type1 / type2 / typeDefault
type1 = 1 => tstr
type2 = 2 => tstr
typeDefault = int => nil

UnorderedMap3 = {
1*2000 uint => uint,
1*2000 nint => nint,
}

group1 = (
uint => tstr,
nint => bstr,
)
type3 = 3*3group1

UnorderedMap4 = {
3*3group1,
}
1 change: 1 addition & 0 deletions tests/decode/test3_simple/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ set(py_command_serial_recovery
-d
${bit_arg}
--short-names
--unordered-maps

# Testing the --include-prefix option
--include-prefix serial
Expand Down
43 changes: 18 additions & 25 deletions tests/decode/test3_simple/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,19 +487,15 @@ ZTEST(cbor_decode_test3, test_serial1)
zassert_equal(ZCBOR_SUCCESS, ret, "decoding failed: %d.", ret);
zassert_equal(sizeof(serial_rec_input1), decode_len, NULL);

zassert_equal(5, upload.members_count,
"expect 5 members");
zassert_equal(Member_data_c, upload.members[0].members
.Member_choice, "expect data 1st");
zassert_equal(Member_image_c, upload.members[1].members
.Member_choice, "expect image 2nd");
zassert_equal(Member_len_c, upload.members[2].members
.Member_choice, "was %d\r\n", upload.members[2].members
.Member_choice);
zassert_equal(Member_off_c, upload.members[3].members
.Member_choice, "expect off 4th");
zassert_equal(Member_sha_c, upload.members[4].members
.Member_choice, "expect sha 5th");
zassert_true(upload.data_present, NULL);
zassert_equal(0x129, upload.data.data.len, NULL);
zassert_true(upload.image_present, NULL);
zassert_equal(0, upload.image.image, NULL);
zassert_true(upload.len_present, NULL);
zassert_equal(0x3b2c, upload.len.len, NULL);
zassert_equal(0, upload.off, NULL);
zassert_true(upload.sha_present, NULL);
zassert_equal(0x20, upload.sha.sha.len, NULL);
}

ZTEST(cbor_decode_test3, test_serial2)
Expand All @@ -511,18 +507,15 @@ ZTEST(cbor_decode_test3, test_serial2)
zassert_equal(ZCBOR_SUCCESS, ret, "decoding failed: %d.", ret);
zassert_equal(sizeof(serial_rec_input2), decode_len, NULL);

zassert_equal(5, upload.members_count,
"expect 5 members");
zassert_equal(Member_data_c, upload.members[0].members
.Member_choice, "expect data 1st");
zassert_equal(Member_image_c, upload.members[1].members
.Member_choice, "expect image 2nd");
zassert_equal(Member_len_c, upload.members[2].members
.Member_choice, "expect len 3rd");
zassert_equal(Member_off_c, upload.members[3].members
.Member_choice, "expect off 4th");
zassert_equal(Member_sha_c, upload.members[4].members
.Member_choice, "expect sha 5th");
zassert_true(upload.data_present, NULL);
zassert_equal(0x129, upload.data.data.len, NULL);
zassert_true(upload.image_present, NULL);
zassert_equal(0, upload.image.image, NULL);
zassert_true(upload.len_present, NULL);
zassert_equal(0x2fe0, upload.len.len, NULL);
zassert_equal(0, upload.off, NULL);
zassert_true(upload.sha_present, NULL);
zassert_equal(0x20, upload.sha.sha.len, NULL);
}

ZTEST_SUITE(cbor_decode_test3, NULL, NULL, NULL, NULL, NULL);
2 changes: 2 additions & 0 deletions tests/decode/test9_manifest14/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ cmake_minimum_required(VERSION 3.13.1)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(test9_manifest14)
set(MAP_SEARCH_FLAGS ON) # Because of --unordered maps
include(../../cmake/test_template.cmake)

if (NOT MANIFEST)
Expand All @@ -30,6 +31,7 @@ set(py_command
SUIT_Common_Sequence
-d
${bit_arg}
--unordered-maps
)

execute_process(
Expand Down
40 changes: 40 additions & 0 deletions tests/decode/testA_unordered_map/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#

cmake_minimum_required(VERSION 3.13.1)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(test9_manifest14)
set(MAP_SEARCH_FLAGS ON) # Because of --unordered maps
set(MAP_SMART_SEARCH ON)
include(../../cmake/test_template.cmake)

set(py_command
${PYTHON_EXECUTABLE}
${CMAKE_CURRENT_LIST_DIR}/../../../zcbor/zcbor.py
code
-c ${CMAKE_CURRENT_LIST_DIR}/../../cases/unordered_map.cddl
--output-cmake ${PROJECT_BINARY_DIR}/unordered_map.cmake
-t
UnorderedMap1
UnorderedMap2
UnorderedMap3
UnorderedMap4
-d
${bit_arg}
--unordered-maps
)

execute_process(
COMMAND
${py_command}
COMMAND_ERROR_IS_FATAL ANY
)

include(${PROJECT_BINARY_DIR}/unordered_map.cmake)

target_link_libraries(unordered_map PRIVATE zephyr_interface)
target_link_libraries(app PRIVATE unordered_map)
8 changes: 8 additions & 0 deletions tests/decode/testA_unordered_map/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#
# Copyright (c) 2021 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#

CONFIG_ZTEST=y
CONFIG_ZTEST_STACK_SIZE=131072
Loading