Skip to content

Commit d229ca7

Browse files
zcbor.py: Add support for unordered maps in generated code
Add support for the --unordered-maps option to zcbor code which makes use of the zcbor_unordered_maps_*() API in generated code. Signed-off-by: Øyvind Rønningstad <[email protected]>
1 parent ae96219 commit d229ca7

File tree

16 files changed

+665
-55
lines changed

16 files changed

+665
-55
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ usage: zcbor code [-h] -c CDDL [--no-prelude] [-v]
442442
-t ENTRY_TYPES [ENTRY_TYPES ...] [-d] [-e] [--time-header]
443443
[--git-sha-header] [-b {32,64}]
444444
[--include-prefix INCLUDE_PREFIX] [-s]
445-
[--file-header FILE_HEADER]
445+
[--file-header FILE_HEADER] [--unordered-maps]
446446
447447
Parse a CDDL file and produce C code that validates and xcodes CBOR.
448448
The output from this script is a C file and a header file. The header file
@@ -543,6 +543,15 @@ options:
543543
generated files, e.g. copyright. Can be a string or a
544544
path to a file. If interpreted as a path to an
545545
existing file, the file's contents will be used.
546+
--unordered-maps Add support in the generated code for decoding maps
547+
with unknown element order. When enabled, the
548+
generated code will use the zcbor_unordered_map_*()
549+
API to decode data whenever inside a map. zcbor
550+
detects when ZCBOR_MAP_SMART_SEARCH is needed and
551+
enables it in the generated cmake file. Enabling
552+
--unordered-maps places some restrictions on the level
553+
of ambiguity allowed between map keys in a map. Only
554+
affects decoding (--decode/-d).
546555
547556
```
548557

include/zcbor_common.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ do { \
285285
#define ZCBOR_ERR_MAP_FLAGS_NOT_AVAILABLE 20
286286
#define ZCBOR_ERR_INVALID_VALUE_ENCODING 21 ///! When ZCBOR_CANONICAL is defined, and the incoming data is not encoded with minimal length, or uses indefinite length array.
287287
#define ZCBOR_ERR_CONSTANT_STATE_MISSING 22
288+
#define ZCBOR_ERR_NO_FLAG_MEM 23
288289
#define ZCBOR_ERR_UNKNOWN 31
289290

290291
/** The largest possible elem_count. */
@@ -339,11 +340,22 @@ void zcbor_new_state(zcbor_state_t *state_array, size_t n_states,
339340

340341
/** Do boilerplate entry function procedure.
341342
* Initialize states, call function, and check the result.
343+
* This sets `manually_process_elem` to true.
342344
*/
343345
int zcbor_entry_function(const uint8_t *payload, size_t payload_len,
344346
void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func,
345347
size_t n_states, size_t elem_count);
346348

349+
350+
/** Do boilerplate entry function procedure.
351+
* Initialize states, call function, and check the result.
352+
* Allows for using one or more state structs for map elem_states.
353+
* This sets `manually_process_elem` to true.
354+
*/
355+
int zcbor_entry_function_with_elem_states(const uint8_t *payload, size_t payload_len,
356+
void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func,
357+
size_t n_states, size_t elem_count, size_t n_elem_states);
358+
347359
#ifdef ZCBOR_STOP_ON_ERROR
348360
/** Check stored error and fail if present, but only if stop_on_error is true.
349361
*
@@ -522,10 +534,15 @@ static inline size_t zcbor_flags_to_bytes(size_t num_flags)
522534
(ZCBOR_ROUND_UP(num_flags, sizeof(zcbor_state_t) * ZCBOR_BITS_PER_BYTE) \
523535
/ (sizeof(zcbor_state_t) * ZCBOR_BITS_PER_BYTE))
524536

537+
#define ZCBOR_BYTES_TO_STATES(num_bytes) \
538+
(ZCBOR_ROUND_UP(num_bytes, sizeof(zcbor_state_t)) / (sizeof(zcbor_state_t)))
539+
540+
#define ZCBOR_BYTE_STATES(n_bytes) ZCBOR_BYTES_TO_STATES(n_bytes)
525541
#define ZCBOR_FLAG_STATES(n_flags) ZCBOR_FLAGS_TO_STATES(n_flags)
526542

527543
#else
528-
#define ZCBOR_FLAG_STATES(n_flags) 0
544+
#define ZCBOR_BYTE_STATES(n_bytes) (n_bytes * 0)
545+
#define ZCBOR_FLAG_STATES(n_flags) (n_flags * 0)
529546
#endif
530547

531548
size_t strnlen(const char *, size_t);

src/zcbor_common.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ void zcbor_new_state(zcbor_state_t *state_array, size_t n_states,
142142
state_array[0].decode_state.indefinite_length_array = false;
143143
#ifdef ZCBOR_MAP_SMART_SEARCH
144144
state_array[0].decode_state.map_search_elem_state = flags;
145+
if (flags == NULL) {
146+
flags_bytes = 0;
147+
}
145148
state_array[0].decode_state.map_elem_count = 0;
146149
#else
147150
state_array[0].decode_state.map_elems_processed = 0;
@@ -299,11 +302,33 @@ size_t zcbor_remaining_str_len(zcbor_state_t *state)
299302
}
300303

301304

302-
int zcbor_entry_function(const uint8_t *payload, size_t payload_len,
305+
int zcbor_entry_function_with_elem_states(const uint8_t *payload, size_t payload_len,
303306
void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func,
304-
size_t n_states, size_t elem_count)
307+
size_t n_states, size_t elem_count, size_t n_elem_states)
305308
{
306-
zcbor_new_state(state, n_states, payload, payload_len, elem_count, NULL, 0);
309+
uint8_t *flags = NULL;
310+
size_t n_elem_state_bytes = 0;
311+
312+
#ifdef ZCBOR_MAP_SMART_SEARCH
313+
if (n_elem_states > 0) {
314+
if (n_states < (ZCBOR_FLAG_STATES(n_elem_states) + 3)) {
315+
return ZCBOR_ERR_NO_FLAG_MEM;
316+
}
317+
318+
size_t flag_state_index =
319+
n_states - ZCBOR_FLAG_STATES(n_elem_states);
320+
flags = (uint8_t *)&state[flag_state_index];
321+
n_states = flag_state_index;
322+
n_elem_state_bytes = zcbor_flags_to_bytes(n_elem_states);
323+
}
324+
#else
325+
(void)n_elem_states;
326+
#endif
327+
328+
zcbor_new_state(state, n_states, payload, payload_len, elem_count, flags,
329+
n_elem_state_bytes);
330+
331+
state->constant_state->manually_process_elem = true;
307332

308333
bool ret = func(state, result);
309334

@@ -321,6 +346,13 @@ int zcbor_entry_function(const uint8_t *payload, size_t payload_len,
321346
return ZCBOR_SUCCESS;
322347
}
323348

349+
int zcbor_entry_function(const uint8_t *payload, size_t payload_len,
350+
void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func,
351+
size_t n_states, size_t elem_count)
352+
{
353+
return zcbor_entry_function_with_elem_states(payload, payload_len, result, payload_len_out,
354+
state, func, n_states, elem_count, 0);
355+
}
324356

325357
/* Float16: */
326358
#define F16_SIGN_OFFS 15 /* Bit offset of the sign bit. */

src/zcbor_decode.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,10 @@ bool zcbor_unordered_map_search(zcbor_decoder_t key_decoder, zcbor_state_t *stat
959959
(void)old_flags;
960960
}
961961

962+
if (!should_try_key(state)) {
963+
zcbor_log("Skipping element at index %zu.\n", get_current_index(state, 0));
964+
}
965+
962966
if (should_try_key(state) && try_key(state, key_result, key_decoder)) {
963967
if (!ZCBOR_MANUALLY_PROCESS_ELEM(state)) {
964968
ZCBOR_FAIL_IF(!zcbor_elem_processed(state));
@@ -1576,8 +1580,8 @@ bool zcbor_multi_decode(size_t min_decode,
15761580
*num_decode = i;
15771581
state->payload = payload_bak;
15781582
state->elem_count = elem_count_bak;
1579-
ZCBOR_ERR_IF(i < min_decode, ZCBOR_ERR_ITERATIONS);
15801583
zcbor_log("Found %zu elements.\r\n", i);
1584+
ZCBOR_ERR_IF(i < min_decode, ZCBOR_ERR_ITERATIONS);
15811585
return true;
15821586
}
15831587
}

src/zcbor_print.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ const char *zcbor_error_str(int error)
9393
ZCBOR_ERR_CASE(ZCBOR_ERR_MAP_FLAGS_NOT_AVAILABLE)
9494
ZCBOR_ERR_CASE(ZCBOR_ERR_INVALID_VALUE_ENCODING)
9595
ZCBOR_ERR_CASE(ZCBOR_ERR_CONSTANT_STATE_MISSING)
96+
ZCBOR_ERR_CASE(ZCBOR_ERR_NO_FLAG_MEM)
9697
}
9798
#undef ZCBOR_ERR_CASE
9899

tests/cases/serial_recovery.cddl

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@
44
; SPDX-License-Identifier: Apache-2.0
55
;
66

7-
Member = ("image" => int) /
8-
("data" => bstr) /
9-
("len" => int) /
10-
("off" => int) /
11-
("sha" => bstr) /
12-
(tstr => any)
13-
147
Upload = {
15-
3*8members: Member
8+
? "image" => uint,
9+
? "len" => uint,
10+
"off" => uint,
11+
? "sha" => bstr,
12+
? "data" => bstr,
13+
? "upgrade" => bool,
1614
}

tests/cases/unordered_map.cddl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
;
2+
; Copyright (c) 2023 Nordic Semiconductor ASA
3+
;
4+
; SPDX-License-Identifier: Apache-2.0
5+
;
6+
7+
UnorderedMap1 = {
8+
1 => "one",
9+
2 => "two",
10+
int => bstr,
11+
"foo" => "bar",
12+
bazboz,
13+
*bstr => intlist,
14+
}
15+
16+
bazboz = ("baz" => "boz")
17+
intlist = [*int]
18+
19+
UnorderedMap2 = {
20+
+typeUnion,
21+
"map" => {
22+
?-1 => bstr,
23+
?-2 => bstr,
24+
?bool => int,
25+
}
26+
}
27+
28+
typeUnion = type1 / type2 / typeDefault
29+
type1 = 1 => tstr
30+
type2 = 2 => tstr
31+
typeDefault = int => nil
32+
33+
UnorderedMap3 = {
34+
1*2000 uint => uint,
35+
1*2000 nint => nint,
36+
}
37+
38+
group1 = (
39+
uint => tstr,
40+
nint => bstr,
41+
)
42+
type3 = 3*3group1
43+
44+
UnorderedMap4 = {
45+
5*5group1,
46+
}
47+
48+
UnorderedMap5 = {
49+
1 => bool,
50+
2 => {
51+
-1 => int,
52+
-2 => int,
53+
-3 => UnorderedMap2,
54+
*uint => bstr,
55+
},
56+
57+
}

tests/decode/test3_simple/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ set(py_command_serial_recovery
3535
-d
3636
${bit_arg}
3737
--short-names
38+
--unordered-maps
3839

3940
# Testing the --include-prefix option
4041
--include-prefix serial

tests/decode/test3_simple/src/main.c

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -487,19 +487,15 @@ ZTEST(cbor_decode_test3, test_serial1)
487487
zassert_equal(ZCBOR_SUCCESS, ret, "decoding failed: %d.", ret);
488488
zassert_equal(sizeof(serial_rec_input1), decode_len, NULL);
489489

490-
zassert_equal(5, upload.members_count,
491-
"expect 5 members");
492-
zassert_equal(Member_data_c, upload.members[0].members
493-
.Member_choice, "expect data 1st");
494-
zassert_equal(Member_image_c, upload.members[1].members
495-
.Member_choice, "expect image 2nd");
496-
zassert_equal(Member_len_c, upload.members[2].members
497-
.Member_choice, "was %d\r\n", upload.members[2].members
498-
.Member_choice);
499-
zassert_equal(Member_off_c, upload.members[3].members
500-
.Member_choice, "expect off 4th");
501-
zassert_equal(Member_sha_c, upload.members[4].members
502-
.Member_choice, "expect sha 5th");
490+
zassert_true(upload.data_present, NULL);
491+
zassert_equal(0x129, upload.data.data.len, NULL);
492+
zassert_true(upload.image_present, NULL);
493+
zassert_equal(0, upload.image.image, NULL);
494+
zassert_true(upload.len_present, NULL);
495+
zassert_equal(0x3b2c, upload.len.len, NULL);
496+
zassert_equal(0, upload.off, NULL);
497+
zassert_true(upload.sha_present, NULL);
498+
zassert_equal(0x20, upload.sha.sha.len, NULL);
503499
}
504500

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

514-
zassert_equal(5, upload.members_count,
515-
"expect 5 members");
516-
zassert_equal(Member_data_c, upload.members[0].members
517-
.Member_choice, "expect data 1st");
518-
zassert_equal(Member_image_c, upload.members[1].members
519-
.Member_choice, "expect image 2nd");
520-
zassert_equal(Member_len_c, upload.members[2].members
521-
.Member_choice, "expect len 3rd");
522-
zassert_equal(Member_off_c, upload.members[3].members
523-
.Member_choice, "expect off 4th");
524-
zassert_equal(Member_sha_c, upload.members[4].members
525-
.Member_choice, "expect sha 5th");
510+
zassert_true(upload.data_present, NULL);
511+
zassert_equal(0x129, upload.data.data.len, NULL);
512+
zassert_true(upload.image_present, NULL);
513+
zassert_equal(0, upload.image.image, NULL);
514+
zassert_true(upload.len_present, NULL);
515+
zassert_equal(0x2fe0, upload.len.len, NULL);
516+
zassert_equal(0, upload.off, NULL);
517+
zassert_true(upload.sha_present, NULL);
518+
zassert_equal(0x20, upload.sha.sha.len, NULL);
526519
}
527520

528521
ZTEST_SUITE(cbor_decode_test3, NULL, NULL, NULL, NULL, NULL);

tests/decode/test9_manifest14/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ cmake_minimum_required(VERSION 3.13.1)
88

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

1314
if (NOT MANIFEST)
@@ -30,6 +31,7 @@ set(py_command
3031
SUIT_Common_Sequence
3132
-d
3233
${bit_arg}
34+
--unordered-maps
3335
)
3436

3537
execute_process(

0 commit comments

Comments
 (0)