insert(P&& value) {
+ return insert_impl(KeySelect()(value), std::forward(value));
+ }
+
+ template
+ iterator insert_hint(const_iterator hint, P&& value) {
+ if (hint != cend() &&
+ compare_keys(KeySelect()(*hint), KeySelect()(value))) {
+ return mutable_iterator(hint);
+ }
+
+ return insert(std::forward(value)).first;
+ }
+
+ template
+ void insert(InputIt first, InputIt last) {
+ if (std::is_base_of<
+ std::forward_iterator_tag,
+ typename std::iterator_traits::iterator_category>::value) {
+ const auto nb_elements_insert = std::distance(first, last);
+ const size_type nb_free_buckets = m_load_threshold - size();
+ tsl_oh_assert(m_load_threshold >= size());
+
+ if (nb_elements_insert > 0 &&
+ nb_free_buckets < size_type(nb_elements_insert)) {
+ reserve(size() + size_type(nb_elements_insert));
+ }
+ }
+
+ for (; first != last; ++first) {
+ insert(*first);
+ }
+ }
+
+ template
+ std::pair insert_or_assign(K&& key, M&& value) {
+ auto it = try_emplace(std::forward(key), std::forward(value));
+ if (!it.second) {
+ it.first.value() = std::forward(value);
+ }
+
+ return it;
+ }
+
+ template
+ iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) {
+ if (hint != cend() && compare_keys(KeySelect()(*hint), key)) {
+ auto it = mutable_iterator(hint);
+ it.value() = std::forward(obj);
+
+ return it;
+ }
+
+ return insert_or_assign(std::forward(key), std::forward(obj)).first;
+ }
+
+ template
+ std::pair emplace(Args&&... args) {
+ return insert(value_type(std::forward(args)...));
+ }
+
+ template
+ iterator emplace_hint(const_iterator hint, Args&&... args) {
+ return insert_hint(hint, value_type(std::forward(args)...));
+ }
+
+ template
+ std::pair try_emplace(K&& key, Args&&... value_args) {
+ return insert_impl(
+ key, std::piecewise_construct,
+ std::forward_as_tuple(std::forward(key)),
+ std::forward_as_tuple(std::forward(value_args)...));
+ }
+
+ template
+ iterator try_emplace_hint(const_iterator hint, K&& key, Args&&... args) {
+ if (hint != cend() && compare_keys(KeySelect()(*hint), key)) {
+ return mutable_iterator(hint);
+ }
+
+ return try_emplace(std::forward(key), std::forward(args)...).first;
+ }
+
+ /**
+ * Here to avoid `template size_type erase(const K& key)` being used
+ * when we use an `iterator` instead of a `const_iterator`.
+ */
+ iterator erase(iterator pos) { return erase(const_iterator(pos)); }
+
+ iterator erase(const_iterator pos) {
+ tsl_oh_assert(pos != cend());
+
+ const std::size_t index_erase = iterator_to_index(pos);
+
+ auto it_bucket = find_key(pos.key(), hash_key(pos.key()));
+ tsl_oh_assert(it_bucket != m_buckets_data.end());
+
+ erase_value_from_bucket(it_bucket);
+
+ /*
+ * One element was removed from m_values, due to the left shift the next
+ * element is now at the position of the previous element (or end if none).
+ */
+ return begin() + index_erase;
+ }
+
+ iterator erase(const_iterator first, const_iterator last) {
+ if (first == last) {
+ return mutable_iterator(first);
+ }
+
+ tsl_oh_assert(std::distance(first, last) > 0);
+ const std::size_t start_index = iterator_to_index(first);
+ const std::size_t nb_values = std::size_t(std::distance(first, last));
+ const std::size_t end_index = start_index + nb_values;
+
+ // Delete all values
+#ifdef TSL_OH_NO_CONTAINER_ERASE_CONST_ITERATOR
+ auto next_it = m_values.erase(mutable_iterator(first).m_iterator,
+ mutable_iterator(last).m_iterator);
+#else
+ auto next_it = m_values.erase(first.m_iterator, last.m_iterator);
+#endif
+
+ /*
+ * Mark the buckets corresponding to the values as empty and do a backward
+ * shift.
+ *
+ * Also, the erase operation on m_values has shifted all the values on the
+ * right of last.m_iterator. Adapt the indexes for these values.
+ */
+ std::size_t ibucket = 0;
+ while (ibucket < m_buckets_data.size()) {
+ if (m_buckets[ibucket].empty()) {
+ ibucket++;
+ } else if (m_buckets[ibucket].index() >= start_index &&
+ m_buckets[ibucket].index() < end_index) {
+ m_buckets[ibucket].clear();
+ backward_shift(ibucket);
+ // Don't increment ibucket, backward_shift may have replaced current
+ // bucket.
+ } else if (m_buckets[ibucket].index() >= end_index) {
+ m_buckets[ibucket].set_index(
+ index_type(m_buckets[ibucket].index() - nb_values));
+ ibucket++;
+ } else {
+ ibucket++;
+ }
+ }
+
+ return iterator(next_it);
+ }
+
+ template
+ size_type erase(const K& key) {
+ return erase(key, hash_key(key));
+ }
+
+ template
+ size_type erase(const K& key, std::size_t hash) {
+ return erase_impl(key, hash);
+ }
+
+ void swap(ordered_hash& other) {
+ using std::swap;
+
+ swap(static_cast(*this), static_cast(other));
+ swap(static_cast(*this), static_cast(other));
+ swap(m_buckets_data, other.m_buckets_data);
+ swap(m_buckets, other.m_buckets);
+ swap(m_hash_mask, other.m_hash_mask);
+ swap(m_values, other.m_values);
+ swap(m_load_threshold, other.m_load_threshold);
+ swap(m_max_load_factor, other.m_max_load_factor);
+ swap(m_grow_on_next_insert, other.m_grow_on_next_insert);
+ }
+
+ /*
+ * Lookup
+ */
+ template ::value>::type* = nullptr>
+ typename U::value_type& at(const K& key) {
+ return at(key, hash_key(key));
+ }
+
+ template ::value>::type* = nullptr>
+ typename U::value_type& at(const K& key, std::size_t hash) {
+ return const_cast(
+ static_cast(this)->at(key, hash));
+ }
+
+ template ::value>::type* = nullptr>
+ const typename U::value_type& at(const K& key) const {
+ return at(key, hash_key(key));
+ }
+
+ template ::value>::type* = nullptr>
+ const typename U::value_type& at(const K& key, std::size_t hash) const {
+ auto it = find(key, hash);
+ if (it != end()) {
+ return it.value();
+ } else {
+ TSL_OH_THROW_OR_TERMINATE(std::out_of_range, "Couldn't find the key.");
+ }
+ }
+
+ template ::value>::type* = nullptr>
+ typename U::value_type& operator[](K&& key) {
+ return try_emplace(std::forward(key)).first.value();
+ }
+
+ template
+ size_type count(const K& key) const {
+ return count(key, hash_key(key));
+ }
+
+ template
+ size_type count(const K& key, std::size_t hash) const {
+ if (find(key, hash) == cend()) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ template
+ iterator find(const K& key) {
+ return find(key, hash_key(key));
+ }
+
+ template
+ iterator find(const K& key, std::size_t hash) {
+ auto it_bucket = find_key(key, hash);
+ return (it_bucket != m_buckets_data.end())
+ ? iterator(m_values.begin() + it_bucket->index())
+ : end();
+ }
+
+ template
+ const_iterator find(const K& key) const {
+ return find(key, hash_key(key));
+ }
+
+ template
+ const_iterator find(const K& key, std::size_t hash) const {
+ auto it_bucket = find_key(key, hash);
+ return (it_bucket != m_buckets_data.cend())
+ ? const_iterator(m_values.begin() + it_bucket->index())
+ : end();
+ }
+
+ template
+ bool contains(const K& key) const {
+ return contains(key, hash_key(key));
+ }
+
+ template
+ bool contains(const K& key, std::size_t hash) const {
+ return find(key, hash) != cend();
+ }
+
+ template
+ std::pair equal_range(const K& key) {
+ return equal_range(key, hash_key(key));
+ }
+
+ template
+ std::pair equal_range(const K& key, std::size_t hash) {
+ iterator it = find(key, hash);
+ return std::make_pair(it, (it == end()) ? it : std::next(it));
+ }
+
+ template
+ std::pair equal_range(const K& key) const {
+ return equal_range(key, hash_key(key));
+ }
+
+ template
+ std::pair equal_range(
+ const K& key, std::size_t hash) const {
+ const_iterator it = find(key, hash);
+ return std::make_pair(it, (it == cend()) ? it : std::next(it));
+ }
+
+ /*
+ * Bucket interface
+ */
+ size_type bucket_count() const { return m_buckets_data.size(); }
+
+ size_type max_bucket_count() const { return m_buckets_data.max_size(); }
+
+ /*
+ * Hash policy
+ */
+ float load_factor() const {
+ if (bucket_count() == 0) {
+ return 0;
+ }
+
+ return float(size()) / float(bucket_count());
+ }
+
+ float max_load_factor() const { return m_max_load_factor; }
+
+ void max_load_factor(float ml) {
+ m_max_load_factor = clamp(ml, float(MAX_LOAD_FACTOR__MINIMUM),
+ float(MAX_LOAD_FACTOR__MAXIMUM));
+
+ m_max_load_factor = ml;
+ m_load_threshold = size_type(float(bucket_count()) * m_max_load_factor);
+ }
+
+ void rehash(size_type count) {
+ count = std::max(count,
+ size_type(std::ceil(float(size()) / max_load_factor())));
+ rehash_impl(count);
+ }
+
+ void reserve(size_type count) {
+ reserve_space_for_values(count);
+
+ count = size_type(std::ceil(float(count) / max_load_factor()));
+ rehash(count);
+ }
+
+ /*
+ * Observers
+ */
+ hasher hash_function() const { return static_cast(*this); }
+
+ key_equal key_eq() const { return static_cast(*this); }
+
+ /*
+ * Other
+ */
+ iterator mutable_iterator(const_iterator pos) {
+ return iterator(m_values.begin() + iterator_to_index(pos));
+ }
+
+ iterator nth(size_type index) {
+ tsl_oh_assert(index <= size());
+ return iterator(m_values.begin() + index);
+ }
+
+ const_iterator nth(size_type index) const {
+ tsl_oh_assert(index <= size());
+ return const_iterator(m_values.cbegin() + index);
+ }
+
+ const_reference front() const {
+ tsl_oh_assert(!empty());
+ return m_values.front();
+ }
+
+ const_reference back() const {
+ tsl_oh_assert(!empty());
+ return m_values.back();
+ }
+
+ const values_container_type& values_container() const noexcept {
+ return m_values;
+ }
+
+ values_container_type release() {
+ values_container_type ret;
+ for (auto& bucket : m_buckets_data) {
+ bucket.clear();
+ }
+ m_grow_on_next_insert = false;
+ std::swap(ret, m_values);
+ return ret;
+ }
+
+ template ::value>::type* = nullptr>
+ const typename values_container_type::value_type* data() const noexcept {
+ return m_values.data();
+ }
+
+ template ::value>::type* = nullptr>
+ size_type capacity() const noexcept {
+ return m_values.capacity();
+ }
+
+ void shrink_to_fit() { m_values.shrink_to_fit(); }
+
+ template
+ std::pair insert_at_position(const_iterator pos, P&& value) {
+ return insert_at_position_impl(pos.m_iterator, KeySelect()(value),
+ std::forward(value));
+ }
+
+ template
+ std::pair emplace_at_position(const_iterator pos,
+ Args&&... args) {
+ return insert_at_position(pos, value_type(std::forward(args)...));
+ }
+
+ template
+ std::pair try_emplace_at_position(const_iterator pos, K&& key,
+ Args&&... value_args) {
+ return insert_at_position_impl(
+ pos.m_iterator, key, std::piecewise_construct,
+ std::forward_as_tuple(std::forward(key)),
+ std::forward_as_tuple(std::forward(value_args)...));
+ }
+
+ void pop_back() {
+ tsl_oh_assert(!empty());
+ erase(std::prev(end()));
+ }
+
+ /**
+ * Here to avoid `template size_type unordered_erase(const K& key)`
+ * being used when we use a iterator instead of a const_iterator.
+ */
+ iterator unordered_erase(iterator pos) {
+ return unordered_erase(const_iterator(pos));
+ }
+
+ iterator unordered_erase(const_iterator pos) {
+ const std::size_t index_erase = iterator_to_index(pos);
+ unordered_erase(pos.key());
+
+ /*
+ * One element was deleted, index_erase now points to the next element as
+ * the elements after the deleted value were shifted to the left in m_values
+ * (will be end() if we deleted the last element).
+ */
+ return begin() + index_erase;
+ }
+
+ template
+ size_type unordered_erase(const K& key) {
+ return unordered_erase(key, hash_key(key));
+ }
+
+ template
+ size_type unordered_erase(const K& key, std::size_t hash) {
+ auto it_bucket_key = find_key(key, hash);
+ if (it_bucket_key == m_buckets_data.end()) {
+ return 0;
+ }
+
+ /**
+ * If we are not erasing the last element in m_values, we swap
+ * the element we are erasing with the last element. We then would
+ * just have to do a pop_back() in m_values.
+ */
+ if (!compare_keys(key, KeySelect()(back()))) {
+ auto it_bucket_last_elem =
+ find_key(KeySelect()(back()), hash_key(KeySelect()(back())));
+ tsl_oh_assert(it_bucket_last_elem != m_buckets_data.end());
+ tsl_oh_assert(it_bucket_last_elem->index() == m_values.size() - 1);
+
+ using std::swap;
+ swap(m_values[it_bucket_key->index()],
+ m_values[it_bucket_last_elem->index()]);
+ swap(it_bucket_key->index_ref(), it_bucket_last_elem->index_ref());
+ }
+
+ erase_value_from_bucket(it_bucket_key);
+
+ return 1;
+ }
+
+ /**
+ * Remove all entries for which the given predicate matches.
+ */
+ template
+ size_type erase_if(Predicate& pred) {
+ // Get the bucket associated with the given element.
+ auto get_bucket = [this](typename values_container_type::iterator it) {
+ return find_key(KeySelect()(*it), hash_key(KeySelect()(*it)));
+ };
+ // Clear a bucket without touching the container holding the values.
+ auto clear_bucket = [this](typename buckets_container_type::iterator it) {
+ tsl_oh_assert(it != m_buckets_data.end());
+ it->clear();
+ backward_shift(std::size_t(std::distance(m_buckets_data.begin(), it)));
+ };
+ // Ensure that only const references are passed to the predicate.
+ auto cpred = [&pred](typename values_container_type::const_reference x) {
+ return pred(x);
+ };
+
+ // Find first element that matches the predicate.
+ const auto last = m_values.end();
+ auto first = std::find_if(m_values.begin(), last, cpred);
+ if (first == last) {
+ return 0;
+ }
+ // Remove all elements that match the predicate.
+ clear_bucket(get_bucket(first));
+ for (auto it = std::next(first); it != last; ++it) {
+ auto it_bucket = get_bucket(it);
+ if (cpred(*it)) {
+ clear_bucket(it_bucket);
+ } else {
+ it_bucket->set_index(
+ static_cast(std::distance(m_values.begin(), first)));
+ *first++ = std::move(*it);
+ }
+ }
+ // Resize the vector and return the number of deleted elements.
+ auto deleted = static_cast(std::distance(first, last));
+ m_values.erase(first, last);
+ return deleted;
+ }
+
+ template
+ void serialize(Serializer& serializer) const {
+ serialize_impl(serializer);
+ }
+
+ template
+ void deserialize(Deserializer& deserializer, bool hash_compatible) {
+ deserialize_impl(deserializer, hash_compatible);
+ }
+
+ friend bool operator==(const ordered_hash& lhs, const ordered_hash& rhs) {
+ return lhs.m_values == rhs.m_values;
+ }
+
+ friend bool operator!=(const ordered_hash& lhs, const ordered_hash& rhs) {
+ return lhs.m_values != rhs.m_values;
+ }
+
+ friend bool operator<(const ordered_hash& lhs, const ordered_hash& rhs) {
+ return lhs.m_values < rhs.m_values;
+ }
+
+ friend bool operator<=(const ordered_hash& lhs, const ordered_hash& rhs) {
+ return lhs.m_values <= rhs.m_values;
+ }
+
+ friend bool operator>(const ordered_hash& lhs, const ordered_hash& rhs) {
+ return lhs.m_values > rhs.m_values;
+ }
+
+ friend bool operator>=(const ordered_hash& lhs, const ordered_hash& rhs) {
+ return lhs.m_values >= rhs.m_values;
+ }
+
+ private:
+ template
+ std::size_t hash_key(const K& key) const {
+ return Hash::operator()(key);
+ }
+
+ template
+ bool compare_keys(const K1& key1, const K2& key2) const {
+ return KeyEqual::operator()(key1, key2);
+ }
+
+ template
+ typename buckets_container_type::iterator find_key(const K& key,
+ std::size_t hash) {
+ auto it = static_cast(this)->find_key(key, hash);
+ return m_buckets_data.begin() + std::distance(m_buckets_data.cbegin(), it);
+ }
+
+ /**
+ * Return bucket which has the key 'key' or m_buckets_data.end() if none.
+ *
+ * From the bucket_for_hash, search for the value until we either find an
+ * empty bucket or a bucket which has a value with a distance from its ideal
+ * bucket longer than the probe length for the value we are looking for.
+ */
+ template
+ typename buckets_container_type::const_iterator find_key(
+ const K& key, std::size_t hash) const {
+ for (std::size_t ibucket = bucket_for_hash(hash),
+ dist_from_ideal_bucket = 0;
+ ; ibucket = next_bucket(ibucket), dist_from_ideal_bucket++) {
+ if (m_buckets[ibucket].empty()) {
+ return m_buckets_data.end();
+ } else if (m_buckets[ibucket].truncated_hash() ==
+ bucket_entry::truncate_hash(hash) &&
+ compare_keys(
+ key, KeySelect()(m_values[m_buckets[ibucket].index()]))) {
+ return m_buckets_data.begin() + ibucket;
+ } else if (dist_from_ideal_bucket > distance_from_ideal_bucket(ibucket)) {
+ return m_buckets_data.end();
+ }
+ }
+ }
+
+ void rehash_impl(size_type bucket_count) {
+ tsl_oh_assert(bucket_count >=
+ size_type(std::ceil(float(size()) / max_load_factor())));
+
+ if (bucket_count > max_bucket_count()) {
+ TSL_OH_THROW_OR_TERMINATE(std::length_error,
+ "The map exceeds its maximum size.");
+ }
+
+ if (bucket_count > 0) {
+ bucket_count = round_up_to_power_of_two(bucket_count);
+ }
+
+ if (bucket_count == this->bucket_count()) {
+ return;
+ }
+
+ buckets_container_type old_buckets(bucket_count);
+ m_buckets_data.swap(old_buckets);
+ m_buckets = m_buckets_data.empty() ? static_empty_bucket_ptr()
+ : m_buckets_data.data();
+ // Everything should be noexcept from here.
+
+ m_hash_mask = (bucket_count > 0) ? (bucket_count - 1) : 0;
+ this->max_load_factor(m_max_load_factor);
+ m_grow_on_next_insert = false;
+
+ for (const bucket_entry& old_bucket : old_buckets) {
+ if (old_bucket.empty()) {
+ continue;
+ }
+
+ truncated_hash_type insert_hash = old_bucket.truncated_hash();
+ index_type insert_index = old_bucket.index();
+
+ for (std::size_t ibucket = bucket_for_hash(insert_hash),
+ dist_from_ideal_bucket = 0;
+ ; ibucket = next_bucket(ibucket), dist_from_ideal_bucket++) {
+ if (m_buckets[ibucket].empty()) {
+ m_buckets[ibucket].set_index(insert_index);
+ m_buckets[ibucket].set_hash(insert_hash);
+ break;
+ }
+
+ const std::size_t distance = distance_from_ideal_bucket(ibucket);
+ if (dist_from_ideal_bucket > distance) {
+ std::swap(insert_index, m_buckets[ibucket].index_ref());
+ std::swap(insert_hash, m_buckets[ibucket].truncated_hash_ref());
+ dist_from_ideal_bucket = distance;
+ }
+ }
+ }
+ }
+
+ template ::value>::type* = nullptr>
+ void reserve_space_for_values(size_type count) {
+ m_values.reserve(count);
+ }
+
+ template ::value>::type* = nullptr>
+ void reserve_space_for_values(size_type /*count*/) {}
+
+ /**
+ * Swap the empty bucket with the values on its right until we cross another
+ * empty bucket or if the other bucket has a distance_from_ideal_bucket == 0.
+ */
+ void backward_shift(std::size_t empty_ibucket) noexcept {
+ tsl_oh_assert(m_buckets[empty_ibucket].empty());
+
+ std::size_t previous_ibucket = empty_ibucket;
+ for (std::size_t current_ibucket = next_bucket(previous_ibucket);
+ !m_buckets[current_ibucket].empty() &&
+ distance_from_ideal_bucket(current_ibucket) > 0;
+ previous_ibucket = current_ibucket,
+ current_ibucket = next_bucket(current_ibucket)) {
+ std::swap(m_buckets[current_ibucket], m_buckets[previous_ibucket]);
+ }
+ }
+
+ void erase_value_from_bucket(
+ typename buckets_container_type::iterator it_bucket) {
+ tsl_oh_assert(it_bucket != m_buckets_data.end() && !it_bucket->empty());
+
+ m_values.erase(m_values.begin() + it_bucket->index());
+
+ /*
+ * m_values.erase shifted all the values on the right of the erased value,
+ * shift the indexes by -1 in the buckets array for these values.
+ */
+ if (it_bucket->index() != m_values.size()) {
+ shift_indexes_in_buckets(it_bucket->index() + 1, -1);
+ }
+
+ // Mark the bucket as empty and do a backward shift of the values on the
+ // right
+ it_bucket->clear();
+ backward_shift(
+ std::size_t(std::distance(m_buckets_data.begin(), it_bucket)));
+ }
+
+ /**
+ * Shift any index >= index_above_or_equal in m_buckets_data by delta.
+ *
+ * delta must be equal to 1 or -1.
+ */
+ void shift_indexes_in_buckets(index_type index_above_or_equal,
+ int delta) noexcept {
+ tsl_oh_assert(delta == 1 || delta == -1);
+
+ for (bucket_entry& bucket : m_buckets_data) {
+ if (!bucket.empty() && bucket.index() >= index_above_or_equal) {
+ tsl_oh_assert(delta >= 0 ||
+ bucket.index() >= static_cast(-delta));
+ tsl_oh_assert(delta <= 0 ||
+ (bucket_entry::max_size() - bucket.index()) >=
+ static_cast(delta));
+ bucket.set_index(static_cast(bucket.index() + delta));
+ }
+ }
+ }
+
+ template
+ size_type erase_impl(const K& key, std::size_t hash) {
+ auto it_bucket = find_key(key, hash);
+ if (it_bucket != m_buckets_data.end()) {
+ erase_value_from_bucket(it_bucket);
+
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Insert the element at the end.
+ */
+ template
+ std::pair insert_impl(const K& key,
+ Args&&... value_type_args) {
+ const std::size_t hash = hash_key(key);
+
+ std::size_t ibucket = bucket_for_hash(hash);
+ std::size_t dist_from_ideal_bucket = 0;
+
+ while (!m_buckets[ibucket].empty() &&
+ dist_from_ideal_bucket <= distance_from_ideal_bucket(ibucket)) {
+ if (m_buckets[ibucket].truncated_hash() ==
+ bucket_entry::truncate_hash(hash) &&
+ compare_keys(key,
+ KeySelect()(m_values[m_buckets[ibucket].index()]))) {
+ return std::make_pair(begin() + m_buckets[ibucket].index(), false);
+ }
+
+ ibucket = next_bucket(ibucket);
+ dist_from_ideal_bucket++;
+ }
+
+ if (size() >= max_size()) {
+ TSL_OH_THROW_OR_TERMINATE(
+ std::length_error, "We reached the maximum size for the hash table.");
+ }
+
+ if (grow_on_high_load()) {
+ ibucket = bucket_for_hash(hash);
+ dist_from_ideal_bucket = 0;
+ }
+
+ m_values.emplace_back(std::forward(value_type_args)...);
+ insert_index(ibucket, dist_from_ideal_bucket,
+ index_type(m_values.size() - 1),
+ bucket_entry::truncate_hash(hash));
+
+ return std::make_pair(std::prev(end()), true);
+ }
+
+ /**
+ * Insert the element before insert_position.
+ */
+ template
+ std::pair insert_at_position_impl(
+ typename values_container_type::const_iterator insert_position,
+ const K& key, Args&&... value_type_args) {
+ const std::size_t hash = hash_key(key);
+
+ std::size_t ibucket = bucket_for_hash(hash);
+ std::size_t dist_from_ideal_bucket = 0;
+
+ while (!m_buckets[ibucket].empty() &&
+ dist_from_ideal_bucket <= distance_from_ideal_bucket(ibucket)) {
+ if (m_buckets[ibucket].truncated_hash() ==
+ bucket_entry::truncate_hash(hash) &&
+ compare_keys(key,
+ KeySelect()(m_values[m_buckets[ibucket].index()]))) {
+ return std::make_pair(begin() + m_buckets[ibucket].index(), false);
+ }
+
+ ibucket = next_bucket(ibucket);
+ dist_from_ideal_bucket++;
+ }
+
+ if (size() >= max_size()) {
+ TSL_OH_THROW_OR_TERMINATE(
+ std::length_error, "We reached the maximum size for the hash table.");
+ }
+
+ if (grow_on_high_load()) {
+ ibucket = bucket_for_hash(hash);
+ dist_from_ideal_bucket = 0;
+ }
+
+ const index_type index_insert_position =
+ index_type(std::distance(m_values.cbegin(), insert_position));
+
+#ifdef TSL_OH_NO_CONTAINER_EMPLACE_CONST_ITERATOR
+ m_values.emplace(
+ m_values.begin() + std::distance(m_values.cbegin(), insert_position),
+ std::forward(value_type_args)...);
+#else
+ m_values.emplace(insert_position, std::forward(value_type_args)...);
+#endif
+
+ /*
+ * The insertion didn't happend at the end of the m_values container,
+ * we need to shift the indexes in m_buckets_data.
+ */
+ if (index_insert_position != m_values.size() - 1) {
+ shift_indexes_in_buckets(index_insert_position, 1);
+ }
+
+ insert_index(ibucket, dist_from_ideal_bucket, index_insert_position,
+ bucket_entry::truncate_hash(hash));
+
+ return std::make_pair(iterator(m_values.begin() + index_insert_position),
+ true);
+ }
+
+ void insert_index(std::size_t ibucket, std::size_t dist_from_ideal_bucket,
+ index_type index_insert,
+ truncated_hash_type hash_insert) noexcept {
+ while (!m_buckets[ibucket].empty()) {
+ const std::size_t distance = distance_from_ideal_bucket(ibucket);
+ if (dist_from_ideal_bucket > distance) {
+ std::swap(index_insert, m_buckets[ibucket].index_ref());
+ std::swap(hash_insert, m_buckets[ibucket].truncated_hash_ref());
+
+ dist_from_ideal_bucket = distance;
+ }
+
+ ibucket = next_bucket(ibucket);
+ dist_from_ideal_bucket++;
+
+ if (dist_from_ideal_bucket > REHASH_ON_HIGH_NB_PROBES__NPROBES &&
+ !m_grow_on_next_insert &&
+ load_factor() >= REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR) {
+ // We don't want to grow the map now as we need this method to be
+ // noexcept. Do it on next insert.
+ m_grow_on_next_insert = true;
+ }
+ }
+
+ m_buckets[ibucket].set_index(index_insert);
+ m_buckets[ibucket].set_hash(hash_insert);
+ }
+
+ std::size_t distance_from_ideal_bucket(std::size_t ibucket) const noexcept {
+ const std::size_t ideal_bucket =
+ bucket_for_hash(m_buckets[ibucket].truncated_hash());
+
+ if (ibucket >= ideal_bucket) {
+ return ibucket - ideal_bucket;
+ }
+ // If the bucket is smaller than the ideal bucket for the value, there was a
+ // wrapping at the end of the bucket array due to the modulo.
+ else {
+ return (bucket_count() + ibucket) - ideal_bucket;
+ }
+ }
+
+ std::size_t next_bucket(std::size_t index) const noexcept {
+ tsl_oh_assert(index < m_buckets_data.size());
+
+ index++;
+ return (index < m_buckets_data.size()) ? index : 0;
+ }
+
+ std::size_t bucket_for_hash(std::size_t hash) const noexcept {
+ return hash & m_hash_mask;
+ }
+
+ std::size_t iterator_to_index(const_iterator it) const noexcept {
+ const auto dist = std::distance(cbegin(), it);
+ tsl_oh_assert(dist >= 0);
+
+ return std::size_t(dist);
+ }
+
+ /**
+ * Return true if the map has been rehashed.
+ */
+ bool grow_on_high_load() {
+ if (m_grow_on_next_insert || size() >= m_load_threshold) {
+ rehash_impl(std::max(size_type(1), bucket_count() * 2));
+ m_grow_on_next_insert = false;
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ template
+ void serialize_impl(Serializer& serializer) const {
+ const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION;
+ serializer(version);
+
+ const slz_size_type nb_elements = m_values.size();
+ serializer(nb_elements);
+
+ const slz_size_type bucket_count = m_buckets_data.size();
+ serializer(bucket_count);
+
+ const float max_load_factor = m_max_load_factor;
+ serializer(max_load_factor);
+
+ for (const value_type& value : m_values) {
+ serializer(value);
+ }
+
+ for (const bucket_entry& bucket : m_buckets_data) {
+ bucket.serialize(serializer);
+ }
+ }
+
+ template
+ void deserialize_impl(Deserializer& deserializer, bool hash_compatible) {
+ tsl_oh_assert(m_buckets_data.empty()); // Current hash table must be empty
+
+ const slz_size_type version =
+ deserialize_value(deserializer);
+ // For now we only have one version of the serialization protocol.
+ // If it doesn't match there is a problem with the file.
+ if (version != SERIALIZATION_PROTOCOL_VERSION) {
+ TSL_OH_THROW_OR_TERMINATE(std::runtime_error,
+ "Can't deserialize the ordered_map/set. "
+ "The protocol version header is invalid.");
+ }
+
+ const slz_size_type nb_elements =
+ deserialize_value(deserializer);
+ const slz_size_type bucket_count_ds =
+ deserialize_value(deserializer);
+ const float max_load_factor = deserialize_value(deserializer);
+
+ if (max_load_factor < MAX_LOAD_FACTOR__MINIMUM ||
+ max_load_factor > MAX_LOAD_FACTOR__MAXIMUM) {
+ TSL_OH_THROW_OR_TERMINATE(
+ std::runtime_error,
+ "Invalid max_load_factor. Check that the serializer "
+ "and deserializer support floats correctly as they "
+ "can be converted implicitly to ints.");
+ }
+
+ this->max_load_factor(max_load_factor);
+
+ if (bucket_count_ds == 0) {
+ tsl_oh_assert(nb_elements == 0);
+ return;
+ }
+
+ if (!hash_compatible) {
+ reserve(numeric_cast(nb_elements,
+ "Deserialized nb_elements is too big."));
+ for (slz_size_type el = 0; el < nb_elements; el++) {
+ insert(deserialize_value(deserializer));
+ }
+ } else {
+ m_buckets_data.reserve(numeric_cast(
+ bucket_count_ds, "Deserialized bucket_count is too big."));
+ m_buckets = m_buckets_data.data(),
+ m_hash_mask = m_buckets_data.capacity() - 1;
+
+ reserve_space_for_values(numeric_cast(
+ nb_elements, "Deserialized nb_elements is too big."));
+ for (slz_size_type el = 0; el < nb_elements; el++) {
+ m_values.push_back(deserialize_value(deserializer));
+ }
+
+ for (slz_size_type b = 0; b < bucket_count_ds; b++) {
+ m_buckets_data.push_back(bucket_entry::deserialize(deserializer));
+ }
+ }
+ }
+
+ static std::size_t round_up_to_power_of_two(std::size_t value) {
+ if (is_power_of_two(value)) {
+ return value;
+ }
+
+ if (value == 0) {
+ return 1;
+ }
+
+ --value;
+ for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
+ value |= value >> i;
+ }
+
+ return value + 1;
+ }
+
+ static constexpr bool is_power_of_two(std::size_t value) {
+ return value != 0 && (value & (value - 1)) == 0;
+ }
+
+ public:
+ static const size_type DEFAULT_INIT_BUCKETS_SIZE = 0;
+ static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.75f;
+
+ private:
+ static constexpr float MAX_LOAD_FACTOR__MINIMUM = 0.1f;
+ static constexpr float MAX_LOAD_FACTOR__MAXIMUM = 0.95f;
+
+ static const size_type REHASH_ON_HIGH_NB_PROBES__NPROBES = 128;
+ static constexpr float REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR = 0.15f;
+
+ /**
+ * Protocol version currenlty used for serialization.
+ */
+ static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1;
+
+ /**
+ * Return an always valid pointer to an static empty bucket_entry with
+ * last_bucket() == true.
+ */
+ bucket_entry* static_empty_bucket_ptr() {
+ static bucket_entry empty_bucket;
+ return &empty_bucket;
+ }
+
+ private:
+ buckets_container_type m_buckets_data;
+
+ /**
+ * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points
+ * to static_empty_bucket_ptr. This variable is useful to avoid the cost of
+ * checking if m_buckets_data is empty when trying to find an element.
+ *
+ * TODO Remove m_buckets_data and only use a pointer+size instead of a
+ * pointer+vector to save some space in the ordered_hash object.
+ */
+ bucket_entry* m_buckets;
+
+ size_type m_hash_mask;
+
+ values_container_type m_values;
+
+ size_type m_load_threshold;
+ float m_max_load_factor;
+
+ bool m_grow_on_next_insert;
+};
+
+} // end namespace detail_ordered_hash
+
+} // end namespace tsl
+
+#endif
diff --git a/rmw_zenoh_cpp/src/detail/ordered_map.hpp b/rmw_zenoh_cpp/src/detail/ordered_map.hpp
new file mode 100644
index 00000000..14303f94
--- /dev/null
+++ b/rmw_zenoh_cpp/src/detail/ordered_map.hpp
@@ -0,0 +1,999 @@
+/**
+ * MIT License
+ *
+ * Copyright (c) 2017 Thibaut Goetghebuer-Planchon
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// Copied from https://github.com/Tessil/ordered-map/blob/bd8d5ef4149cd40783a486011778a2e7eedde441/include/tsl/ordered_map.h
+
+#ifndef TSL_ORDERED_MAP_H
+#define TSL_ORDERED_MAP_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ordered_hash.hpp"
+
+namespace tsl {
+
+/**
+ * Implementation of an hash map using open addressing with robin hood with
+ * backshift delete to resolve collisions.
+ *
+ * The particularity of this hash map is that it remembers the order in which
+ * the elements were added and provide a way to access the structure which
+ * stores these values through the 'values_container()' method. The used
+ * container is defined by ValueTypeContainer, by default a std::deque is used
+ * (grows faster) but a std::vector may be used. In this case the map provides a
+ * 'data()' method which give a direct access to the memory used to store the
+ * values (which can be useful to communicate with C API's).
+ *
+ * The Key and T must be copy constructible and/or move constructible. To use
+ * `unordered_erase` they both must be swappable.
+ *
+ * The behaviour of the hash map is undefined if the destructor of Key or T
+ * throws an exception.
+ *
+ * By default the maximum size of a map is limited to 2^32 - 1 values, if needed
+ * this can be changed through the IndexType template parameter. Using an
+ * `uint64_t` will raise this limit to 2^64 - 1 values but each bucket will use
+ * 16 bytes instead of 8 bytes in addition to the space needed to store the
+ * values.
+ *
+ * Iterators invalidation:
+ * - clear, operator=, reserve, rehash: always invalidate the iterators (also
+ * invalidate end()).
+ * - insert, emplace, emplace_hint, operator[]: when a std::vector is used as
+ * ValueTypeContainer and if size() < capacity(), only end(). Otherwise all the
+ * iterators are invalidated if an insert occurs.
+ * - erase, unordered_erase: when a std::vector is used as ValueTypeContainer
+ * invalidate the iterator of the erased element and all the ones after the
+ * erased element (including end()). Otherwise all the iterators are invalidated
+ * if an erase occurs.
+ */
+template ,
+ class KeyEqual = std::equal_to,
+ class Allocator = std::allocator>,
+ class ValueTypeContainer = std::deque, Allocator>,
+ class IndexType = std::uint_least32_t>
+class ordered_map {
+ private:
+ template
+ using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent;
+
+ class KeySelect {
+ public:
+ using key_type = Key;
+
+ const key_type& operator()(
+ const std::pair& key_value) const noexcept {
+ return key_value.first;
+ }
+
+ key_type& operator()(std::pair& key_value) noexcept {
+ return key_value.first;
+ }
+ };
+
+ class ValueSelect {
+ public:
+ using value_type = T;
+
+ const value_type& operator()(
+ const std::pair& key_value) const noexcept {
+ return key_value.second;
+ }
+
+ value_type& operator()(std::pair& key_value) noexcept {
+ return key_value.second;
+ }
+ };
+
+ using ht =
+ detail_ordered_hash::ordered_hash, KeySelect,
+ ValueSelect, Hash, KeyEqual, Allocator,
+ ValueTypeContainer, IndexType>;
+
+ public:
+ using key_type = typename ht::key_type;
+ using mapped_type = T;
+ using value_type = typename ht::value_type;
+ using size_type = typename ht::size_type;
+ using difference_type = typename ht::difference_type;
+ using hasher = typename ht::hasher;
+ using key_equal = typename ht::key_equal;
+ using allocator_type = typename ht::allocator_type;
+ using reference = typename ht::reference;
+ using const_reference = typename ht::const_reference;
+ using pointer = typename ht::pointer;
+ using const_pointer = typename ht::const_pointer;
+ using iterator = typename ht::iterator;
+ using const_iterator = typename ht::const_iterator;
+ using reverse_iterator = typename ht::reverse_iterator;
+ using const_reverse_iterator = typename ht::const_reverse_iterator;
+
+ using values_container_type = typename ht::values_container_type;
+
+ /*
+ * Constructors
+ */
+ ordered_map() : ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {}
+
+ explicit ordered_map(size_type bucket_count, const Hash& hash = Hash(),
+ const KeyEqual& equal = KeyEqual(),
+ const Allocator& alloc = Allocator())
+ : m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) {}
+
+ ordered_map(size_type bucket_count, const Allocator& alloc)
+ : ordered_map(bucket_count, Hash(), KeyEqual(), alloc) {}
+
+ ordered_map(size_type bucket_count, const Hash& hash, const Allocator& alloc)
+ : ordered_map(bucket_count, hash, KeyEqual(), alloc) {}
+
+ explicit ordered_map(const Allocator& alloc)
+ : ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {}
+
+ template
+ ordered_map(InputIt first, InputIt last,
+ size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
+ const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
+ const Allocator& alloc = Allocator())
+ : ordered_map(bucket_count, hash, equal, alloc) {
+ insert(first, last);
+ }
+
+ template
+ ordered_map(InputIt first, InputIt last, size_type bucket_count,
+ const Allocator& alloc)
+ : ordered_map(first, last, bucket_count, Hash(), KeyEqual(), alloc) {}
+
+ template
+ ordered_map(InputIt first, InputIt last, size_type bucket_count,
+ const Hash& hash, const Allocator& alloc)
+ : ordered_map(first, last, bucket_count, hash, KeyEqual(), alloc) {}
+
+ ordered_map(std::initializer_list init,
+ size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
+ const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(),
+ const Allocator& alloc = Allocator())
+ : ordered_map(init.begin(), init.end(), bucket_count, hash, equal,
+ alloc) {}
+
+ ordered_map(std::initializer_list init, size_type bucket_count,
+ const Allocator& alloc)
+ : ordered_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(),
+ alloc) {}
+
+ ordered_map(std::initializer_list init, size_type bucket_count,
+ const Hash& hash, const Allocator& alloc)
+ : ordered_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(),
+ alloc) {}
+
+ ordered_map& operator=(std::initializer_list ilist) {
+ m_ht.clear();
+
+ m_ht.reserve(ilist.size());
+ m_ht.insert(ilist.begin(), ilist.end());
+
+ return *this;
+ }
+
+ allocator_type get_allocator() const { return m_ht.get_allocator(); }
+
+ /*
+ * Iterators
+ */
+ iterator begin() noexcept { return m_ht.begin(); }
+ const_iterator begin() const noexcept { return m_ht.begin(); }
+ const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
+
+ iterator end() noexcept { return m_ht.end(); }
+ const_iterator end() const noexcept { return m_ht.end(); }
+ const_iterator cend() const noexcept { return m_ht.cend(); }
+
+ reverse_iterator rbegin() noexcept { return m_ht.rbegin(); }
+ const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); }
+ const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); }
+
+ reverse_iterator rend() noexcept { return m_ht.rend(); }
+ const_reverse_iterator rend() const noexcept { return m_ht.rend(); }
+ const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); }
+
+ /*
+ * Capacity
+ */
+ bool empty() const noexcept { return m_ht.empty(); }
+ size_type size() const noexcept { return m_ht.size(); }
+ size_type max_size() const noexcept { return m_ht.max_size(); }
+
+ /*
+ * Modifiers
+ */
+ void clear() noexcept { m_ht.clear(); }
+
+ std::pair insert(const value_type& value) {
+ return m_ht.insert(value);
+ }
+
+ template ::value>::type* = nullptr>
+ std::pair insert(P&& value) {
+ return m_ht.emplace(std::forward(value));
+ }
+
+ std::pair insert(value_type&& value) {
+ return m_ht.insert(std::move(value));
+ }
+
+ iterator insert(const_iterator hint, const value_type& value) {
+ return m_ht.insert_hint(hint, value);
+ }
+
+ template ::value>::type* = nullptr>
+ iterator insert(const_iterator hint, P&& value) {
+ return m_ht.emplace_hint(hint, std::forward(value));
+ }
+
+ iterator insert(const_iterator hint, value_type&& value) {
+ return m_ht.insert_hint(hint, std::move(value));
+ }
+
+ template
+ void insert(InputIt first, InputIt last) {
+ m_ht.insert(first, last);
+ }
+ void insert(std::initializer_list ilist) {
+ m_ht.insert(ilist.begin(), ilist.end());
+ }
+
+ template
+ std::pair insert_or_assign(const key_type& k, M&& obj) {
+ return m_ht.insert_or_assign(k, std::forward(obj));
+ }
+
+ template
+ std::pair insert_or_assign(key_type&& k, M&& obj) {
+ return m_ht.insert_or_assign(std::move(k), std::forward(obj));
+ }
+
+ template
+ iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
+ return m_ht.insert_or_assign(hint, k, std::forward(obj));
+ }
+
+ template
+ iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
+ return m_ht.insert_or_assign(hint, std::move(k), std::forward(obj));
+ }
+
+ /**
+ * Due to the way elements are stored, emplace will need to move or copy the
+ * key-value once. The method is equivalent to
+ * insert(value_type(std::forward(args)...));
+ *
+ * Mainly here for compatibility with the std::unordered_map interface.
+ */
+ template
+ std::pair emplace(Args&&... args) {
+ return m_ht.emplace(std::forward(args)...);
+ }
+
+ /**
+ * Due to the way elements are stored, emplace_hint will need to move or copy
+ * the key-value once. The method is equivalent to insert(hint,
+ * value_type(std::forward