summaryrefslogtreecommitdiffstats
path: root/contrib/restricted/aws/aws-c-common/source/hash_table.c
diff options
context:
space:
mode:
authorunril <[email protected]>2022-02-10 16:46:05 +0300
committerDaniil Cherednik <[email protected]>2022-02-10 16:46:05 +0300
commit11ae9eca250d0188b7962459cbc6706719e7dca9 (patch)
tree4b7d6755091980d33210de19b2eb35a401a761ea /contrib/restricted/aws/aws-c-common/source/hash_table.c
parent9c914f41ba5e9f9365f404e892197553ac23809e (diff)
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/restricted/aws/aws-c-common/source/hash_table.c')
-rw-r--r--contrib/restricted/aws/aws-c-common/source/hash_table.c1548
1 files changed, 774 insertions, 774 deletions
diff --git a/contrib/restricted/aws/aws-c-common/source/hash_table.c b/contrib/restricted/aws/aws-c-common/source/hash_table.c
index a8125a2df11..e59a30db183 100644
--- a/contrib/restricted/aws/aws-c-common/source/hash_table.c
+++ b/contrib/restricted/aws/aws-c-common/source/hash_table.c
@@ -1,57 +1,57 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
- */
-
-/* For more information on how the RH hash works and in particular how we do
- * deletions, see:
- * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
- */
-
-#include <aws/common/hash_table.h>
-#include <aws/common/math.h>
-#include <aws/common/private/hash_table_impl.h>
-#include <aws/common/string.h>
-
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-/* Include lookup3.c so we can (potentially) inline it and make use of the mix()
- * macro. */
+ */
+
+/* For more information on how the RH hash works and in particular how we do
+ * deletions, see:
+ * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
+ */
+
+#include <aws/common/hash_table.h>
+#include <aws/common/math.h>
+#include <aws/common/private/hash_table_impl.h>
+#include <aws/common/string.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Include lookup3.c so we can (potentially) inline it and make use of the mix()
+ * macro. */
#include <aws/common/private/lookup3.inl>
-
-static void s_suppress_unused_lookup3_func_warnings(void) {
- /* We avoid making changes to lookup3 if we can avoid it, but since it has functions
- * we're not using, reference them somewhere to suppress the unused function warning.
- */
- (void)hashword;
- (void)hashword2;
- (void)hashlittle;
- (void)hashbig;
-}
-
+
+static void s_suppress_unused_lookup3_func_warnings(void) {
+ /* We avoid making changes to lookup3 if we can avoid it, but since it has functions
+ * we're not using, reference them somewhere to suppress the unused function warning.
+ */
+ (void)hashword;
+ (void)hashword2;
+ (void)hashlittle;
+ (void)hashbig;
+}
+
/**
* Calculate the hash for the given key.
* Ensures a reasonable semantics for null keys.
* Ensures that no object ever hashes to 0, which is the sentinal value for an empty hash element.
*/
-static uint64_t s_hash_for(struct hash_table_state *state, const void *key) {
+static uint64_t s_hash_for(struct hash_table_state *state, const void *key) {
AWS_PRECONDITION(hash_table_state_is_valid(state));
- s_suppress_unused_lookup3_func_warnings();
-
+ s_suppress_unused_lookup3_func_warnings();
+
if (key == NULL) {
/* The best answer */
return 42;
}
- uint64_t hash_code = state->hash_fn(key);
- if (!hash_code) {
- hash_code = 1;
- }
+ uint64_t hash_code = state->hash_fn(key);
+ if (!hash_code) {
+ hash_code = 1;
+ }
AWS_RETURN_WITH_POSTCONDITION(hash_code, hash_code != 0);
-}
-
+}
+
/**
* Check equality of two objects, with a reasonable semantics for null.
*/
@@ -77,270 +77,270 @@ static bool s_hash_keys_eq(struct hash_table_state *state, const void *a, const
AWS_RETURN_WITH_POSTCONDITION(rval, hash_table_state_is_valid(state));
}
-static size_t s_index_for(struct hash_table_state *map, struct hash_table_entry *entry) {
- AWS_PRECONDITION(hash_table_state_is_valid(map));
- size_t index = entry - map->slots;
+static size_t s_index_for(struct hash_table_state *map, struct hash_table_entry *entry) {
+ AWS_PRECONDITION(hash_table_state_is_valid(map));
+ size_t index = entry - map->slots;
AWS_RETURN_WITH_POSTCONDITION(index, index < map->size && hash_table_state_is_valid(map));
-}
-
-#if 0
-/* Useful debugging code for anyone working on this in the future */
-static uint64_t s_distance(struct hash_table_state *state, int index) {
- return (index - state->slots[index].hash_code) & state->mask;
-}
-
-void hash_dump(struct aws_hash_table *tbl) {
- struct hash_table_state *state = tbl->p_impl;
-
- printf("Dumping hash table contents:\n");
-
- for (int i = 0; i < state->size; i++) {
- printf("%7d: ", i);
- struct hash_table_entry *e = &state->slots[i];
- if (!e->hash_code) {
- printf("EMPTY\n");
- } else {
- printf("k: %p v: %p hash_code: %lld displacement: %lld\n",
- e->element.key, e->element.value, e->hash_code,
- (i - e->hash_code) & state->mask);
- }
- }
-}
-#endif
-
-#if 0
-/* Not currently exposed as an API. Should we have something like this? Useful for benchmarks */
-AWS_COMMON_API
-void aws_hash_table_print_stats(struct aws_hash_table *table) {
- struct hash_table_state *state = table->p_impl;
- uint64_t total_disp = 0;
- uint64_t max_disp = 0;
-
- printf("\n=== Hash table statistics ===\n");
- printf("Table size: %zu/%zu (max load %zu, remaining %zu)\n", state->entry_count, state->size, state->max_load, state->max_load - state->entry_count);
- printf("Load factor: %02.2lf%% (max %02.2lf%%)\n",
- 100.0 * ((double)state->entry_count / (double)state->size),
- state->max_load_factor);
-
- for (size_t i = 0; i < state->size; i++) {
- if (state->slots[i].hash_code) {
- int displacement = distance(state, i);
- total_disp += displacement;
- if (displacement > max_disp) {
- max_disp = displacement;
- }
- }
- }
-
- size_t *disp_counts = calloc(sizeof(*disp_counts), max_disp + 1);
-
- for (size_t i = 0; i < state->size; i++) {
- if (state->slots[i].hash_code) {
- disp_counts[distance(state, i)]++;
- }
- }
-
- uint64_t median = 0;
- uint64_t passed = 0;
- for (uint64_t i = 0; i <= max_disp && passed < total_disp / 2; i++) {
- median = i;
- passed += disp_counts[i];
- }
-
- printf("Displacement statistics: Avg %02.2lf max %llu median %llu\n", (double)total_disp / (double)state->entry_count, max_disp, median);
- for (uint64_t i = 0; i <= max_disp; i++) {
- printf("Displacement %2lld: %zu entries\n", i, disp_counts[i]);
- }
- free(disp_counts);
- printf("\n");
-}
-#endif
-
-size_t aws_hash_table_get_entry_count(const struct aws_hash_table *map) {
- struct hash_table_state *state = map->p_impl;
- return state->entry_count;
-}
-
-/* Given a header template, allocates space for a hash table of the appropriate
- * size, and copies the state header into this allocated memory, which is
- * returned.
- */
-static struct hash_table_state *s_alloc_state(const struct hash_table_state *template) {
- size_t required_bytes;
- if (hash_table_state_required_bytes(template->size, &required_bytes)) {
- return NULL;
- }
-
- /* An empty slot has hashcode 0. So this marks all slots as empty */
- struct hash_table_state *state = aws_mem_calloc(template->alloc, 1, required_bytes);
-
- if (state == NULL) {
- return state;
- }
-
- *state = *template;
- return state;
-}
-
-/* Computes the correct size and max_load based on a requested size. */
-static int s_update_template_size(struct hash_table_state *template, size_t expected_elements) {
- size_t min_size = expected_elements;
-
- if (min_size < 2) {
- min_size = 2;
- }
-
- /* size is always a power of 2 */
- size_t size;
- if (aws_round_up_to_power_of_two(min_size, &size)) {
- return AWS_OP_ERR;
- }
-
- /* Update the template once we've calculated everything successfully */
- template->size = size;
- template->max_load = (size_t)(template->max_load_factor * (double)template->size);
- /* Ensure that there is always at least one empty slot in the hash table */
- if (template->max_load >= size) {
- template->max_load = size - 1;
- }
-
- /* Since size is a power of 2: (index & (size - 1)) == (index % size) */
- template->mask = size - 1;
-
- return AWS_OP_SUCCESS;
-}
-
-int aws_hash_table_init(
- struct aws_hash_table *map,
- struct aws_allocator *alloc,
- size_t size,
- aws_hash_fn *hash_fn,
- aws_hash_callback_eq_fn *equals_fn,
- aws_hash_callback_destroy_fn *destroy_key_fn,
- aws_hash_callback_destroy_fn *destroy_value_fn) {
+}
+
+#if 0
+/* Useful debugging code for anyone working on this in the future */
+static uint64_t s_distance(struct hash_table_state *state, int index) {
+ return (index - state->slots[index].hash_code) & state->mask;
+}
+
+void hash_dump(struct aws_hash_table *tbl) {
+ struct hash_table_state *state = tbl->p_impl;
+
+ printf("Dumping hash table contents:\n");
+
+ for (int i = 0; i < state->size; i++) {
+ printf("%7d: ", i);
+ struct hash_table_entry *e = &state->slots[i];
+ if (!e->hash_code) {
+ printf("EMPTY\n");
+ } else {
+ printf("k: %p v: %p hash_code: %lld displacement: %lld\n",
+ e->element.key, e->element.value, e->hash_code,
+ (i - e->hash_code) & state->mask);
+ }
+ }
+}
+#endif
+
+#if 0
+/* Not currently exposed as an API. Should we have something like this? Useful for benchmarks */
+AWS_COMMON_API
+void aws_hash_table_print_stats(struct aws_hash_table *table) {
+ struct hash_table_state *state = table->p_impl;
+ uint64_t total_disp = 0;
+ uint64_t max_disp = 0;
+
+ printf("\n=== Hash table statistics ===\n");
+ printf("Table size: %zu/%zu (max load %zu, remaining %zu)\n", state->entry_count, state->size, state->max_load, state->max_load - state->entry_count);
+ printf("Load factor: %02.2lf%% (max %02.2lf%%)\n",
+ 100.0 * ((double)state->entry_count / (double)state->size),
+ state->max_load_factor);
+
+ for (size_t i = 0; i < state->size; i++) {
+ if (state->slots[i].hash_code) {
+ int displacement = distance(state, i);
+ total_disp += displacement;
+ if (displacement > max_disp) {
+ max_disp = displacement;
+ }
+ }
+ }
+
+ size_t *disp_counts = calloc(sizeof(*disp_counts), max_disp + 1);
+
+ for (size_t i = 0; i < state->size; i++) {
+ if (state->slots[i].hash_code) {
+ disp_counts[distance(state, i)]++;
+ }
+ }
+
+ uint64_t median = 0;
+ uint64_t passed = 0;
+ for (uint64_t i = 0; i <= max_disp && passed < total_disp / 2; i++) {
+ median = i;
+ passed += disp_counts[i];
+ }
+
+ printf("Displacement statistics: Avg %02.2lf max %llu median %llu\n", (double)total_disp / (double)state->entry_count, max_disp, median);
+ for (uint64_t i = 0; i <= max_disp; i++) {
+ printf("Displacement %2lld: %zu entries\n", i, disp_counts[i]);
+ }
+ free(disp_counts);
+ printf("\n");
+}
+#endif
+
+size_t aws_hash_table_get_entry_count(const struct aws_hash_table *map) {
+ struct hash_table_state *state = map->p_impl;
+ return state->entry_count;
+}
+
+/* Given a header template, allocates space for a hash table of the appropriate
+ * size, and copies the state header into this allocated memory, which is
+ * returned.
+ */
+static struct hash_table_state *s_alloc_state(const struct hash_table_state *template) {
+ size_t required_bytes;
+ if (hash_table_state_required_bytes(template->size, &required_bytes)) {
+ return NULL;
+ }
+
+ /* An empty slot has hashcode 0. So this marks all slots as empty */
+ struct hash_table_state *state = aws_mem_calloc(template->alloc, 1, required_bytes);
+
+ if (state == NULL) {
+ return state;
+ }
+
+ *state = *template;
+ return state;
+}
+
+/* Computes the correct size and max_load based on a requested size. */
+static int s_update_template_size(struct hash_table_state *template, size_t expected_elements) {
+ size_t min_size = expected_elements;
+
+ if (min_size < 2) {
+ min_size = 2;
+ }
+
+ /* size is always a power of 2 */
+ size_t size;
+ if (aws_round_up_to_power_of_two(min_size, &size)) {
+ return AWS_OP_ERR;
+ }
+
+ /* Update the template once we've calculated everything successfully */
+ template->size = size;
+ template->max_load = (size_t)(template->max_load_factor * (double)template->size);
+ /* Ensure that there is always at least one empty slot in the hash table */
+ if (template->max_load >= size) {
+ template->max_load = size - 1;
+ }
+
+ /* Since size is a power of 2: (index & (size - 1)) == (index % size) */
+ template->mask = size - 1;
+
+ return AWS_OP_SUCCESS;
+}
+
+int aws_hash_table_init(
+ struct aws_hash_table *map,
+ struct aws_allocator *alloc,
+ size_t size,
+ aws_hash_fn *hash_fn,
+ aws_hash_callback_eq_fn *equals_fn,
+ aws_hash_callback_destroy_fn *destroy_key_fn,
+ aws_hash_callback_destroy_fn *destroy_value_fn) {
AWS_PRECONDITION(map != NULL);
AWS_PRECONDITION(alloc != NULL);
AWS_PRECONDITION(hash_fn != NULL);
AWS_PRECONDITION(equals_fn != NULL);
-
- struct hash_table_state template;
- template.hash_fn = hash_fn;
- template.equals_fn = equals_fn;
- template.destroy_key_fn = destroy_key_fn;
- template.destroy_value_fn = destroy_value_fn;
- template.alloc = alloc;
-
- template.entry_count = 0;
- template.max_load_factor = 0.95; /* TODO - make configurable? */
-
- if (s_update_template_size(&template, size)) {
- return AWS_OP_ERR;
- }
- map->p_impl = s_alloc_state(&template);
-
- if (!map->p_impl) {
- return AWS_OP_ERR;
- }
-
+
+ struct hash_table_state template;
+ template.hash_fn = hash_fn;
+ template.equals_fn = equals_fn;
+ template.destroy_key_fn = destroy_key_fn;
+ template.destroy_value_fn = destroy_value_fn;
+ template.alloc = alloc;
+
+ template.entry_count = 0;
+ template.max_load_factor = 0.95; /* TODO - make configurable? */
+
+ if (s_update_template_size(&template, size)) {
+ return AWS_OP_ERR;
+ }
+ map->p_impl = s_alloc_state(&template);
+
+ if (!map->p_impl) {
+ return AWS_OP_ERR;
+ }
+
AWS_SUCCEED_WITH_POSTCONDITION(aws_hash_table_is_valid(map));
-}
-
-void aws_hash_table_clean_up(struct aws_hash_table *map) {
+}
+
+void aws_hash_table_clean_up(struct aws_hash_table *map) {
AWS_PRECONDITION(map != NULL);
AWS_PRECONDITION(
map->p_impl == NULL || aws_hash_table_is_valid(map),
"Input aws_hash_table [map] must be valid or hash_table_state pointer [map->p_impl] must be NULL, in case "
"aws_hash_table_clean_up was called twice.");
- struct hash_table_state *state = map->p_impl;
-
- /* Ensure that we're idempotent */
- if (!state) {
- return;
- }
-
- aws_hash_table_clear(map);
+ struct hash_table_state *state = map->p_impl;
+
+ /* Ensure that we're idempotent */
+ if (!state) {
+ return;
+ }
+
+ aws_hash_table_clear(map);
aws_mem_release(map->p_impl->alloc, map->p_impl);
-
- map->p_impl = NULL;
+
+ map->p_impl = NULL;
AWS_POSTCONDITION(map->p_impl == NULL);
-}
-
-void aws_hash_table_swap(struct aws_hash_table *AWS_RESTRICT a, struct aws_hash_table *AWS_RESTRICT b) {
- AWS_PRECONDITION(a != b);
- struct aws_hash_table tmp = *a;
- *a = *b;
- *b = tmp;
-}
-
-void aws_hash_table_move(struct aws_hash_table *AWS_RESTRICT to, struct aws_hash_table *AWS_RESTRICT from) {
+}
+
+void aws_hash_table_swap(struct aws_hash_table *AWS_RESTRICT a, struct aws_hash_table *AWS_RESTRICT b) {
+ AWS_PRECONDITION(a != b);
+ struct aws_hash_table tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+void aws_hash_table_move(struct aws_hash_table *AWS_RESTRICT to, struct aws_hash_table *AWS_RESTRICT from) {
AWS_PRECONDITION(to != NULL);
AWS_PRECONDITION(from != NULL);
AWS_PRECONDITION(to != from);
AWS_PRECONDITION(aws_hash_table_is_valid(from));
- *to = *from;
- AWS_ZERO_STRUCT(*from);
- AWS_POSTCONDITION(aws_hash_table_is_valid(to));
-}
-
-/* Tries to find where the requested key is or where it should go if put.
- * Returns AWS_ERROR_SUCCESS if the item existed (leaving it in *entry),
- * or AWS_ERROR_HASHTBL_ITEM_NOT_FOUND if it did not (putting its destination
- * in *entry). Note that this does not take care of displacing whatever was in
- * that entry before.
- *
- * probe_idx is set to the probe index of the entry found.
- */
-
-static int s_find_entry1(
- struct hash_table_state *state,
- uint64_t hash_code,
- const void *key,
- struct hash_table_entry **p_entry,
- size_t *p_probe_idx);
-
-/* Inlined fast path: Check the first slot, only. */
-/* TODO: Force inlining? */
-static int inline s_find_entry(
- struct hash_table_state *state,
- uint64_t hash_code,
- const void *key,
- struct hash_table_entry **p_entry,
- size_t *p_probe_idx) {
- struct hash_table_entry *entry = &state->slots[hash_code & state->mask];
-
- if (entry->hash_code == 0) {
- if (p_probe_idx) {
- *p_probe_idx = 0;
- }
- *p_entry = entry;
- return AWS_ERROR_HASHTBL_ITEM_NOT_FOUND;
- }
-
+ *to = *from;
+ AWS_ZERO_STRUCT(*from);
+ AWS_POSTCONDITION(aws_hash_table_is_valid(to));
+}
+
+/* Tries to find where the requested key is or where it should go if put.
+ * Returns AWS_ERROR_SUCCESS if the item existed (leaving it in *entry),
+ * or AWS_ERROR_HASHTBL_ITEM_NOT_FOUND if it did not (putting its destination
+ * in *entry). Note that this does not take care of displacing whatever was in
+ * that entry before.
+ *
+ * probe_idx is set to the probe index of the entry found.
+ */
+
+static int s_find_entry1(
+ struct hash_table_state *state,
+ uint64_t hash_code,
+ const void *key,
+ struct hash_table_entry **p_entry,
+ size_t *p_probe_idx);
+
+/* Inlined fast path: Check the first slot, only. */
+/* TODO: Force inlining? */
+static int inline s_find_entry(
+ struct hash_table_state *state,
+ uint64_t hash_code,
+ const void *key,
+ struct hash_table_entry **p_entry,
+ size_t *p_probe_idx) {
+ struct hash_table_entry *entry = &state->slots[hash_code & state->mask];
+
+ if (entry->hash_code == 0) {
+ if (p_probe_idx) {
+ *p_probe_idx = 0;
+ }
+ *p_entry = entry;
+ return AWS_ERROR_HASHTBL_ITEM_NOT_FOUND;
+ }
+
if (entry->hash_code == hash_code && s_hash_keys_eq(state, key, entry->element.key)) {
- if (p_probe_idx) {
- *p_probe_idx = 0;
- }
- *p_entry = entry;
- return AWS_OP_SUCCESS;
- }
-
- return s_find_entry1(state, hash_code, key, p_entry, p_probe_idx);
-}
-
-static int s_find_entry1(
- struct hash_table_state *state,
- uint64_t hash_code,
- const void *key,
- struct hash_table_entry **p_entry,
- size_t *p_probe_idx) {
- size_t probe_idx = 1;
- /* If we find a deleted entry, we record that index and return it as our probe index (i.e. we'll keep searching to
- * see if it already exists, but if not we'll overwrite the deleted entry).
- */
-
- int rv;
- struct hash_table_entry *entry;
+ if (p_probe_idx) {
+ *p_probe_idx = 0;
+ }
+ *p_entry = entry;
+ return AWS_OP_SUCCESS;
+ }
+
+ return s_find_entry1(state, hash_code, key, p_entry, p_probe_idx);
+}
+
+static int s_find_entry1(
+ struct hash_table_state *state,
+ uint64_t hash_code,
+ const void *key,
+ struct hash_table_entry **p_entry,
+ size_t *p_probe_idx) {
+ size_t probe_idx = 1;
+ /* If we find a deleted entry, we record that index and return it as our probe index (i.e. we'll keep searching to
+ * see if it already exists, but if not we'll overwrite the deleted entry).
+ */
+
+ int rv;
+ struct hash_table_entry *entry;
/* This loop is guaranteed to terminate because entry_probe is bounded above by state->mask (i.e. state->size - 1).
* Since probe_idx increments every loop iteration, it will become larger than entry_probe after at most state->size
* transitions and the loop will exit (if it hasn't already)
@@ -350,82 +350,82 @@ static int s_find_entry1(
# pragma CPROVER check push
# pragma CPROVER check disable "unsigned-overflow"
#endif
- uint64_t index = (hash_code + probe_idx) & state->mask;
+ uint64_t index = (hash_code + probe_idx) & state->mask;
#ifdef CBMC
# pragma CPROVER check pop
#endif
- entry = &state->slots[index];
- if (!entry->hash_code) {
- rv = AWS_ERROR_HASHTBL_ITEM_NOT_FOUND;
- break;
- }
-
+ entry = &state->slots[index];
+ if (!entry->hash_code) {
+ rv = AWS_ERROR_HASHTBL_ITEM_NOT_FOUND;
+ break;
+ }
+
if (entry->hash_code == hash_code && s_hash_keys_eq(state, key, entry->element.key)) {
- rv = AWS_ERROR_SUCCESS;
- break;
- }
-
+ rv = AWS_ERROR_SUCCESS;
+ break;
+ }
+
#ifdef CBMC
# pragma CPROVER check push
# pragma CPROVER check disable "unsigned-overflow"
#endif
- uint64_t entry_probe = (index - entry->hash_code) & state->mask;
+ uint64_t entry_probe = (index - entry->hash_code) & state->mask;
#ifdef CBMC
# pragma CPROVER check pop
#endif
-
- if (entry_probe < probe_idx) {
- /* We now know that our target entry cannot exist; if it did exist,
- * it would be at the current location as it has a higher probe
- * length than the entry we are examining and thus would have
- * preempted that item
- */
- rv = AWS_ERROR_HASHTBL_ITEM_NOT_FOUND;
- break;
- }
-
- probe_idx++;
- }
-
- *p_entry = entry;
- if (p_probe_idx) {
- *p_probe_idx = probe_idx;
+
+ if (entry_probe < probe_idx) {
+ /* We now know that our target entry cannot exist; if it did exist,
+ * it would be at the current location as it has a higher probe
+ * length than the entry we are examining and thus would have
+ * preempted that item
+ */
+ rv = AWS_ERROR_HASHTBL_ITEM_NOT_FOUND;
+ break;
+ }
+
+ probe_idx++;
}
-
- return rv;
-}
-
-int aws_hash_table_find(const struct aws_hash_table *map, const void *key, struct aws_hash_element **p_elem) {
+
+ *p_entry = entry;
+ if (p_probe_idx) {
+ *p_probe_idx = probe_idx;
+ }
+
+ return rv;
+}
+
+int aws_hash_table_find(const struct aws_hash_table *map, const void *key, struct aws_hash_element **p_elem) {
AWS_PRECONDITION(aws_hash_table_is_valid(map));
AWS_PRECONDITION(AWS_OBJECT_PTR_IS_WRITABLE(p_elem), "Input aws_hash_element pointer [p_elem] must be writable.");
- struct hash_table_state *state = map->p_impl;
- uint64_t hash_code = s_hash_for(state, key);
- struct hash_table_entry *entry;
-
- int rv = s_find_entry(state, hash_code, key, &entry, NULL);
-
- if (rv == AWS_ERROR_SUCCESS) {
- *p_elem = &entry->element;
- } else {
- *p_elem = NULL;
- }
+ struct hash_table_state *state = map->p_impl;
+ uint64_t hash_code = s_hash_for(state, key);
+ struct hash_table_entry *entry;
+
+ int rv = s_find_entry(state, hash_code, key, &entry, NULL);
+
+ if (rv == AWS_ERROR_SUCCESS) {
+ *p_elem = &entry->element;
+ } else {
+ *p_elem = NULL;
+ }
AWS_SUCCEED_WITH_POSTCONDITION(aws_hash_table_is_valid(map));
-}
-
+}
+
/**
* Attempts to find a home for the given entry.
* If the entry was empty (i.e. hash-code of 0), then the function does nothing and returns NULL
* Otherwise, it emplaces the item, and returns a pointer to the newly emplaced entry.
* This function is only called after the hash-table has been expanded to fit the new element,
* so it should never fail.
- */
-static struct hash_table_entry *s_emplace_item(
- struct hash_table_state *state,
- struct hash_table_entry entry,
- size_t probe_idx) {
+ */
+static struct hash_table_entry *s_emplace_item(
+ struct hash_table_state *state,
+ struct hash_table_entry entry,
+ size_t probe_idx) {
AWS_PRECONDITION(hash_table_state_is_valid(state));
-
+
if (entry.hash_code == 0) {
AWS_RETURN_WITH_POSTCONDITION(NULL, hash_table_state_is_valid(state));
}
@@ -439,263 +439,263 @@ static struct hash_table_entry *s_emplace_item(
# pragma CPROVER check push
# pragma CPROVER check disable "unsigned-overflow"
#endif
- size_t index = (size_t)(entry.hash_code + probe_idx) & state->mask;
+ size_t index = (size_t)(entry.hash_code + probe_idx) & state->mask;
#ifdef CBMC
# pragma CPROVER check pop
#endif
- struct hash_table_entry *victim = &state->slots[index];
-
+ struct hash_table_entry *victim = &state->slots[index];
+
#ifdef CBMC
# pragma CPROVER check push
# pragma CPROVER check disable "unsigned-overflow"
#endif
- size_t victim_probe_idx = (size_t)(index - victim->hash_code) & state->mask;
+ size_t victim_probe_idx = (size_t)(index - victim->hash_code) & state->mask;
#ifdef CBMC
# pragma CPROVER check pop
#endif
-
- if (!victim->hash_code || victim_probe_idx < probe_idx) {
+
+ if (!victim->hash_code || victim_probe_idx < probe_idx) {
/* The first thing we emplace is the entry itself. A pointer to its location becomes the rval */
if (!rval) {
rval = victim;
- }
-
- struct hash_table_entry tmp = *victim;
- *victim = entry;
- entry = tmp;
-
- probe_idx = victim_probe_idx + 1;
- } else {
- probe_idx++;
- }
- }
-
+ }
+
+ struct hash_table_entry tmp = *victim;
+ *victim = entry;
+ entry = tmp;
+
+ probe_idx = victim_probe_idx + 1;
+ } else {
+ probe_idx++;
+ }
+ }
+
AWS_RETURN_WITH_POSTCONDITION(
rval,
hash_table_state_is_valid(state) && rval >= &state->slots[0] && rval < &state->slots[state->size],
"Output hash_table_entry pointer [rval] must point in the slots of [state].");
-}
-
-static int s_expand_table(struct aws_hash_table *map) {
- struct hash_table_state *old_state = map->p_impl;
- struct hash_table_state template = *old_state;
-
+}
+
+static int s_expand_table(struct aws_hash_table *map) {
+ struct hash_table_state *old_state = map->p_impl;
+ struct hash_table_state template = *old_state;
+
size_t new_size;
if (aws_mul_size_checked(template.size, 2, &new_size)) {
return AWS_OP_ERR;
}
-
+
if (s_update_template_size(&template, new_size)) {
return AWS_OP_ERR;
}
- struct hash_table_state *new_state = s_alloc_state(&template);
- if (!new_state) {
- return AWS_OP_ERR;
- }
-
- for (size_t i = 0; i < old_state->size; i++) {
- struct hash_table_entry entry = old_state->slots[i];
- if (entry.hash_code) {
- /* We can directly emplace since we know we won't put the same item twice */
- s_emplace_item(new_state, entry, 0);
- }
- }
-
- map->p_impl = new_state;
- aws_mem_release(new_state->alloc, old_state);
-
- return AWS_OP_SUCCESS;
-}
-
-int aws_hash_table_create(
- struct aws_hash_table *map,
- const void *key,
- struct aws_hash_element **p_elem,
- int *was_created) {
-
- struct hash_table_state *state = map->p_impl;
- uint64_t hash_code = s_hash_for(state, key);
- struct hash_table_entry *entry;
- size_t probe_idx;
- int ignored;
- if (!was_created) {
- was_created = &ignored;
- }
-
- int rv = s_find_entry(state, hash_code, key, &entry, &probe_idx);
-
- if (rv == AWS_ERROR_SUCCESS) {
- if (p_elem) {
- *p_elem = &entry->element;
- }
- *was_created = 0;
- return AWS_OP_SUCCESS;
- }
-
- /* Okay, we need to add an entry. Check the load factor first. */
- size_t incr_entry_count;
- if (aws_add_size_checked(state->entry_count, 1, &incr_entry_count)) {
- return AWS_OP_ERR;
- }
- if (incr_entry_count > state->max_load) {
- rv = s_expand_table(map);
- if (rv != AWS_OP_SUCCESS) {
- /* Any error was already raised in expand_table */
- return rv;
- }
- state = map->p_impl;
- /* If we expanded the table, we need to discard the probe index returned from find_entry,
- * as it's likely that we can find a more desirable slot. If we don't, then later gets will
- * terminate before reaching our probe index.
-
- * n.b. currently we ignore this probe_idx subsequently, but leaving
- this here so we don't
- * forget when we optimize later. */
- probe_idx = 0;
- }
-
- state->entry_count++;
- struct hash_table_entry new_entry;
- new_entry.element.key = key;
- new_entry.element.value = NULL;
- new_entry.hash_code = hash_code;
-
- entry = s_emplace_item(state, new_entry, probe_idx);
-
- if (p_elem) {
- *p_elem = &entry->element;
- }
-
- *was_created = 1;
-
- return AWS_OP_SUCCESS;
-}
-
-AWS_COMMON_API
-int aws_hash_table_put(struct aws_hash_table *map, const void *key, void *value, int *was_created) {
- struct aws_hash_element *p_elem;
- int was_created_fallback;
-
- if (!was_created) {
- was_created = &was_created_fallback;
- }
-
- if (aws_hash_table_create(map, key, &p_elem, was_created)) {
- return AWS_OP_ERR;
- }
-
- /*
- * aws_hash_table_create might resize the table, which results in map->p_impl changing.
- * It is therefore important to wait to read p_impl until after we return.
- */
- struct hash_table_state *state = map->p_impl;
-
- if (!*was_created) {
- if (p_elem->key != key && state->destroy_key_fn) {
- state->destroy_key_fn((void *)p_elem->key);
- }
-
- if (state->destroy_value_fn) {
- state->destroy_value_fn((void *)p_elem->value);
- }
- }
-
- p_elem->key = key;
- p_elem->value = value;
-
- return AWS_OP_SUCCESS;
-}
-
-/* Clears an entry. Does _not_ invoke destructor callbacks.
- * Returns the last slot touched (note that if we wrap, we'll report an index
- * lower than the original entry's index)
- */
-static size_t s_remove_entry(struct hash_table_state *state, struct hash_table_entry *entry) {
- AWS_PRECONDITION(hash_table_state_is_valid(state));
- AWS_PRECONDITION(state->entry_count > 0);
+ struct hash_table_state *new_state = s_alloc_state(&template);
+ if (!new_state) {
+ return AWS_OP_ERR;
+ }
+
+ for (size_t i = 0; i < old_state->size; i++) {
+ struct hash_table_entry entry = old_state->slots[i];
+ if (entry.hash_code) {
+ /* We can directly emplace since we know we won't put the same item twice */
+ s_emplace_item(new_state, entry, 0);
+ }
+ }
+
+ map->p_impl = new_state;
+ aws_mem_release(new_state->alloc, old_state);
+
+ return AWS_OP_SUCCESS;
+}
+
+int aws_hash_table_create(
+ struct aws_hash_table *map,
+ const void *key,
+ struct aws_hash_element **p_elem,
+ int *was_created) {
+
+ struct hash_table_state *state = map->p_impl;
+ uint64_t hash_code = s_hash_for(state, key);
+ struct hash_table_entry *entry;
+ size_t probe_idx;
+ int ignored;
+ if (!was_created) {
+ was_created = &ignored;
+ }
+
+ int rv = s_find_entry(state, hash_code, key, &entry, &probe_idx);
+
+ if (rv == AWS_ERROR_SUCCESS) {
+ if (p_elem) {
+ *p_elem = &entry->element;
+ }
+ *was_created = 0;
+ return AWS_OP_SUCCESS;
+ }
+
+ /* Okay, we need to add an entry. Check the load factor first. */
+ size_t incr_entry_count;
+ if (aws_add_size_checked(state->entry_count, 1, &incr_entry_count)) {
+ return AWS_OP_ERR;
+ }
+ if (incr_entry_count > state->max_load) {
+ rv = s_expand_table(map);
+ if (rv != AWS_OP_SUCCESS) {
+ /* Any error was already raised in expand_table */
+ return rv;
+ }
+ state = map->p_impl;
+ /* If we expanded the table, we need to discard the probe index returned from find_entry,
+ * as it's likely that we can find a more desirable slot. If we don't, then later gets will
+ * terminate before reaching our probe index.
+
+ * n.b. currently we ignore this probe_idx subsequently, but leaving
+ this here so we don't
+ * forget when we optimize later. */
+ probe_idx = 0;
+ }
+
+ state->entry_count++;
+ struct hash_table_entry new_entry;
+ new_entry.element.key = key;
+ new_entry.element.value = NULL;
+ new_entry.hash_code = hash_code;
+
+ entry = s_emplace_item(state, new_entry, probe_idx);
+
+ if (p_elem) {
+ *p_elem = &entry->element;
+ }
+
+ *was_created = 1;
+
+ return AWS_OP_SUCCESS;
+}
+
+AWS_COMMON_API
+int aws_hash_table_put(struct aws_hash_table *map, const void *key, void *value, int *was_created) {
+ struct aws_hash_element *p_elem;
+ int was_created_fallback;
+
+ if (!was_created) {
+ was_created = &was_created_fallback;
+ }
+
+ if (aws_hash_table_create(map, key, &p_elem, was_created)) {
+ return AWS_OP_ERR;
+ }
+
+ /*
+ * aws_hash_table_create might resize the table, which results in map->p_impl changing.
+ * It is therefore important to wait to read p_impl until after we return.
+ */
+ struct hash_table_state *state = map->p_impl;
+
+ if (!*was_created) {
+ if (p_elem->key != key && state->destroy_key_fn) {
+ state->destroy_key_fn((void *)p_elem->key);
+ }
+
+ if (state->destroy_value_fn) {
+ state->destroy_value_fn((void *)p_elem->value);
+ }
+ }
+
+ p_elem->key = key;
+ p_elem->value = value;
+
+ return AWS_OP_SUCCESS;
+}
+
+/* Clears an entry. Does _not_ invoke destructor callbacks.
+ * Returns the last slot touched (note that if we wrap, we'll report an index
+ * lower than the original entry's index)
+ */
+static size_t s_remove_entry(struct hash_table_state *state, struct hash_table_entry *entry) {
+ AWS_PRECONDITION(hash_table_state_is_valid(state));
+ AWS_PRECONDITION(state->entry_count > 0);
AWS_PRECONDITION(
entry >= &state->slots[0] && entry < &state->slots[state->size],
"Input hash_table_entry [entry] pointer must point in the available slots.");
- state->entry_count--;
-
- /* Shift subsequent entries back until we find an entry that belongs at its
- * current position. This is important to ensure that subsequent searches
- * don't terminate at the removed element.
- */
- size_t index = s_index_for(state, entry);
- /* There is always at least one empty slot in the hash table, so this loop always terminates */
- while (1) {
- size_t next_index = (index + 1) & state->mask;
-
- /* If we hit an empty slot, stop */
- if (!state->slots[next_index].hash_code) {
- break;
- }
- /* If the next slot is at the start of the probe sequence, stop.
- * We know that nothing with an earlier home slot is after this;
- * otherwise this index-zero entry would have been evicted from its
- * home.
- */
- if ((state->slots[next_index].hash_code & state->mask) == next_index) {
- break;
- }
-
- /* Okay, shift this one back */
- state->slots[index] = state->slots[next_index];
- index = next_index;
- }
-
- /* Clear the entry we shifted out of */
- AWS_ZERO_STRUCT(state->slots[index]);
+ state->entry_count--;
+
+ /* Shift subsequent entries back until we find an entry that belongs at its
+ * current position. This is important to ensure that subsequent searches
+ * don't terminate at the removed element.
+ */
+ size_t index = s_index_for(state, entry);
+ /* There is always at least one empty slot in the hash table, so this loop always terminates */
+ while (1) {
+ size_t next_index = (index + 1) & state->mask;
+
+ /* If we hit an empty slot, stop */
+ if (!state->slots[next_index].hash_code) {
+ break;
+ }
+ /* If the next slot is at the start of the probe sequence, stop.
+ * We know that nothing with an earlier home slot is after this;
+ * otherwise this index-zero entry would have been evicted from its
+ * home.
+ */
+ if ((state->slots[next_index].hash_code & state->mask) == next_index) {
+ break;
+ }
+
+ /* Okay, shift this one back */
+ state->slots[index] = state->slots[next_index];
+ index = next_index;
+ }
+
+ /* Clear the entry we shifted out of */
+ AWS_ZERO_STRUCT(state->slots[index]);
AWS_RETURN_WITH_POSTCONDITION(index, hash_table_state_is_valid(state) && index <= state->size);
-}
-
-int aws_hash_table_remove(
- struct aws_hash_table *map,
- const void *key,
- struct aws_hash_element *p_value,
- int *was_present) {
+}
+
+int aws_hash_table_remove(
+ struct aws_hash_table *map,
+ const void *key,
+ struct aws_hash_element *p_value,
+ int *was_present) {
AWS_PRECONDITION(aws_hash_table_is_valid(map));
AWS_PRECONDITION(
p_value == NULL || AWS_OBJECT_PTR_IS_WRITABLE(p_value), "Input pointer [p_value] must be NULL or writable.");
AWS_PRECONDITION(
was_present == NULL || AWS_OBJECT_PTR_IS_WRITABLE(was_present),
"Input pointer [was_present] must be NULL or writable.");
-
- struct hash_table_state *state = map->p_impl;
- uint64_t hash_code = s_hash_for(state, key);
- struct hash_table_entry *entry;
- int ignored;
-
- if (!was_present) {
- was_present = &ignored;
- }
-
- int rv = s_find_entry(state, hash_code, key, &entry, NULL);
-
- if (rv != AWS_ERROR_SUCCESS) {
- *was_present = 0;
+
+ struct hash_table_state *state = map->p_impl;
+ uint64_t hash_code = s_hash_for(state, key);
+ struct hash_table_entry *entry;
+ int ignored;
+
+ if (!was_present) {
+ was_present = &ignored;
+ }
+
+ int rv = s_find_entry(state, hash_code, key, &entry, NULL);
+
+ if (rv != AWS_ERROR_SUCCESS) {
+ *was_present = 0;
AWS_SUCCEED_WITH_POSTCONDITION(aws_hash_table_is_valid(map));
- }
-
- *was_present = 1;
-
- if (p_value) {
- *p_value = entry->element;
- } else {
- if (state->destroy_key_fn) {
- state->destroy_key_fn((void *)entry->element.key);
- }
- if (state->destroy_value_fn) {
- state->destroy_value_fn(entry->element.value);
- }
- }
- s_remove_entry(state, entry);
-
+ }
+
+ *was_present = 1;
+
+ if (p_value) {
+ *p_value = entry->element;
+ } else {
+ if (state->destroy_key_fn) {
+ state->destroy_key_fn((void *)entry->element.key);
+ }
+ if (state->destroy_value_fn) {
+ state->destroy_value_fn(entry->element.value);
+ }
+ }
+ s_remove_entry(state, entry);
+
AWS_SUCCEED_WITH_POSTCONDITION(aws_hash_table_is_valid(map));
-}
-
+}
+
int aws_hash_table_remove_element(struct aws_hash_table *map, struct aws_hash_element *p_value) {
AWS_PRECONDITION(aws_hash_table_is_valid(map));
AWS_PRECONDITION(p_value != NULL);
@@ -708,213 +708,213 @@ int aws_hash_table_remove_element(struct aws_hash_table *map, struct aws_hash_el
AWS_SUCCEED_WITH_POSTCONDITION(aws_hash_table_is_valid(map));
}
-int aws_hash_table_foreach(
- struct aws_hash_table *map,
- int (*callback)(void *context, struct aws_hash_element *pElement),
- void *context) {
-
- for (struct aws_hash_iter iter = aws_hash_iter_begin(map); !aws_hash_iter_done(&iter); aws_hash_iter_next(&iter)) {
- int rv = callback(context, &iter.element);
-
- if (rv & AWS_COMMON_HASH_TABLE_ITER_DELETE) {
- aws_hash_iter_delete(&iter, false);
- }
-
- if (!(rv & AWS_COMMON_HASH_TABLE_ITER_CONTINUE)) {
- break;
- }
- }
-
- return AWS_OP_SUCCESS;
-}
-
-bool aws_hash_table_eq(
- const struct aws_hash_table *a,
- const struct aws_hash_table *b,
- aws_hash_callback_eq_fn *value_eq) {
+int aws_hash_table_foreach(
+ struct aws_hash_table *map,
+ int (*callback)(void *context, struct aws_hash_element *pElement),
+ void *context) {
+
+ for (struct aws_hash_iter iter = aws_hash_iter_begin(map); !aws_hash_iter_done(&iter); aws_hash_iter_next(&iter)) {
+ int rv = callback(context, &iter.element);
+
+ if (rv & AWS_COMMON_HASH_TABLE_ITER_DELETE) {
+ aws_hash_iter_delete(&iter, false);
+ }
+
+ if (!(rv & AWS_COMMON_HASH_TABLE_ITER_CONTINUE)) {
+ break;
+ }
+ }
+
+ return AWS_OP_SUCCESS;
+}
+
+bool aws_hash_table_eq(
+ const struct aws_hash_table *a,
+ const struct aws_hash_table *b,
+ aws_hash_callback_eq_fn *value_eq) {
AWS_PRECONDITION(aws_hash_table_is_valid(a));
AWS_PRECONDITION(aws_hash_table_is_valid(b));
AWS_PRECONDITION(value_eq != NULL);
- if (aws_hash_table_get_entry_count(a) != aws_hash_table_get_entry_count(b)) {
+ if (aws_hash_table_get_entry_count(a) != aws_hash_table_get_entry_count(b)) {
AWS_RETURN_WITH_POSTCONDITION(false, aws_hash_table_is_valid(a) && aws_hash_table_is_valid(b));
- }
-
- /*
- * Now that we have established that the two tables have the same number of
- * entries, we can simply iterate one and compare against the same key in
- * the other.
- */
+ }
+
+ /*
+ * Now that we have established that the two tables have the same number of
+ * entries, we can simply iterate one and compare against the same key in
+ * the other.
+ */
for (size_t i = 0; i < a->p_impl->size; ++i) {
const struct hash_table_entry *const a_entry = &a->p_impl->slots[i];
if (a_entry->hash_code == 0) {
continue;
}
- struct aws_hash_element *b_element = NULL;
-
+ struct aws_hash_element *b_element = NULL;
+
aws_hash_table_find(b, a_entry->element.key, &b_element);
-
- if (!b_element) {
- /* Key is present in A only */
+
+ if (!b_element) {
+ /* Key is present in A only */
AWS_RETURN_WITH_POSTCONDITION(false, aws_hash_table_is_valid(a) && aws_hash_table_is_valid(b));
- }
-
+ }
+
if (!s_safe_eq_check(value_eq, a_entry->element.value, b_element->value)) {
AWS_RETURN_WITH_POSTCONDITION(false, aws_hash_table_is_valid(a) && aws_hash_table_is_valid(b));
- }
- }
+ }
+ }
AWS_RETURN_WITH_POSTCONDITION(true, aws_hash_table_is_valid(a) && aws_hash_table_is_valid(b));
-}
-
-/**
- * Given an iterator, and a start slot, find the next available filled slot if it exists
- * Otherwise, return an iter that will return true for aws_hash_iter_done().
- * Note that aws_hash_iter_is_valid() need not hold on entry to the function, since
- * it can be called on a partially constructed iter from aws_hash_iter_begin().
- *
- * Note that calling this on an iterator which is "done" is idempotent: it will return another
- * iterator which is "done".
- */
-static inline void s_get_next_element(struct aws_hash_iter *iter, size_t start_slot) {
- AWS_PRECONDITION(iter != NULL);
+}
+
+/**
+ * Given an iterator, and a start slot, find the next available filled slot if it exists
+ * Otherwise, return an iter that will return true for aws_hash_iter_done().
+ * Note that aws_hash_iter_is_valid() need not hold on entry to the function, since
+ * it can be called on a partially constructed iter from aws_hash_iter_begin().
+ *
+ * Note that calling this on an iterator which is "done" is idempotent: it will return another
+ * iterator which is "done".
+ */
+static inline void s_get_next_element(struct aws_hash_iter *iter, size_t start_slot) {
+ AWS_PRECONDITION(iter != NULL);
AWS_PRECONDITION(aws_hash_table_is_valid(iter->map));
- struct hash_table_state *state = iter->map->p_impl;
- size_t limit = iter->limit;
-
- for (size_t i = start_slot; i < limit; i++) {
- struct hash_table_entry *entry = &state->slots[i];
-
- if (entry->hash_code) {
- iter->element = entry->element;
- iter->slot = i;
- iter->status = AWS_HASH_ITER_STATUS_READY_FOR_USE;
- return;
- }
- }
- iter->element.key = NULL;
- iter->element.value = NULL;
- iter->slot = iter->limit;
- iter->status = AWS_HASH_ITER_STATUS_DONE;
- AWS_POSTCONDITION(aws_hash_iter_is_valid(iter));
-}
-
-struct aws_hash_iter aws_hash_iter_begin(const struct aws_hash_table *map) {
- AWS_PRECONDITION(aws_hash_table_is_valid(map));
- struct hash_table_state *state = map->p_impl;
- struct aws_hash_iter iter;
+ struct hash_table_state *state = iter->map->p_impl;
+ size_t limit = iter->limit;
+
+ for (size_t i = start_slot; i < limit; i++) {
+ struct hash_table_entry *entry = &state->slots[i];
+
+ if (entry->hash_code) {
+ iter->element = entry->element;
+ iter->slot = i;
+ iter->status = AWS_HASH_ITER_STATUS_READY_FOR_USE;
+ return;
+ }
+ }
+ iter->element.key = NULL;
+ iter->element.value = NULL;
+ iter->slot = iter->limit;
+ iter->status = AWS_HASH_ITER_STATUS_DONE;
+ AWS_POSTCONDITION(aws_hash_iter_is_valid(iter));
+}
+
+struct aws_hash_iter aws_hash_iter_begin(const struct aws_hash_table *map) {
+ AWS_PRECONDITION(aws_hash_table_is_valid(map));
+ struct hash_table_state *state = map->p_impl;
+ struct aws_hash_iter iter;
AWS_ZERO_STRUCT(iter);
- iter.map = map;
- iter.limit = state->size;
- s_get_next_element(&iter, 0);
+ iter.map = map;
+ iter.limit = state->size;
+ s_get_next_element(&iter, 0);
AWS_RETURN_WITH_POSTCONDITION(
iter,
aws_hash_iter_is_valid(&iter) &&
(iter.status == AWS_HASH_ITER_STATUS_DONE || iter.status == AWS_HASH_ITER_STATUS_READY_FOR_USE),
"The status of output aws_hash_iter [iter] must either be DONE or READY_FOR_USE.");
-}
-
-bool aws_hash_iter_done(const struct aws_hash_iter *iter) {
- AWS_PRECONDITION(aws_hash_iter_is_valid(iter));
+}
+
+bool aws_hash_iter_done(const struct aws_hash_iter *iter) {
+ AWS_PRECONDITION(aws_hash_iter_is_valid(iter));
AWS_PRECONDITION(
iter->status == AWS_HASH_ITER_STATUS_DONE || iter->status == AWS_HASH_ITER_STATUS_READY_FOR_USE,
"Input aws_hash_iter [iter] must either be done, or ready to use.");
- /*
- * SIZE_MAX is a valid (non-terminal) value for iter->slot in the event that
- * we delete slot 0. See comments in aws_hash_iter_delete.
- *
- * As such we must use == rather than >= here.
- */
- bool rval = (iter->slot == iter->limit);
+ /*
+ * SIZE_MAX is a valid (non-terminal) value for iter->slot in the event that
+ * we delete slot 0. See comments in aws_hash_iter_delete.
+ *
+ * As such we must use == rather than >= here.
+ */
+ bool rval = (iter->slot == iter->limit);
AWS_POSTCONDITION(
iter->status == AWS_HASH_ITER_STATUS_DONE || iter->status == AWS_HASH_ITER_STATUS_READY_FOR_USE,
"The status of output aws_hash_iter [iter] must either be DONE or READY_FOR_USE.");
AWS_POSTCONDITION(
rval == (iter->status == AWS_HASH_ITER_STATUS_DONE),
"Output bool [rval] must be true if and only if the status of [iter] is DONE.");
- AWS_POSTCONDITION(aws_hash_iter_is_valid(iter));
- return rval;
-}
-
-void aws_hash_iter_next(struct aws_hash_iter *iter) {
- AWS_PRECONDITION(aws_hash_iter_is_valid(iter));
+ AWS_POSTCONDITION(aws_hash_iter_is_valid(iter));
+ return rval;
+}
+
+void aws_hash_iter_next(struct aws_hash_iter *iter) {
+ AWS_PRECONDITION(aws_hash_iter_is_valid(iter));
#ifdef CBMC
# pragma CPROVER check push
# pragma CPROVER check disable "unsigned-overflow"
#endif
- s_get_next_element(iter, iter->slot + 1);
+ s_get_next_element(iter, iter->slot + 1);
#ifdef CBMC
# pragma CPROVER check pop
#endif
AWS_POSTCONDITION(
iter->status == AWS_HASH_ITER_STATUS_DONE || iter->status == AWS_HASH_ITER_STATUS_READY_FOR_USE,
"The status of output aws_hash_iter [iter] must either be DONE or READY_FOR_USE.");
- AWS_POSTCONDITION(aws_hash_iter_is_valid(iter));
-}
-
-void aws_hash_iter_delete(struct aws_hash_iter *iter, bool destroy_contents) {
+ AWS_POSTCONDITION(aws_hash_iter_is_valid(iter));
+}
+
+void aws_hash_iter_delete(struct aws_hash_iter *iter, bool destroy_contents) {
AWS_PRECONDITION(
iter->status == AWS_HASH_ITER_STATUS_READY_FOR_USE, "Input aws_hash_iter [iter] must be ready for use.");
- AWS_PRECONDITION(aws_hash_iter_is_valid(iter));
+ AWS_PRECONDITION(aws_hash_iter_is_valid(iter));
AWS_PRECONDITION(
iter->map->p_impl->entry_count > 0,
"The hash_table_state pointed by input [iter] must contain at least one entry.");
-
- struct hash_table_state *state = iter->map->p_impl;
- if (destroy_contents) {
- if (state->destroy_key_fn) {
- state->destroy_key_fn((void *)iter->element.key);
- }
- if (state->destroy_value_fn) {
- state->destroy_value_fn(iter->element.value);
- }
- }
-
- size_t last_index = s_remove_entry(state, &state->slots[iter->slot]);
-
- /* If we shifted elements that are not part of the window we intend to iterate
- * over, it means we shifted an element that we already visited into the
- * iter->limit - 1 position. To avoid double iteration, we'll now reduce the
- * limit to compensate.
- *
- * Note that last_index cannot equal iter->slot, because slots[iter->slot]
- * is empty before we start walking the table.
- */
- if (last_index < iter->slot || last_index >= iter->limit) {
- iter->limit--;
- }
-
- /*
- * After removing this entry, the next entry might be in the same slot, or
- * in some later slot, or we might have no further entries.
- *
- * We also expect that the caller will call aws_hash_iter_done and aws_hash_iter_next
- * after this delete call. This gets a bit tricky if we just deleted the value
- * in slot 0, and a new value has shifted in.
- *
- * To deal with this, we'll just step back one slot, and let _next start iteration
- * at our current slot. Note that if we just deleted slot 0, this will result in
- * underflowing to SIZE_MAX; we have to take care in aws_hash_iter_done to avoid
- * treating this as an end-of-iteration condition.
- */
+
+ struct hash_table_state *state = iter->map->p_impl;
+ if (destroy_contents) {
+ if (state->destroy_key_fn) {
+ state->destroy_key_fn((void *)iter->element.key);
+ }
+ if (state->destroy_value_fn) {
+ state->destroy_value_fn(iter->element.value);
+ }
+ }
+
+ size_t last_index = s_remove_entry(state, &state->slots[iter->slot]);
+
+ /* If we shifted elements that are not part of the window we intend to iterate
+ * over, it means we shifted an element that we already visited into the
+ * iter->limit - 1 position. To avoid double iteration, we'll now reduce the
+ * limit to compensate.
+ *
+ * Note that last_index cannot equal iter->slot, because slots[iter->slot]
+ * is empty before we start walking the table.
+ */
+ if (last_index < iter->slot || last_index >= iter->limit) {
+ iter->limit--;
+ }
+
+ /*
+ * After removing this entry, the next entry might be in the same slot, or
+ * in some later slot, or we might have no further entries.
+ *
+ * We also expect that the caller will call aws_hash_iter_done and aws_hash_iter_next
+ * after this delete call. This gets a bit tricky if we just deleted the value
+ * in slot 0, and a new value has shifted in.
+ *
+ * To deal with this, we'll just step back one slot, and let _next start iteration
+ * at our current slot. Note that if we just deleted slot 0, this will result in
+ * underflowing to SIZE_MAX; we have to take care in aws_hash_iter_done to avoid
+ * treating this as an end-of-iteration condition.
+ */
#ifdef CBMC
# pragma CPROVER check push
# pragma CPROVER check disable "unsigned-overflow"
#endif
- iter->slot--;
+ iter->slot--;
#ifdef CBMC
# pragma CPROVER check pop
#endif
- iter->status = AWS_HASH_ITER_STATUS_DELETE_CALLED;
+ iter->status = AWS_HASH_ITER_STATUS_DELETE_CALLED;
AWS_POSTCONDITION(
iter->status == AWS_HASH_ITER_STATUS_DELETE_CALLED,
"The status of output aws_hash_iter [iter] must be DELETE_CALLED.");
- AWS_POSTCONDITION(aws_hash_iter_is_valid(iter));
-}
-
-void aws_hash_table_clear(struct aws_hash_table *map) {
+ AWS_POSTCONDITION(aws_hash_iter_is_valid(iter));
+}
+
+void aws_hash_table_clear(struct aws_hash_table *map) {
AWS_PRECONDITION(aws_hash_table_is_valid(map));
- struct hash_table_state *state = map->p_impl;
+ struct hash_table_state *state = map->p_impl;
/* Check that we have at least one destructor before iterating over the table */
if (state->destroy_key_fn || state->destroy_value_fn) {
@@ -922,65 +922,65 @@ void aws_hash_table_clear(struct aws_hash_table *map) {
struct hash_table_entry *entry = &state->slots[i];
if (!entry->hash_code) {
continue;
- }
+ }
if (state->destroy_key_fn) {
state->destroy_key_fn((void *)entry->element.key);
- }
+ }
if (state->destroy_value_fn) {
- state->destroy_value_fn(entry->element.value);
- }
- }
- }
- /* Since hash code 0 represents an empty slot we can just zero out the
- * entire table. */
- memset(state->slots, 0, sizeof(*state->slots) * state->size);
-
- state->entry_count = 0;
+ state->destroy_value_fn(entry->element.value);
+ }
+ }
+ }
+ /* Since hash code 0 represents an empty slot we can just zero out the
+ * entire table. */
+ memset(state->slots, 0, sizeof(*state->slots) * state->size);
+
+ state->entry_count = 0;
AWS_POSTCONDITION(aws_hash_table_is_valid(map));
-}
-
-uint64_t aws_hash_c_string(const void *item) {
+}
+
+uint64_t aws_hash_c_string(const void *item) {
AWS_PRECONDITION(aws_c_string_is_valid(item));
- const char *str = item;
-
- /* first digits of pi in hex */
- uint32_t b = 0x3243F6A8, c = 0x885A308D;
- hashlittle2(str, strlen(str), &c, &b);
-
- return ((uint64_t)b << 32) | c;
-}
-
-uint64_t aws_hash_string(const void *item) {
+ const char *str = item;
+
+ /* first digits of pi in hex */
+ uint32_t b = 0x3243F6A8, c = 0x885A308D;
+ hashlittle2(str, strlen(str), &c, &b);
+
+ return ((uint64_t)b << 32) | c;
+}
+
+uint64_t aws_hash_string(const void *item) {
AWS_PRECONDITION(aws_string_is_valid(item));
- const struct aws_string *str = item;
-
- /* first digits of pi in hex */
- uint32_t b = 0x3243F6A8, c = 0x885A308D;
- hashlittle2(aws_string_bytes(str), str->len, &c, &b);
+ const struct aws_string *str = item;
+
+ /* first digits of pi in hex */
+ uint32_t b = 0x3243F6A8, c = 0x885A308D;
+ hashlittle2(aws_string_bytes(str), str->len, &c, &b);
AWS_RETURN_WITH_POSTCONDITION(((uint64_t)b << 32) | c, aws_string_is_valid(str));
-}
-
-uint64_t aws_hash_byte_cursor_ptr(const void *item) {
+}
+
+uint64_t aws_hash_byte_cursor_ptr(const void *item) {
AWS_PRECONDITION(aws_byte_cursor_is_valid(item));
- const struct aws_byte_cursor *cur = item;
-
- /* first digits of pi in hex */
- uint32_t b = 0x3243F6A8, c = 0x885A308D;
- hashlittle2(cur->ptr, cur->len, &c, &b);
+ const struct aws_byte_cursor *cur = item;
+
+ /* first digits of pi in hex */
+ uint32_t b = 0x3243F6A8, c = 0x885A308D;
+ hashlittle2(cur->ptr, cur->len, &c, &b);
AWS_RETURN_WITH_POSTCONDITION(((uint64_t)b << 32) | c, aws_byte_cursor_is_valid(cur));
-}
-
-uint64_t aws_hash_ptr(const void *item) {
+}
+
+uint64_t aws_hash_ptr(const void *item) {
/* Since the numeric value of the pointer is considered, not the memory behind it, 0 is an acceptable value */
- /* first digits of e in hex
- * 2.b7e 1516 28ae d2a6 */
- uint32_t b = 0x2b7e1516, c = 0x28aed2a6;
-
- hashlittle2(&item, sizeof(item), &c, &b);
-
- return ((uint64_t)b << 32) | c;
-}
-
+ /* first digits of e in hex
+ * 2.b7e 1516 28ae d2a6 */
+ uint32_t b = 0x2b7e1516, c = 0x28aed2a6;
+
+ hashlittle2(&item, sizeof(item), &c, &b);
+
+ 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 */
@@ -989,28 +989,28 @@ uint64_t aws_hash_combine(uint64_t item1, uint64_t item2) {
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));
- bool rval = !strcmp(a, b);
+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));
+ bool rval = !strcmp(a, b);
AWS_RETURN_WITH_POSTCONDITION(rval, aws_c_string_is_valid(a) && aws_c_string_is_valid(b));
-}
-
-bool aws_hash_callback_string_eq(const void *a, const void *b) {
- AWS_PRECONDITION(aws_string_is_valid(a));
- AWS_PRECONDITION(aws_string_is_valid(b));
- bool rval = aws_string_eq(a, b);
+}
+
+bool aws_hash_callback_string_eq(const void *a, const void *b) {
+ AWS_PRECONDITION(aws_string_is_valid(a));
+ AWS_PRECONDITION(aws_string_is_valid(b));
+ bool rval = aws_string_eq(a, b);
AWS_RETURN_WITH_POSTCONDITION(rval, aws_c_string_is_valid(a) && aws_c_string_is_valid(b));
-}
-
-void aws_hash_callback_string_destroy(void *a) {
- AWS_PRECONDITION(aws_string_is_valid(a));
- aws_string_destroy(a);
-}
-
-bool aws_ptr_eq(const void *a, const void *b) {
- return a == b;
-}
+}
+
+void aws_hash_callback_string_destroy(void *a) {
+ AWS_PRECONDITION(aws_string_is_valid(a));
+ aws_string_destroy(a);
+}
+
+bool aws_ptr_eq(const void *a, const void *b) {
+ return a == b;
+}
/**
* Best-effort check of hash_table_state data-structure invariants