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_common.h: Introduce the ZCBOR_CAST_FP() macro #473

Open
wants to merge 1 commit 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
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
Loading