6 #ifndef METALL_CONTAINER_CONCURRENT_STRING_KEY_STORE_HPP
7 #define METALL_CONTAINER_CONCURRENT_STRING_KEY_STORE_HPP
10 #include <string_view>
30 template <
typename _value_type,
35 using other_allocator =
36 typename std::allocator_traits<allocator_type>::template rebind_alloc<T>;
39 using other_scoped_allocator =
42 using internal_id_type = uint64_t;
43 using internal_string_type =
45 using internal_value_type = std::tuple<internal_string_type, _value_type>;
48 internal_id_type, internal_value_type, std::hash<internal_id_type>,
49 std::equal_to<internal_id_type>,
50 other_scoped_allocator<
51 std::pair<const internal_id_type, internal_value_type>>>;
54 static constexpr internal_id_type k_max_internal_id =
55 std::numeric_limits<internal_id_type>::max();
73 const allocator_type &allocator = allocator_type())
81 : m_unique(other.m_unique),
82 m_hash_seed(other.m_hash_seed),
83 m_map(other.m_map, alloc) {}
90 const allocator_type &alloc) noexcept
91 : m_unique(other.m_unique),
92 m_hash_seed(other.m_hash_seed),
93 m_map(std::move(other.m_map), alloc) {}
107 const auto internal_id = priv_find_or_generate_internal_id(
key);
108 if (m_unique && m_map.count(internal_id) == 1) {
116 auto internal_value =
117 internal_value_type{std::allocator_arg, m_map.get_allocator()};
118 std::get<0>(internal_value) =
key;
120 m_map.emplace(internal_id, std::move(internal_value));
131 const auto internal_id = priv_find_or_generate_internal_id(
key);
133 assert(!m_unique || m_map.count(internal_id) <= 1);
134 if (m_unique && m_map.count(internal_id) == 1) {
135 assert(m_map.count(internal_id) == 1);
136 std::get<1>(m_map.find(internal_id)->second) =
value;
138 m_map.emplace(internal_id, internal_value_type{std::allocator_arg,
139 m_map.get_allocator(),
150 const auto internal_id = priv_find_or_generate_internal_id(
key);
152 assert(!m_unique || m_map.count(internal_id) <= 1);
153 if (m_unique && m_map.count(internal_id) == 1) {
154 std::get<1>(m_map.find(internal_id)->second) = std::move(
value);
156 m_map.emplace(internal_id, internal_value_type{
157 std::allocator_arg, m_map.get_allocator(),
166 m_max_id_probe_distance = 0;
173 const auto internal_id = priv_find_internal_id(
key);
174 return m_map.count(internal_id);
179 std::size_t
size()
const {
return m_map.size(); }
185 const auto &
key = std::get<0>(position.m_iterator->second);
194 auto non_const_iterator =
195 m_map.erase(position.m_iterator, position.m_iterator);
196 return std::get<1>(non_const_iterator->second);
203 return std::get<1>(position.m_iterator->second);
210 const auto internal_id = priv_find_internal_id(
key);
220 const auto internal_id = priv_find_internal_id(
key);
221 const auto range = m_map.equal_range(internal_id);
238 const auto internal_id = priv_find_internal_id(
key);
239 return m_map.erase(internal_id);
246 if (position ==
end())
return end();
260 assert(m_map.empty());
261 for (
auto &elem : old_map) {
262 insert(std::get<0>(elem.second), std::move(std::get<1>(elem.second)));
281 internal_id_type priv_generate_internal_id(
const key_type &
key) {
282 auto internal_id = priv_hash_key(
key, m_hash_seed);
284 std::size_t distance = 0;
285 while (m_map.count(internal_id) > 0) {
286 internal_id = priv_increment_internal_id(internal_id);
289 m_max_id_probe_distance = std::max(distance, m_max_id_probe_distance);
297 internal_id_type priv_find_internal_id(
const key_type &
key)
const {
298 auto internal_id = priv_hash_key(
key, m_hash_seed);
300 for (std::size_t d = 0; d <= m_max_id_probe_distance; ++d) {
301 const auto itr = m_map.find(internal_id);
302 if (itr == m_map.end()) {
306 if (std::get<0>(itr->second) ==
key) {
309 internal_id = priv_increment_internal_id(internal_id);
312 return k_max_internal_id;
318 internal_id_type priv_find_or_generate_internal_id(
const key_type &
key) {
319 auto internal_id = priv_find_internal_id(
key);
320 if (internal_id == k_max_internal_id) {
322 internal_id = priv_generate_internal_id(
key);
327 static internal_id_type priv_hash_key(
const key_type &
key,
328 [[maybe_unused]]
const uint64_t seed) {
329 #ifdef METALL_CONTAINER_STRING_KEY_STORE_USE_SIMPLE_HASH
330 internal_id_type
hash =
key.empty() ? 0 : (uint8_t)
key[0] % 2;
332 auto hash = (internal_id_type)metall::mtlldetail::murmur_hash_64a(
333 key.data(), (int)
key.length(), seed);
335 if (
hash == k_max_internal_id) {
336 hash = priv_increment_internal_id(
hash);
338 assert(
hash != k_max_internal_id);
342 static internal_id_type priv_increment_internal_id(
343 const internal_id_type
id) {
344 const auto new_id = (
id + 1) % k_max_internal_id;
345 assert(new_id != k_max_internal_id);
349 bool m_unique{
false};
350 uint64_t m_hash_seed{123};
351 map_type m_map{allocator_type{}};
352 std::size_t m_max_id_probe_distance{0};