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 cf51895
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 26 deletions.
4 changes: 4 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# 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.
You are recommended to use it on any function pointer that is passed as an argument to a zcbor function.


# 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 cf51895

Please sign in to comment.