Skip to content

Commit dbd3ff4

Browse files
clalancettefujitatomoyamethylDragon
authored
Calculate the next power-of-two for the user in hash_map_init. (#420)
* Calculate the next power-of-two for the user in hash_map_init. That is, we allow the user to specify non powers of two, and then we do the calculation for them. Signed-off-by: Chris Lalancette <clalancette@gmail.com> Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com> Co-authored-by: methylDragon <methylDragon@gmail.com>
1 parent 5bd1230 commit dbd3ff4

File tree

3 files changed

+24
-8
lines changed

3 files changed

+24
-8
lines changed

include/rcutils/types/hash_map.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ rcutils_get_zero_initialized_hash_map();
144144
* ```c
145145
* rcutils_hash_map_t hash_map = rcutils_get_zero_initialized_hash_map();
146146
* rcutils_ret_t ret =
147-
* rcutils_hash_map_init(&hash_map, 10, rcutils_get_default_allocator());
147+
* rcutils_hash_map_init(&hash_map, 2, rcutils_get_default_allocator());
148148
* if (ret != RCUTILS_RET_OK) {
149149
* // ... do error handling
150150
* }
@@ -156,7 +156,8 @@ rcutils_get_zero_initialized_hash_map();
156156
* ```
157157
*
158158
* \param[inout] hash_map rcutils_hash_map_t to be initialized
159-
* \param[in] initial_capacity the amount of initial capacity for the hash_map - this must be greater than zero and a power of 2
159+
* \param[in] initial_capacity the amount of initial capacity for the hash_map - this must be
160+
* greater than zero and will be automatically rounded up to the next power of 2
160161
* \param[in] key_size the size (in bytes) of the key used to index the data
161162
* \param[in] data_size the size (in bytes) of the data being stored
162163
* \param[in] key_hashing_func a function that returns a hashed value for a key

src/hash_map.c

+16-4
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,19 @@ static rcutils_ret_t hash_map_check_and_grow_map(rcutils_hash_map_t * hash_map)
230230
return ret;
231231
}
232232

233+
// Modified from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
234+
static size_t next_power_of_two(size_t v)
235+
{
236+
size_t shf = 0;
237+
v--;
238+
for (size_t i = 0; shf < sizeof(size_t) * 4; ++i) {
239+
shf = (((size_t) 1) << i);
240+
v |= v >> shf;
241+
}
242+
v++;
243+
return v > 1 ? v : 1;
244+
}
245+
233246
rcutils_ret_t
234247
rcutils_hash_map_init(
235248
rcutils_hash_map_t * hash_map,
@@ -255,11 +268,10 @@ rcutils_hash_map_init(
255268
return RCUTILS_RET_INVALID_ARGUMENT;
256269
}
257270

258-
// Due to an optimization we use during lookup, we can currently only handle power-of-two
259-
// capacities. Enforce that here.
271+
// Due to an optimization we use during lookup, the capacity must be a power-of-two.
272+
// If the user passed us something that is not power-of-two, round up to the next power-of-two
260273
if ((initial_capacity & (initial_capacity - 1)) != 0) {
261-
RCUTILS_SET_ERROR_MSG("This hashmap only works with power-of-two capacities");
262-
return RCUTILS_RET_INVALID_ARGUMENT;
274+
initial_capacity = next_power_of_two(initial_capacity);
263275
}
264276

265277
hash_map->impl = allocator->allocate(sizeof(rcutils_hash_map_impl_t), allocator->state);

test/test_hash_map.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,14 @@ TEST_F(HashMapBaseTest, init_map_initial_capacity_zero_fails) {
9595
EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, ret) << rcutils_get_error_string().str;
9696
}
9797

98-
TEST_F(HashMapBaseTest, init_map_initial_capacity_not_power_of_two_fails) {
98+
TEST_F(HashMapBaseTest, init_map_initial_capacity_not_power_of_two) {
9999
rcutils_ret_t ret = rcutils_hash_map_init(
100100
&map, 3, sizeof(uint32_t), sizeof(uint32_t),
101101
test_hash_map_uint32_hash_func, test_uint32_cmp, &allocator);
102-
EXPECT_EQ(RCUTILS_RET_INVALID_ARGUMENT, ret) << rcutils_get_error_string().str;
102+
ASSERT_EQ(RCUTILS_RET_OK, ret) << rcutils_get_error_string().str;
103+
size_t current_capacity = 0;
104+
ASSERT_EQ(RCUTILS_RET_OK, rcutils_hash_map_get_capacity(&map, &current_capacity));
105+
EXPECT_EQ(4u, current_capacity);
103106
}
104107

105108
TEST_F(HashMapBaseTest, init_map_key_size_zero_fails) {

0 commit comments

Comments
 (0)