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

Fletcher32: Add incremental API #19981

Merged
merged 2 commits into from
Oct 20, 2023
Merged
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
40 changes: 31 additions & 9 deletions sys/checksum/fletcher32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
43 changes: 41 additions & 2 deletions sys/include/checksum/fletcher32.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*
* @file
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
* @author Koen Zandberg <koen@bergzand.net>
*/

#ifndef CHECKSUM_FLETCHER32_H
Expand All @@ -27,22 +28,60 @@
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
*
* found on
* http://en.wikipedia.org/w/index.php?title=Fletcher%27s_checksum&oldid=661273016#Optimizations
*
* @note the returned sum is never 0
* @note pay attention to alignment issues since this operates on an input
* buffer containing 16 bit words, not bytes.
* @note pay attention to the @p words parameter buffer containing 16 bit words, not bytes.
*
* @param buf input buffer to hash
* @param words length of buffer, in 16 bit words
* @return 32 bit sized hash in the interval [1..2^32]
*/
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
Expand Down
15 changes: 15 additions & 0 deletions tests/unittests/tests-checksum/tests-checksum-fletcher32.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,28 @@ 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) {
new_TestFixture(test_checksum_fletcher32_empty),
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);
Expand Down