diff --git a/sys/checksum/fletcher16.c b/sys/checksum/fletcher16.c index 6b8a62777d..2144a3e2b4 100644 --- a/sys/checksum/fletcher16.c +++ b/sys/checksum/fletcher16.c @@ -20,21 +20,41 @@ #include "checksum/fletcher16.h" +static inline void _reduce(fletcher16_ctx_t *ctx) +{ + ctx->sum1 = (ctx->sum1 & 0xff) + (ctx->sum1 >> 8); + ctx->sum2 = (ctx->sum2 & 0xff) + (ctx->sum2 >> 8); +} + +void fletcher16_init(fletcher16_ctx_t *ctx) +{ + ctx->sum1 = 0xff; + ctx->sum2 = 0xff; +} + +void fletcher16_update(fletcher16_ctx_t *ctx, const uint8_t *data, size_t len) +{ + while (len) { + size_t tlen = len > 20 ? 20 : len; + len -= tlen; + do { + ctx->sum2 += ctx->sum1 += *data++; + } while (--tlen); + _reduce(ctx); + } +} + +uint16_t fletcher16_finish(fletcher16_ctx_t *ctx) +{ + /* Second reduction step to reduce sums to 8 bits */ + _reduce(ctx); + return (ctx->sum2 << 8) | ctx->sum1; +} + uint16_t fletcher16(const uint8_t *data, size_t bytes) { - uint16_t sum1 = 0xff, sum2 = 0xff; - - while (bytes) { - size_t tlen = bytes > 20 ? 20 : bytes; - bytes -= tlen; - do { - sum2 += sum1 += *data++; - } while (--tlen); - sum1 = (sum1 & 0xff) + (sum1 >> 8); - sum2 = (sum2 & 0xff) + (sum2 >> 8); - } - /* Second reduction step to reduce sums to 8 bits */ - sum1 = (sum1 & 0xff) + (sum1 >> 8); - sum2 = (sum2 & 0xff) + (sum2 >> 8); - return (sum2 << 8) | sum1; + fletcher16_ctx_t ctx; + fletcher16_init(&ctx); + fletcher16_update(&ctx, data, bytes); + return fletcher16_finish(&ctx); } diff --git a/sys/include/checksum/fletcher16.h b/sys/include/checksum/fletcher16.h index fc8b8e1ea0..200a9e670a 100644 --- a/sys/include/checksum/fletcher16.h +++ b/sys/include/checksum/fletcher16.h @@ -27,8 +27,17 @@ extern "C" { #endif +/** + * @brief Fletcher's 16 bit checksum context struct + */ +typedef struct { + uint16_t sum1; /**< First sum of the checksum */ + uint16_t sum2; /**< Second sum of the checksum */ +} fletcher16_ctx_t; + /** * @brief Fletcher's 16 bit checksum + * @anchor fletcher16_full * * found on * http://en.wikipedia.org/w/index.php?title=Fletcher%27s_checksum&oldid=661273016#Optimizations @@ -41,6 +50,34 @@ extern "C" { */ uint16_t fletcher16(const uint8_t *buf, size_t bytes); +/** + * @brief Initialize a fletcher16 context + * + * Multi-part version of @ref fletcher16_full. + * + * @param[in] ctx fletcher16 context to initialize + */ +void fletcher16_init(fletcher16_ctx_t *ctx); + +/** + * @brief Update the fletcher16 context with new data + * + * @param[in] ctx fletcher16 context + * @param[in] data Data to add to the context + * @param[in] len Length of the data + */ +void fletcher16_update(fletcher16_ctx_t *ctx, const uint8_t *data, size_t len); + +/** + * @brief Finalize the checksum operation and return the checksum + * + * @param[in] ctx fletcher16 context + * + * @return Checksum of the data + */ +uint16_t fletcher16_finish(fletcher16_ctx_t *ctx); + + #ifdef __cplusplus } diff --git a/tests/unittests/tests-checksum/tests-checksum-fletcher16.c b/tests/unittests/tests-checksum/tests-checksum-fletcher16.c index 9309bb2176..935e8559d6 100644 --- a/tests/unittests/tests-checksum/tests-checksum-fletcher16.c +++ b/tests/unittests/tests-checksum/tests-checksum-fletcher16.c @@ -72,6 +72,19 @@ static void test_checksum_fletcher16_wrap_around(void) sizeof(wrap_around_data) - 1, expect)); } +static void test_checksum_fletcher16_multipart(void) +{ + static const uint16_t expect = 0xf122; + static const size_t half_len = sizeof(wrap_around_data)/2; + fletcher16_ctx_t ctx; + + fletcher16_init(&ctx); + fletcher16_update(&ctx, wrap_around_data, half_len); + fletcher16_update(&ctx, wrap_around_data + half_len, + sizeof(wrap_around_data) - half_len - 1); + TEST_ASSERT_EQUAL_INT(expect, fletcher16_finish(&ctx)); +} + Test *tests_checksum_fletcher16_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -79,6 +92,7 @@ Test *tests_checksum_fletcher16_tests(void) new_TestFixture(test_checksum_fletcher16_0to1_undetected), new_TestFixture(test_checksum_fletcher16_atoe), new_TestFixture(test_checksum_fletcher16_wrap_around), + new_TestFixture(test_checksum_fletcher16_multipart), }; EMB_UNIT_TESTCALLER(checksum_fletcher16_tests, NULL, NULL, fixtures);