Skip to content

Commit 3b8c647

Browse files
committed
Add the possibility to customize the growth policy.
1 parent d446e41 commit 3b8c647

3 files changed

Lines changed: 70 additions & 38 deletions

File tree

src/array_hash.h

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -84,24 +84,58 @@ struct str_hash {
8484
}
8585
};
8686

87-
namespace detail_array_hash {
88-
89-
static constexpr bool is_power_of_two(std::size_t value) {
90-
return value != 0 && (value & (value - 1)) == 0;
91-
}
9287

93-
// TODO could be faster
94-
static std::size_t round_up_to_power_of_two(std::size_t value) {
95-
if(is_power_of_two(value)) {
96-
return value;
88+
89+
template<std::size_t GrowthFactor>
90+
class power_of_two_growth_policy {
91+
public:
92+
power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
93+
const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
94+
95+
min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
96+
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
9797
}
9898

99-
std::size_t power = 1;
100-
while(power < value) {
101-
power <<= 1;
99+
std::size_t bucket_for_hash(std::size_t hash, std::size_t bucket_count) const {
100+
return hash & (bucket_count-1);
102101
}
103102

104-
return power;
103+
std::size_t next_bucket_count(std::size_t bucket_count) const {
104+
return bucket_count * GrowthFactor;
105+
}
106+
107+
private:
108+
// TODO could be faster
109+
static std::size_t round_up_to_power_of_two(std::size_t value) {
110+
if(is_power_of_two(value)) {
111+
return value;
112+
}
113+
114+
std::size_t power = 1;
115+
while(power < value) {
116+
power <<= 1;
117+
}
118+
119+
return power;
120+
}
121+
122+
static constexpr bool is_power_of_two(std::size_t value) {
123+
return value != 0 && (value & (value - 1)) == 0;
124+
}
125+
126+
private:
127+
static_assert(GrowthFactor >= 2 && is_power_of_two(GrowthFactor), "GrowthFactor must be a power of two >= 2.");
128+
129+
static const std::size_t MIN_BUCKETS_SIZE = 2;
130+
};
131+
132+
133+
134+
135+
namespace detail_array_hash {
136+
137+
static constexpr bool is_power_of_two(std::size_t value) {
138+
return value != 0 && (value & (value - 1)) == 0;
105139
}
106140

107141
/**
@@ -632,8 +666,9 @@ template<class CharT,
632666
class Traits,
633667
bool StoreNullTerminator,
634668
class KeySizeT,
635-
class IndexSizeT>
636-
class array_hash : private value_container<T>, private Hash {
669+
class IndexSizeT,
670+
class GrowthPolicy>
671+
class array_hash : private value_container<T>, private Hash, private GrowthPolicy {
637672
private:
638673
template<typename U>
639674
using has_value = typename std::integral_constant<bool, !std::is_same<U, void>::value>;
@@ -903,9 +938,10 @@ class array_hash : private value_container<T>, private Hash {
903938
public:
904939
array_hash(size_type bucket_count,
905940
const Hash& hash,
906-
float max_load_factor): Hash(hash), m_buckets(round_up_to_power_of_two(bucket_count)),
941+
float max_load_factor): Hash(hash), GrowthPolicy(bucket_count), m_buckets(0),
907942
m_nb_elements(0), m_max_load_factor(max_load_factor)
908943
{
944+
m_buckets.resize(bucket_count);
909945
}
910946

911947
array_hash(const array_hash& other) = default;
@@ -915,6 +951,7 @@ class array_hash : private value_container<T>, private Hash {
915951
std::is_nothrow_move_constructible<std::vector<array_bucket>>::value)
916952
: value_container<T>(std::move(other)),
917953
Hash(std::move(other)),
954+
GrowthPolicy(std::move(other)),
918955
m_buckets(std::move(other.m_buckets)),
919956
m_nb_elements(other.m_nb_elements),
920957
m_max_load_factor(other.m_max_load_factor)
@@ -1078,6 +1115,7 @@ class array_hash : private value_container<T>, private Hash {
10781115

10791116
swap(static_cast<value_container<T>&>(*this), static_cast<value_container<T>&>(other));
10801117
swap(static_cast<Hash&>(*this), static_cast<Hash&>(other));
1118+
swap(static_cast<GrowthPolicy&>(*this), static_cast<GrowthPolicy&>(other));
10811119
swap(m_buckets, other.m_buckets);
10821120
swap(m_nb_elements, other.m_nb_elements);;
10831121
swap(m_max_load_factor, other.m_max_load_factor);
@@ -1217,11 +1255,11 @@ class array_hash : private value_container<T>, private Hash {
12171255
}
12181256

12191257
std::size_t bucket_for_hash(std::size_t hash) const {
1220-
return hash & (m_buckets.size() - 1);
1258+
return GrowthPolicy::bucket_for_hash(hash, m_buckets.size());
12211259
}
12221260

12231261
std::size_t bucket_for_hash(std::size_t hash, size_type bucket_count) const {
1224-
return hash & (bucket_count - 1);
1262+
return GrowthPolicy::bucket_for_hash(hash, bucket_count);
12251263
}
12261264

12271265
typename std::vector<array_bucket>::iterator first_non_empty_bucket_iterator() noexcept {
@@ -1323,11 +1361,7 @@ class array_hash : private value_container<T>, private Hash {
13231361

13241362
void rehash_if_needed() {
13251363
if(load_factor() > m_max_load_factor) {
1326-
if(bucket_count() > max_bucket_count()/2) {
1327-
throw std::length_error("Insertion impossible, too much values in the map.");
1328-
}
1329-
1330-
rehash_impl(bucket_count()*2);
1364+
rehash_impl(GrowthPolicy::next_bucket_count(m_buckets.size()));
13311365
}
13321366
}
13331367

@@ -1383,13 +1417,7 @@ class array_hash : private value_container<T>, private Hash {
13831417
}
13841418

13851419
void rehash_impl(size_type bucket_count) {
1386-
const size_type old_bucket_count = bucket_count;
1387-
bucket_count = round_up_to_power_of_two(bucket_count);
1388-
1389-
// Unsigned overflow, we can't rehash.
1390-
if(bucket_count < old_bucket_count) {
1391-
throw std::length_error("Can't insert value, too much values in the map.");
1392-
}
1420+
GrowthPolicy new_growth_policy(bucket_count);
13931421

13941422
if(bucket_count == this->bucket_count()) {
13951423
return;
@@ -1426,6 +1454,8 @@ class array_hash : private value_container<T>, private Hash {
14261454
ivalue++;
14271455
}
14281456

1457+
static_assert(noexcept(std::swap(static_cast<GrowthPolicy&>(*this), new_growth_policy)), "");
1458+
std::swap(static_cast<GrowthPolicy&>(*this), new_growth_policy);
14291459
m_buckets.swap(new_buckets);
14301460

14311461
try {
@@ -1435,7 +1465,9 @@ class array_hash : private value_container<T>, private Hash {
14351465
}
14361466
catch(...) {
14371467
// Rollback
1468+
std::swap(static_cast<GrowthPolicy&>(*this), new_growth_policy);
14381469
m_buckets.swap(new_buckets);
1470+
14391471
throw;
14401472
}
14411473
}

src/array_map.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ template<class CharT,
6363
class Traits = std::char_traits<CharT>,
6464
bool StoreNullTerminator = true,
6565
class KeySizeT = std::uint16_t,
66-
class IndexSizeT = std::uint32_t>
66+
class IndexSizeT = std::uint32_t,
67+
class GrowthPolicy = tsl::power_of_two_growth_policy<2>>
6768
class array_map {
6869
private:
6970
using ht = tsl::detail_array_hash::array_hash<CharT, T, Hash, Traits, StoreNullTerminator,
70-
KeySizeT, IndexSizeT>;
71+
KeySizeT, IndexSizeT, GrowthPolicy>;
7172

7273
public:
7374
using traits_type = typename ht::traits_type;

src/array_set.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ template<class CharT,
5959
class Traits = std::char_traits<CharT>,
6060
bool StoreNullTerminator = true,
6161
class KeySizeT = std::uint16_t,
62-
class IndexSizeT = std::uint32_t>
62+
class IndexSizeT = std::uint32_t,
63+
class GrowthPolicy = tsl::power_of_two_growth_policy<2>>
6364
class array_set {
6465
private:
6566
using ht = tsl::detail_array_hash::array_hash<CharT, void, Hash, Traits, StoreNullTerminator,
66-
KeySizeT, IndexSizeT>;
67+
KeySizeT, IndexSizeT, GrowthPolicy>;
6768

6869
public:
6970
using traits_type = typename ht::traits_type;
@@ -172,9 +173,7 @@ class array_set {
172173

173174

174175

175-
template<class InputIt,
176-
// Avoid to participate in overload if InputIt is not an iterator.
177-
typename std::enable_if<!std::is_same<typename std::iterator_traits<InputIt>::iterator_category, void>::value>::type* = nullptr>
176+
template<class InputIt>
178177
void insert(InputIt first, InputIt last) {
179178
if(std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<InputIt>::iterator_category>::value) {
180179
reserve(std::distance(first, last));
@@ -357,7 +356,7 @@ class array_set {
357356

358357
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
359358
const auto it_element_rhs = rhs.find(it.key(), it.key_size());
360-
if(it_element_rhs == rhs.cend() || it_element_rhs.value() != it_element_rhs.value()) {
359+
if(it_element_rhs == rhs.cend()) {
361360
return false;
362361
}
363362
}

0 commit comments

Comments
 (0)