Skip to content

Commit

Permalink
zcbor_common.h: Introduce the ZCBOR_CAST_FP() macro
Browse files Browse the repository at this point in the history
for casting function pointers.

Signed-off-by: Øyvind Rønningstad <[email protected]>
  • Loading branch information
oyvindronningstad committed Jan 10, 2025
1 parent 2b837d1 commit 33d9442
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 26 deletions.
3 changes: 3 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# zcbor v. 0.9.99

* [Recommended] A new macro `ZCBOR_CAST_FP` has been added for casting function pointers for use in the zcbor API.
The macro will first check that the function pointer has one of the supported signatures.


# zcbor v. 0.9.0

Expand Down
49 changes: 49 additions & 0 deletions include/zcbor_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,55 @@ static inline size_t zcbor_flags_to_bytes(size_t num_flags)

size_t strnlen(const char *, size_t);

/** Add some additional safety when casting to zcbor_decoder_t or zcbor_encoder_t
*
* This will fail if trying to cast functions from the zcbor API that should not be used
* as zcbor_decoder_t or zcbor_encoder_t, such as zcbor_decode_int() (3 arguments), or
* zcbor_int32_put() (result is not a pointer).
*
* _Generic is assumed to be supported in newer copilers, even when using the C99 flag.
*
* To add support for casting a custom function foo_decode() with its own result struct,
* you can do something like the following:
*
* #define ZCBOR_CAST_FP_CUSTOM(func) _Generic((func), \
* bool(*)(zcbor_state_t *, foo_t *): ((zcbor_decoder_t *)func), \
* bool(*)(zcbor_state_t *, const foo_t *): ((zcbor_encoder_t *)func), \
* default: ZCBOR_CAST_FP(func))
*
* @param func Function pointer to cast.
* @retval The result of the macro will be func, cast to either zcbor_decoder_t or
* zcbor_encoder_t depending on whether the result argument is const.
*/
#define ZCBOR_CAST_FP(func) _Generic((func), \
bool(*)(zcbor_state_t *, void *): func, \
bool(*)(zcbor_state_t *, int8_t *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, int16_t *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, int32_t *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, int64_t *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, uint8_t *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, uint16_t *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, uint32_t *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, uint64_t *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, bool *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, float *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, double *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, struct zcbor_string *): ((zcbor_decoder_t *)func), \
bool(*)(zcbor_state_t *, const void *): func, \
bool(*)(zcbor_state_t *, const int8_t *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const int16_t *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const int32_t *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const int64_t *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const uint8_t *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const uint16_t *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const uint32_t *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const uint64_t *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const bool *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const float *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const double *): ((zcbor_encoder_t *)func), \
bool(*)(zcbor_state_t *, const struct zcbor_string *): ((zcbor_encoder_t *)func) \
)

#ifdef __cplusplus
}
#endif
Expand Down
6 changes: 6 additions & 0 deletions include/zcbor_decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ bool zcbor_unordered_map_start_decode(zcbor_state_t *state);
* keys in the map until it returns true, at which point
* @ref zcbor_unordered_map_search will return true.
* For example, a zcbor_*_pexpect() function.
* Please use @ref ZCBOR_CAST_FP to cast the function
* since it will also check for compatibility.
* @param[inout] state The current state of decoding. Must be currently decoding
* the contents of a map, and pointing to one (any) of the
* keys, not one of the values. If successful, the @p state
Expand Down Expand Up @@ -316,6 +318,8 @@ bool zcbor_any_skip(zcbor_state_t *state, void *unused);
* an array of result variables.
* Should not be an _expect() function, use
* _pexpect() instead.
* Please use @ref ZCBOR_CAST_FP to cast the function
* since it will also check for compatibility.
* @param[out] result Where to place the decoded values. Must be an array
* of at least @p max_decode elements.
* @param[in] result_len The length of each result variable. Must be the
Expand All @@ -336,6 +340,8 @@ bool zcbor_multi_decode(size_t min_decode, size_t max_decode, size_t *num_decode
*
* @param[out] present Whether or not the data was present and successfully decoded.
* @param[in] decoder The decoder to attempt.
* Please use @ref ZCBOR_CAST_FP to cast the function
* since it will also check for compatibility.
* @param[out] result The result, if present.
*
* @return Should always return true.
Expand Down
2 changes: 2 additions & 0 deletions include/zcbor_encode.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ bool zcbor_list_map_end_force_encode(zcbor_state_t *state);
* The input pointer is moved @p input_len bytes for
* each call to @p encoder, i.e. @p input refers to an
* array of input variables.
* Please use @ref ZCBOR_CAST_FP to cast the function
* since it will also check for compatibility.
* @param[in] input Source of the encoded values. Must be an array of
* at least @p max_encode elements.
* @param[in] input_len The length of the input variables. Must be the
Expand Down
53 changes: 27 additions & 26 deletions tests/unit/test1_unit_tests/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ ZTEST(zcbor_unit_tests, test_stop_on_error)
zassert_false(zcbor_map_start_encode(state_e, 0), NULL);
zassert_false(zcbor_map_end_encode(state_e, 0), NULL);
zassert_false(zcbor_list_end_encode(state_e, 1), NULL);
zassert_false(zcbor_multi_encode(1, (zcbor_encoder_t *)zcbor_int32_put, state_e, (void*)14, 0), NULL);
zassert_false(zcbor_multi_encode_minmax(1, 1, &(size_t){1}, (zcbor_encoder_t *)zcbor_int32_put, state_e, (void*)15, 0), NULL);
zassert_false(zcbor_multi_encode(1, ZCBOR_CAST_FP(zcbor_int32_encode), state_e, &(int32_t){14}, 0), NULL);
zassert_false(zcbor_multi_encode_minmax(1, 1, &(size_t){1}, ZCBOR_CAST_FP(zcbor_int32_encode), state_e, &(int32_t){15}, 0), NULL);


zassert_mem_equal(&state_backup, state_e, sizeof(state_backup), NULL);
Expand Down Expand Up @@ -325,8 +325,8 @@ ZTEST(zcbor_unit_tests, test_stop_on_error)
zassert_true(zcbor_map_start_encode(state_e, 0), NULL);
zassert_true(zcbor_map_end_encode(state_e, 0), NULL);
zassert_true(zcbor_list_end_encode(state_e, 1), NULL);
zassert_true(zcbor_multi_encode(1, (zcbor_encoder_t *)zcbor_int32_put, state_e, (void*)14, 0), NULL);
zassert_true(zcbor_multi_encode_minmax(1, 1, &(size_t){1}, (zcbor_encoder_t *)zcbor_int32_put, state_e, (void*)15, 0), NULL);
zassert_true(zcbor_multi_encode(1, ZCBOR_CAST_FP(zcbor_int32_encode), state_e, &(int32_t){14}, 0), NULL);
zassert_true(zcbor_multi_encode_minmax(1, 1, &(size_t){1}, ZCBOR_CAST_FP(zcbor_int32_encode), state_e, &(int32_t){15}, 0), NULL);

ZCBOR_STATE_D(state_d, 3, payload, sizeof(payload), 50, 0);
state_d->constant_state->stop_on_error = true;
Expand Down Expand Up @@ -374,8 +374,8 @@ ZTEST(zcbor_unit_tests, test_stop_on_error)
zassert_false(zcbor_map_start_decode(state_d), NULL);
zassert_false(zcbor_map_end_decode(state_d), NULL);
zassert_false(zcbor_list_end_decode(state_d), NULL);
zassert_false(zcbor_multi_decode(1, 1, &(size_t){1}, (zcbor_decoder_t *)zcbor_int32_expect, state_d, (void*)14, 0), NULL);
zassert_false(zcbor_present_decode(&(bool){true}, (zcbor_decoder_t *)zcbor_int32_expect, state_d, (void*)15), NULL);
zassert_false(zcbor_multi_decode(1, 1, &(size_t){1}, ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){14}, 0), NULL);
zassert_false(zcbor_present_decode(&(bool){true}, ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){15}), NULL);

zassert_mem_equal(&state_backup, state_d, sizeof(state_backup), NULL);
zassert_mem_equal(&constant_state_backup, state_d->constant_state, sizeof(constant_state_backup), NULL);
Expand Down Expand Up @@ -420,8 +420,8 @@ ZTEST(zcbor_unit_tests, test_stop_on_error)
zassert_true(zcbor_map_start_decode(state_d), NULL);
zassert_true(zcbor_map_end_decode(state_d), NULL);
zassert_true(zcbor_list_end_decode(state_d), NULL);
zassert_true(zcbor_multi_decode(1, 1, &(size_t){1}, (zcbor_decoder_t *)zcbor_int32_expect, state_d, (void*)14, 0), NULL);
zassert_true(zcbor_present_decode(&(bool){1}, (zcbor_decoder_t *)zcbor_int32_expect, state_d, (void*)15), NULL);
zassert_true(zcbor_multi_decode(1, 1, &(size_t){1}, ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){14}, 0), NULL);
zassert_true(zcbor_present_decode(&(bool){1}, ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){15}), NULL);

/* Everything has been decoded. */
zassert_equal(state_e->payload, state_d->payload, NULL);
Expand Down Expand Up @@ -1181,10 +1181,10 @@ ZTEST(zcbor_unit_tests, test_pexpect)
void decode_inner_map(zcbor_state_t *state, void *result)
{
zassert_true(zcbor_unordered_map_start_decode(state), NULL);
zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_bool_pexpect, state, &(bool){false}), NULL);
zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_bool_pexpect), state, &(bool){false}), NULL);
zassert_true(zcbor_undefined_expect(state, NULL), NULL);
zcbor_elem_processed(state);
zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_tstr_expect, state, &(struct zcbor_string){"hello", 5}), NULL);
zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_tstr_expect), state, &(struct zcbor_string){"hello", 5}), NULL);
zassert_true(zcbor_tstr_expect(state, &(struct zcbor_string){"world", 5}), NULL);
zcbor_elem_processed(state);
bool ret = zcbor_unordered_map_end_decode(state);
Expand Down Expand Up @@ -1263,15 +1263,15 @@ ZTEST(zcbor_unit_tests, test_unordered_map)

/* Test empty map */
zassert_true(zcbor_unordered_map_start_decode(state_d), NULL);
zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){2}), NULL);
zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){2}), NULL);
zassert_equal(ZCBOR_ERR_ELEM_NOT_FOUND, zcbor_peek_error(state_d), "err: %d\n", zcbor_peek_error(state_d));
ret = zcbor_unordered_map_end_decode(state_d);
zassert_true(ret, "err: %d\n", zcbor_peek_error(state_d));
zassert_equal(start2, state_d->payload, NULL);

/* Test single entry map */
zassert_true(zcbor_unordered_map_start_decode(state_d), NULL);
ret = zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){1});
ret = zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){1});
zassert_true(ret, "err: %d\n", zcbor_peek_error);
zassert_true(zcbor_int32_expect(state_d, 1), NULL);
ret = zcbor_unordered_map_end_decode(state_d);
Expand All @@ -1281,15 +1281,15 @@ ZTEST(zcbor_unit_tests, test_unordered_map)
/* Test that looping stops both if it starts at the very start and very end of the map.
* Also test ZCBOR_ERR_MAP_MISALIGNED. */
zassert_true(zcbor_unordered_map_start_decode(state_d2), NULL);
zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){3}), NULL);
ret = zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){2});
zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){3}), NULL);
ret = zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){2});
zassert_true(ret, "err: %d\n", zcbor_peek_error(state_d2));
zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){1}), NULL);
zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){1}), NULL);
zassert_equal(zcbor_peek_error(state_d2), ZCBOR_ERR_MAP_MISALIGNED, NULL);
zassert_true(zcbor_int32_expect(state_d2, 2), NULL);
zassert_true(zcbor_array_at_end(state_d2), NULL);
zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){3}), NULL);
zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d2, &(int32_t){1}), NULL);
zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){3}), NULL);
zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d2, &(int32_t){1}), NULL);
zassert_true(zcbor_int32_expect(state_d2, 1), NULL);
ret = zcbor_unordered_map_end_decode(state_d2);
zassert_true(ret, NULL);
Expand All @@ -1300,7 +1300,7 @@ ZTEST(zcbor_unit_tests, test_unordered_map)
zassert_false(zcbor_unordered_map_start_decode(state_d3), NULL);
#else
zassert_true(zcbor_unordered_map_start_decode(state_d3), NULL);
zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d3, &(int32_t){2}), NULL);
zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d3, &(int32_t){2}), NULL);
#endif
zassert_equal(zcbor_peek_error(state_d3), ZCBOR_ERR_MAP_FLAGS_NOT_AVAILABLE, NULL);
#endif
Expand All @@ -1313,17 +1313,17 @@ ZTEST(zcbor_unit_tests, test_unordered_map)
#ifndef ZCBOR_CANONICAL
zcbor_elem_processed(state_d); // Should do nothing because no elements have been discovered.
#endif
zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){1}), NULL);
zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){1}), NULL);
zassert_true(zcbor_int32_expect(state_d, 1), NULL);
ret = zcbor_unordered_map_end_decode(state_d);
zassert_false(ret, NULL);
zassert_equal(ZCBOR_ERR_ELEMS_NOT_PROCESSED, zcbor_peek_error(state_d), NULL);
/* Cause a restart of the map */
zassert_false(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){3}), NULL);
zassert_false(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){3}), NULL);
ret = zcbor_unordered_map_end_decode(state_d);
zassert_false(ret, NULL);
zassert_equal(ZCBOR_ERR_ELEMS_NOT_PROCESSED, zcbor_peek_error(state_d), NULL);
zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){2}), NULL);
zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){2}), NULL);
zassert_true(zcbor_int32_expect(state_d, 2), NULL);
ret = zcbor_unordered_map_end_decode(state_d);
zassert_true(ret, "err: %d\n", zcbor_peek_error(state_d));
Expand All @@ -1332,18 +1332,19 @@ ZTEST(zcbor_unit_tests, test_unordered_map)
/* Test a large map, including nesting. */
state_d->constant_state->manually_process_elem = true;
zassert_true(zcbor_unordered_map_start_decode(state_d), NULL);
ret = zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){-1});
ret = zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){-1});
zassert_true(ret, "err: %d\n", zcbor_peek_error(state_d));
zassert_true(zcbor_tstr_decode(state_d, &str_result1), NULL);
zcbor_elem_processed(state_d);

zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_bool_pexpect, state_d, &(bool){true}), NULL);
zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_bool_pexpect), state_d, &(bool){true}), NULL);
zassert_true(zcbor_tstr_decode(state_d, &str_result2), NULL);
zcbor_elem_processed(state_d);
zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){2}), NULL);
zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){2}), NULL);
zassert_true(zcbor_int32_decode(state_d, &int_result1), NULL);
zcbor_elem_processed(state_d);
zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &(int32_t){1}), NULL);
ret = zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &(int32_t){1});
zassert_true(ret, "%s\n", zcbor_error_str(zcbor_peek_error(state_d)));
zassert_true(zcbor_tstr_decode(state_d, &str_result3), NULL);
zcbor_elem_processed(state_d);

Expand Down Expand Up @@ -1383,7 +1384,7 @@ ZTEST(zcbor_unit_tests, test_unordered_map)
zcbor_elem_processed(state_d);

for (size_t i = 34; i > 2; i--) {
zassert_true(zcbor_unordered_map_search((zcbor_decoder_t *)zcbor_int32_pexpect, state_d, &i), "i: %d, err: %d\n", i, zcbor_peek_error(state_d));
zassert_true(zcbor_unordered_map_search(ZCBOR_CAST_FP(zcbor_int32_pexpect), state_d, &i), "i: %d, err: %d\n", i, zcbor_peek_error(state_d));
zassert_false(zcbor_int32_expect(state_d, i), NULL);
zassert_true(zcbor_int32_expect(state_d, -1 * i), NULL);
zcbor_elem_processed(state_d);
Expand Down

0 comments on commit 33d9442

Please sign in to comment.