From 31df2ba0aa80a19d84b03ae2e333ac512cc6e1d3 Mon Sep 17 00:00:00 2001 From: Koen Zandberg Date: Sat, 17 Sep 2022 22:28:01 +0200 Subject: [PATCH] Fletcher32: Add incremental API --- sys/checksum/fletcher32.c | 40 ++++++++++++++----- sys/include/checksum/fletcher32.h | 40 +++++++++++++++++++ .../tests-checksum-fletcher32.c | 15 +++++++ 3 files changed, 86 insertions(+), 9 deletions(-) diff --git a/sys/checksum/fletcher32.c b/sys/checksum/fletcher32.c index 5bbd306f3d..00babde6cc 100644 --- a/sys/checksum/fletcher32.c +++ b/sys/checksum/fletcher32.c @@ -21,21 +21,43 @@ #include "unaligned.h" #include "checksum/fletcher32.h" -uint32_t fletcher32(const uint16_t *data, size_t words) +static inline void _reduce(fletcher32_ctx_t *ctx) { - uint32_t sum1 = 0xffff, sum2 = 0xffff; + ctx->sum1 = (ctx->sum1 & 0xffff) + (ctx->sum1 >> 16); + ctx->sum2 = (ctx->sum2 & 0xffff) + (ctx->sum2 >> 16); +} +void fletcher32_init(fletcher32_ctx_t *ctx) +{ + ctx->sum1 = 0xffff; + ctx->sum2 = 0xffff; +} + +uint32_t fletcher32_finish(fletcher32_ctx_t *ctx) +{ + /* Second reduction step to reduce sums to 8 bits */ + _reduce(ctx); + return (ctx->sum2 << 16) | ctx->sum1; +} + +void fletcher32_update(fletcher32_ctx_t *ctx, const void *data, size_t words) +{ + const uint16_t *u16_data = (const uint16_t*)data; while (words) { unsigned tlen = words > 359 ? 359 : words; words -= tlen; do { - sum2 += sum1 += unaligned_get_u16(data++); + ctx->sum1 += unaligned_get_u16(u16_data++); + ctx->sum2 += ctx->sum1; } while (--tlen); - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); + _reduce(ctx); } - /* Second reduction step to reduce sums to 16 bits */ - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - return (sum2 << 16) | sum1; +} + +uint32_t fletcher32(const uint16_t *data, size_t words) +{ + fletcher32_ctx_t ctx; + fletcher32_init(&ctx); + fletcher32_update(&ctx, data, words); + return fletcher32_finish(&ctx); } diff --git a/sys/include/checksum/fletcher32.h b/sys/include/checksum/fletcher32.h index 8f23851e97..01fc08dfcb 100644 --- a/sys/include/checksum/fletcher32.h +++ b/sys/include/checksum/fletcher32.h @@ -15,6 +15,7 @@ * * @file * @author Joakim NohlgÄrd + * @author Koen Zandberg */ #ifndef CHECKSUM_FLETCHER32_H @@ -27,6 +28,14 @@ extern "C" { #endif +/** + * @brief Fletcher's 32 bit checksum context struct + */ +typedef struct { + uint32_t sum1; /**< First sum of the checksum */ + uint32_t sum2; /**< Second sum of the checksum */ +} fletcher32_ctx_t; + /** * @brief Fletcher's 32 bit checksum * @@ -43,6 +52,37 @@ extern "C" { */ uint32_t fletcher32(const uint16_t *buf, size_t words); +/** + * @brief Initialize a fletcher32 context + * + * Multi-part version of @ref fletcher32. + * + * @param[in] ctx fletcher32 context to initialize + */ +void fletcher32_init(fletcher32_ctx_t *ctx); + +/** + * @brief Incrementally update the fletcher32 context with new data. Can be an arbitrary amount of + * times with new data to checksum. + * + * @note @p words is the number of 16 bit words in the buffer + * @note @p data should contain an integer number of 16 bit words + * + * @param[in] ctx fletcher32 context + * @param[in] data Data to add to the context + * @param[in] words Length of the data in 16 bit words + */ +void fletcher32_update(fletcher32_ctx_t *ctx, const void *data, size_t words); + +/** + * @brief Finalize the checksum operation and return the checksum + * + * @param[in] ctx fletcher32 context + * + * @return 32 bit sized hash in the interval [1..2^32] + */ +uint32_t fletcher32_finish(fletcher32_ctx_t *ctx); + #ifdef __cplusplus } #endif diff --git a/tests/unittests/tests-checksum/tests-checksum-fletcher32.c b/tests/unittests/tests-checksum/tests-checksum-fletcher32.c index 38dcba750b..cd3ed5cd3a 100644 --- a/tests/unittests/tests-checksum/tests-checksum-fletcher32.c +++ b/tests/unittests/tests-checksum/tests-checksum-fletcher32.c @@ -85,6 +85,20 @@ static void test_checksum_fletcher32_wrap_around(void) sizeof(wrap_around_data) - 1, expect)); } +static void test_checksum_fletcher32_wrap_around_piecewise(void) +{ + /* XXX: not verified with external implementation yet */ + uint32_t expect = 0x5bac8c3d; + fletcher32_ctx_t ctx; + fletcher32_init(&ctx); + size_t full_len = sizeof(wrap_around_data) - 1; + size_t initial_len = full_len / 2; + fletcher32_update(&ctx, wrap_around_data, initial_len / 2); + fletcher32_update(&ctx, (wrap_around_data + initial_len), (full_len - initial_len) / 2); + uint32_t result = fletcher32_finish(&ctx); + TEST_ASSERT_EQUAL_INT(result, expect); +} + Test *tests_checksum_fletcher32_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -92,6 +106,7 @@ Test *tests_checksum_fletcher32_tests(void) new_TestFixture(test_checksum_fletcher32_0to1_undetected), new_TestFixture(test_checksum_fletcher32_atof), new_TestFixture(test_checksum_fletcher32_wrap_around), + new_TestFixture(test_checksum_fletcher32_wrap_around_piecewise), }; EMB_UNIT_TESTCALLER(checksum_fletcher32_tests, NULL, NULL, fixtures);