From ec3e191ab825c902777ee8a22de929f3a5a68ccd Mon Sep 17 00:00:00 2001 From: Michael Graeb Date: Thu, 2 Apr 2020 12:45:29 -0700 Subject: [PATCH] aws_hash_combine() (#618) * aws_hash_combine() * remove type-punning * Feedback. Unused params fix. * clearer bit-shuffling intent --- include/aws/common/hash_table.h | 3 +++ source/hash_table.c | 8 ++++++++ tests/CMakeLists.txt | 1 + tests/hash_table_test.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/include/aws/common/hash_table.h b/include/aws/common/hash_table.h index 0a6d1c006..7cf429a9d 100644 --- a/include/aws/common/hash_table.h +++ b/include/aws/common/hash_table.h @@ -394,6 +394,9 @@ uint64_t aws_hash_byte_cursor_ptr(const void *item); AWS_COMMON_API uint64_t aws_hash_ptr(const void *item); +AWS_COMMON_API +uint64_t aws_hash_combine(uint64_t item1, uint64_t item2); + /** * Convenience eq callback for NULL-terminated C-strings */ diff --git a/source/hash_table.c b/source/hash_table.c index cbe3cc3e9..1ef1ed8e5 100644 --- a/source/hash_table.c +++ b/source/hash_table.c @@ -991,6 +991,14 @@ uint64_t aws_hash_ptr(const void *item) { return ((uint64_t)b << 32) | c; } +uint64_t aws_hash_combine(uint64_t item1, uint64_t item2) { + uint32_t b = item2 & 0xFFFFFFFF; /* LSB */ + uint32_t c = item2 >> 32; /* MSB */ + + hashlittle2(&item1, sizeof(item1), &c, &b); + return ((uint64_t)b << 32) | c; +} + bool aws_hash_callback_c_str_eq(const void *a, const void *b) { AWS_PRECONDITION(aws_c_string_is_valid(a)); AWS_PRECONDITION(aws_c_string_is_valid(b)); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 33f854da2..50a614ccc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -167,6 +167,7 @@ add_test_case(test_hash_table_eq) add_test_case(test_hash_churn) add_test_case(test_hash_table_cleanup_idempotent) add_test_case(test_hash_table_byte_cursor_create_find) +add_test_case(test_hash_combine) add_test_case(test_is_power_of_two) add_test_case(test_round_up_to_power_of_two) diff --git a/tests/hash_table_test.c b/tests/hash_table_test.c index d5e1dbf2c..d7bf5ba23 100644 --- a/tests/hash_table_test.c +++ b/tests/hash_table_test.c @@ -1258,3 +1258,34 @@ static int s_test_hash_table_byte_cursor_create_find_fn(struct aws_allocator *al return 0; } + +AWS_TEST_CASE(test_hash_combine, s_test_hash_combine_fn) +static int s_test_hash_combine_fn(struct aws_allocator *allocator, void *ctx) { + (void)allocator; + (void)ctx; + + /* We're assuming that the underlying hashing function works well. + * This test just makes sure we hooked it up right for 2 64bit values */ + + uint64_t a = 0x123456789abcdef; + uint64_t b = 0xfedcba987654321; + uint64_t c = aws_hash_combine(a, b); + + /* Sanity check */ + ASSERT_TRUE(c != a); + ASSERT_TRUE(c != b); + + /* Same inputs gets same results, right? */ + ASSERT_UINT_EQUALS(c, aws_hash_combine(a, b)); + + /* Result spread across all bytes, right? */ + uint8_t *c_bytes = (uint8_t *)&c; + for (size_t i = 0; i < sizeof(c); ++i) { + ASSERT_TRUE(c_bytes[i] != 0); + } + + /* Hash should NOT be commutative */ + ASSERT_TRUE(aws_hash_combine(a, b) != aws_hash_combine(b, a)); + + return 0; +}