29 #ifndef PIRANHA_HASH_SET_HPP 30 #define PIRANHA_HASH_SET_HPP 32 #include <boost/iterator/iterator_facade.hpp> 33 #include <boost/numeric/conversion/cast.hpp> 38 #include <initializer_list> 46 #include <type_traits> 50 #include <piranha/config.hpp> 51 #include <piranha/debug_access.hpp> 52 #include <piranha/detail/init_data.hpp> 54 #include <piranha/s11n.hpp> 55 #include <piranha/safe_cast.hpp> 56 #include <piranha/thread_pool.hpp> 121 template <
typename T,
typename Hash = std::hash<T>,
typename Pred = std::equal_to<T>>
128 template <
typename U>
132 typedef typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_type;
133 node() : m_next(
nullptr)
139 node(
const node &) =
delete;
140 node(node &&) =
delete;
164 piranha_assert(m_next);
165 return static_cast<const T *
>(
static_cast<const void *
>(&m_storage));
169 piranha_assert(m_next);
170 return static_cast<T *
>(
static_cast<void *
>(&m_storage));
172 storage_type m_storage;
185 template <
typename U>
186 class iterator_impl :
public boost::iterator_facade<iterator_impl<U>, U, boost::forward_traversal_tag>
188 typedef typename std::conditional<std::is_const<U>::value, node
const *, node *>::type ptr_type;
189 template <
typename V>
190 friend class iterator_impl;
193 iterator_impl() : m_ptr(
nullptr)
196 explicit iterator_impl(ptr_type ptr) : m_ptr(ptr)
200 template <
typename V,
201 enable_if_t<std::is_convertible<typename iterator_impl<V>::ptr_type, ptr_type>::value,
int> = 0>
202 iterator_impl(
const iterator_impl<V> &other) : m_ptr(other.m_ptr)
207 friend class boost::iterator_core_access;
210 piranha_assert(m_ptr && m_ptr->m_next);
211 m_ptr = m_ptr->m_next;
213 template <
typename V>
214 bool equal(
const iterator_impl<V> &other)
const 216 return m_ptr == other.m_ptr;
218 U &dereference()
const 220 piranha_assert(m_ptr && m_ptr->m_next);
221 return *m_ptr->ptr();
235 list(list &&other) noexcept : m_node()
237 steal_from_rvalue(std::move(other));
239 list(
const list &other) : m_node()
243 auto other_cur = &other.m_node;
244 while (other_cur->m_next) {
248 piranha_assert(cur->m_next == &terminator);
251 std::unique_ptr<node> new_node(::
new node());
252 ::new (static_cast<void *>(&new_node->m_storage)) T(*other_cur->ptr());
253 new_node->m_next = &terminator;
255 cur->m_next = new_node.release();
259 ::new (static_cast<void *>(&cur->m_storage)) T(*other_cur->ptr());
260 cur->m_next = &terminator;
262 other_cur = other_cur->m_next;
271 if (likely(
this != &other)) {
274 steal_from_rvalue(std::move(other));
280 if (likely(
this != &other)) {
282 *
this = std::move(tmp);
290 void steal_from_rvalue(list &&other)
292 piranha_assert(
empty());
294 if (other.m_node.m_next) {
296 ::new (static_cast<void *>(&m_node.m_storage)) T(std::move(*other.m_node.ptr()));
298 m_node.m_next = other.m_node.m_next;
300 other.m_node.ptr()->~T();
301 other.m_node.m_next =
nullptr;
303 piranha_assert(other.empty());
305 template <
typename U, enable_if_t<std::is_same<T, uncvref_t<U>>::value,
int> = 0>
311 std::unique_ptr<node> new_node(::
new node());
312 ::new (static_cast<void *>(&new_node->m_storage)) T(std::forward<U>(item));
313 new_node->m_next = m_node.m_next;
315 m_node.m_next = new_node.release();
316 return m_node.m_next;
318 ::new (static_cast<void *>(&m_node.m_storage)) T(std::forward<U>(item));
319 m_node.m_next = &terminator;
329 return iterator((m_node.m_next) ? &terminator : &m_node);
341 return !m_node.m_next;
346 while (cur->m_next) {
353 old->m_next =
nullptr;
355 if (old != &m_node) {
360 piranha_assert(
empty());
362 static node terminator;
371 typedef std::allocator<list> allocator_type;
388 using ptr_type = list *;
392 using pack_type = std::tuple<ptr_type, hasher, key_equal, allocator_type>;
396 return std::get<0u>(m_pack);
398 const ptr_type &ptr()
const 400 return std::get<0u>(m_pack);
402 const hasher &hash()
const 404 return std::get<1u>(m_pack);
408 return std::get<2u>(m_pack);
410 allocator_type &allocator()
412 return std::get<3u>(m_pack);
414 const allocator_type &allocator()
const 416 return std::get<3u>(m_pack);
419 template <
typename Key>
420 class iterator_impl :
public boost::iterator_facade<iterator_impl<Key>, Key, boost::forward_traversal_tag>
423 typedef typename std::conditional<std::is_const<Key>::value,
hash_set const,
hash_set>::type set_type;
424 typedef typename std::conditional<std::is_const<Key>::value,
typename list::const_iterator,
425 typename list::iterator>::type it_type;
428 iterator_impl() : m_set(nullptr), m_idx(0u), m_it()
431 explicit iterator_impl(set_type *
set,
const size_type &idx, it_type it) : m_set(set), m_idx(idx), m_it(it)
436 friend class boost::iterator_core_access;
439 piranha_assert(m_set);
440 auto &container = m_set->ptr();
443 piranha_assert(!container[m_idx].
empty());
444 piranha_assert(m_it != container[m_idx].
end());
446 if (m_it == container[m_idx].
end()) {
447 const size_type container_size = m_set->bucket_count();
450 if (m_idx == container_size) {
453 }
else if (!container[m_idx].
empty()) {
454 m_it = container[m_idx].begin();
460 bool equal(
const iterator_impl &other)
const 464 piranha_assert(m_set && other.m_set);
465 return (m_idx == other.m_idx && m_it == other.m_it);
467 Key &dereference()
const 469 piranha_assert(m_set && m_idx < m_set->
bucket_count() && m_it != m_set->ptr()[m_idx].end());
478 void init_from_n_buckets(
const size_type &n_buckets,
unsigned n_threads)
480 piranha_assert(!ptr() && !m_log2_size && !m_n_elements);
481 if (unlikely(!n_threads)) {
482 piranha_throw(std::invalid_argument,
"the number of threads must be strictly positive");
488 const size_type log2_size = get_log2_from_hint(n_buckets);
490 auto new_ptr = allocator().allocate(
size);
491 if (unlikely(!new_ptr)) {
494 if (n_threads == 1u) {
498 allocator().construct(&new_ptr[i]);
502 using crs_type = std::vector<std::pair<size_type, size_type>>;
503 crs_type constructed_ranges(static_cast<typename crs_type::size_type>(n_threads),
505 if (unlikely(constructed_ranges.size() != n_threads)) {
509 auto thread_function = [
this, new_ptr, &constructed_ranges](
const size_type &start,
const size_type &
end,
510 const unsigned &thread_idx) {
512 this->allocator().construct(&new_ptr[i]);
514 constructed_ranges[thread_idx] = std::make_pair(start,
end);
517 const auto wpt =
size / n_threads;
518 future_list<decltype(thread_function(0u, 0u, 0u))> f_list;
520 for (
unsigned i = 0u; i < n_threads; ++i) {
521 const auto start =
static_cast<size_type>(wpt * i),
522 end = static_cast<size_type>((i == n_threads - 1u) ?
size : wpt * (i + 1u));
533 for (
const auto &r : constructed_ranges) {
534 for (
size_type i = r.first; i != r.second; ++i) {
535 allocator().destroy(&new_ptr[i]);
539 allocator().deallocate(new_ptr,
size);
545 m_log2_size = log2_size;
548 void destroy_and_deallocate()
554 allocator().destroy(&ptr()[i]);
556 allocator().deallocate(ptr(),
size);
558 piranha_assert(!m_log2_size && !m_n_elements);
562 friend class boost::serialization::access;
563 template <
class Archive>
564 void save(Archive &ar,
unsigned)
const 569 boost_save_range(ar,
begin(),
end());
571 template <
class Archive>
572 void load(Archive &ar,
unsigned)
584 const auto p =
insert(std::move(tmp));
585 if (unlikely(!p.second)) {
586 piranha_throw(std::invalid_argument,
"while deserializing a hash_set from a Boost archive " 587 "a duplicate value was encountered");
591 BOOST_SERIALIZATION_SPLIT_MEMBER()
593 template <typename U>
594 using insert_enabler = enable_if_t<
std::is_same<
key_type, uncvref_t<U>>::value,
int>;
596 bool sanity_check()
const 604 for (
auto it = ptr()[i].begin(); it != ptr()[i].end(); ++it) {
611 if (count != m_n_elements) {
615 if (m_log2_size >=
unsigned(std::numeric_limits<size_type>::digits)) {
619 if (!ptr() && (m_log2_size || m_n_elements)) {
624 for (
auto it =
begin(); it !=
end(); ++it, ++count) {
626 if (count != m_n_elements) {
634 static const size_type m_n_nonzero_sizes =
static_cast<size_type>(std::numeric_limits<size_type>::digits);
638 piranha_assert(hint);
639 for (
size_type i = 0u; i < m_n_nonzero_sizes; ++i) {
680 : m_pack(
nullptr, h, k, allocator_type{}), m_log2_size(0u), m_n_elements(0u)
703 unsigned n_threads = 1u)
704 : m_pack(
nullptr, h, k, allocator_type{}), m_log2_size(0u), m_n_elements(0u)
706 init_from_n_buckets(n_buckets, n_threads);
718 : m_pack(nullptr, other.hash(), other.k_equal(), other.allocator()), m_log2_size(0u), m_n_elements(0u)
723 auto new_ptr = allocator().allocate(
size);
724 if (unlikely(!new_ptr)) {
730 for (; i <
size; ++i) {
731 allocator().construct(&new_ptr[i], other.ptr()[i]);
736 allocator().destroy(&new_ptr[j]);
738 allocator().deallocate(new_ptr,
size);
743 m_log2_size = other.m_log2_size;
744 m_n_elements = other.m_n_elements;
746 piranha_assert(!other.m_log2_size && !other.m_n_elements);
757 : m_pack(std::move(other.m_pack)), m_log2_size(other.m_log2_size), m_n_elements(other.m_n_elements)
760 other.ptr() =
nullptr;
761 other.m_log2_size = 0u;
762 other.m_n_elements = 0u;
779 template <
typename InputIterator>
782 : m_pack(
nullptr, h, k, allocator_type{}), m_log2_size(0u), m_n_elements(0u)
784 init_from_n_buckets(n_buckets, 1u);
785 for (
auto it =
begin; it !=
end; ++it) {
800 template <
typename U>
802 : m_pack(nullptr,
hasher{},
key_equal{}, allocator_type{}), m_log2_size(0u), m_n_elements(0u)
805 init_from_n_buckets(static_cast<size_type>(list.size()), 1u);
806 for (
const auto &x : list) {
816 piranha_assert(sanity_check());
817 destroy_and_deallocate();
829 if (likely(
this != &other)) {
831 *
this = std::move(tmp);
843 if (likely(
this != &other)) {
844 destroy_and_deallocate();
845 m_pack = std::move(other.m_pack);
846 m_log2_size = other.m_log2_size;
847 m_n_elements = other.m_n_elements;
849 other.ptr() =
nullptr;
850 other.m_log2_size = 0u;
851 other.m_n_elements = 0u;
867 for (; idx < b_count; ++idx) {
868 if (!ptr()[idx].
empty()) {
874 if (idx != b_count) {
875 retval.m_it = ptr()[idx].begin();
934 return (b_count) ?
static_cast<double>(
size()) / static_cast<double>(b_count) : 0.;
1019 template <
typename U, insert_enabler<U> = 0>
1024 if (unlikely(!b_count)) {
1031 const auto it =
_find(k, bucket_idx);
1034 return std::make_pair(it,
false);
1036 if (unlikely(m_n_elements == std::numeric_limits<size_type>::max())) {
1037 piranha_throw(std::overflow_error,
"maximum number of elements reached");
1040 if (unlikely(static_cast<double>(m_n_elements +
size_type(1u)) / static_cast<double>(b_count)
1046 const auto it_retval =
_unique_insert(std::forward<U>(k), bucket_idx);
1048 return std::make_pair(it_retval,
true);
1067 piranha_assert(!
empty());
1068 const auto b_it =
_erase(it);
1070 retval.m_set =
this;
1072 if (b_it == ptr()[it.m_idx].end()) {
1075 auto idx =
static_cast<size_type>(it.m_idx + 1u);
1078 for (; idx < b_count; ++idx) {
1079 if (!ptr()[idx].empty()) {
1085 if (idx != b_count) {
1086 retval.m_it = ptr()[idx].begin();
1094 retval.m_idx = it.m_idx;
1097 piranha_assert(m_n_elements);
1099 m_n_elements =
static_cast<size_type>(m_n_elements - 1u);
1108 destroy_and_deallocate();
1124 std::swap(m_pack, other.m_pack);
1125 std::swap(m_log2_size, other.m_log2_size);
1126 std::swap(m_n_elements, other.m_n_elements);
1144 if (unlikely(!n_threads)) {
1145 piranha_throw(std::invalid_argument,
"the number of threads must be strictly positive");
1159 hash_set new_set(new_size, hash(), k_equal(), n_threads);
1161 const auto it_f =
_m_end();
1162 for (
auto it =
_m_begin(); it != it_f; ++it) {
1163 const auto new_idx = new_set.
_bucket(*it);
1173 new_set.m_n_elements = m_n_elements;
1177 *
this = std::move(new_set);
1189 std::map<size_type, size_type> retval;
1191 for (
auto it = ptr(); it != it_f; ++it) {
1193 for (
auto l_it = it->begin(); l_it != it->end(); ++l_it) {
1223 retval.m_set =
this;
1225 for (; idx < b_count; ++idx) {
1226 if (!ptr()[idx].
empty()) {
1232 if (idx != b_count) {
1233 retval.m_it = ptr()[idx].begin();
1268 template <
typename U, insert_enabler<U> = 0>
1272 piranha_assert(
find(std::forward<U>(k)) ==
end());
1274 piranha_assert(bucket_idx ==
_bucket(k));
1275 auto p = ptr()[bucket_idx].insert(std::forward<U>(k));
1295 const auto &b = ptr()[bucket_idx];
1296 const auto it_f = b.end();
1298 for (
auto it = b.begin(); it != it_f; ++it) {
1299 if (k_equal()(*it, k)) {
1300 retval.m_idx = bucket_idx;
1318 return hash % (
size_type(1u) << m_log2_size);
1343 m_n_elements = new_size;
1355 if (unlikely(m_log2_size >= m_n_nonzero_sizes - 1u)) {
1360 piranha_assert(ptr() || (!ptr() && !m_log2_size));
1361 const auto new_log2_size = (ptr()) ? (m_log2_size + 1u) : 0u;
1397 piranha_assert(it.m_set ==
this);
1399 piranha_assert(!ptr()[it.m_idx].empty());
1400 piranha_assert(it.m_it != ptr()[it.m_idx].end());
1401 auto &
bucket = ptr()[it.m_idx];
1403 if (&*it == &*
bucket.m_node.ptr()) {
1405 bucket.m_node.ptr()->~T();
1408 bucket.m_node.m_next =
nullptr;
1412 auto tmp =
bucket.m_node.m_next->m_next;
1414 ::new (static_cast<void *>(&
bucket.m_node.m_storage)) T(std::move(*
bucket.m_node.m_next->ptr()));
1415 bucket.m_node.m_next->ptr()->~T();
1416 ::delete
bucket.m_node.m_next;
1418 bucket.m_node.m_next = tmp;
1422 auto b_it =
bucket.begin();
1423 auto prev_b_it = b_it;
1424 const auto b_it_f =
bucket.end();
1426 for (; b_it != b_it_f; ++b_it, ++prev_b_it) {
1427 if (&*b_it == &*it) {
1429 prev_b_it.m_ptr->m_next = b_it.m_ptr->m_next;
1431 b_it.m_ptr->ptr()->~T();
1432 ::delete b_it.m_ptr;
1439 piranha_assert(b_it.m_ptr);
1452 template <
typename T,
typename Hash,
typename Pred>
1453 typename hash_set<T, Hash, Pred>::node hash_set<T, Hash, Pred>::list::terminator;
1455 template <
typename T,
typename Hash,
typename Pred>
1458 inline namespace impl
1462 template <
typename Archive,
typename T,
typename Hash,
typename Pred>
1463 using hash_set_boost_save_enabler
1464 = enable_if_t<conjunction<has_boost_save<Archive, T>,
1465 has_boost_save<Archive, typename hash_set<T, Hash, Pred>::size_type>>::value>;
1467 template <
typename Archive,
typename T,
typename Hash,
typename Pred>
1468 using hash_set_boost_load_enabler
1469 = enable_if_t<conjunction<has_boost_load<Archive, T>,
1470 has_boost_load<Archive, typename hash_set<T, Hash, Pred>::size_type>>::value>;
1483 template <
typename Archive,
typename T,
typename Hash,
typename Pred>
1505 template <
typename Archive,
typename T,
typename Hash,
typename Pred>
1510 #if defined(PIRANHA_WITH_MSGPACK) 1512 inline namespace impl
1516 template <
typename Stream,
typename T,
typename Hash,
typename Pred>
1517 using hash_set_msgpack_pack_enabler
1518 = enable_if_t<conjunction<is_msgpack_stream<Stream>,
1522 template <
typename T>
1523 using hash_set_msgpack_convert_enabler = enable_if_t<has_msgpack_convert<T>::value>;
1534 template <
typename Stream,
typename T,
typename Hash,
typename Pred>
1553 msgpack_pack_range(p, h.
begin(), h.
end(), h.
size(), f);
1562 template <
typename T,
typename Hash,
typename Pred>
1586 std::vector<msgpack::object> items;
1592 for (
const auto &obj : items) {
1595 const auto p = h.
insert(std::move(tmp));
1596 if (unlikely(!p.second)) {
1597 piranha_throw(std::invalid_argument,
"while deserializing a hash_set from a msgpack object " 1598 "a duplicate value was encountered");
hash_set & operator=(const hash_set &other)
Copy assignment operator.
void boost_load(Archive &ar, T &x)
Load from Boost archive.
local_iterator _erase(const_iterator it)
Erase element.
Default implementation of piranha::boost_load().
iterator end()
End iterator.
void msgpack_convert(T &x, const msgpack::object &o, msgpack_format f)
Convert msgpack object.
void _increase_size()
Increase bucket count.
const_iterator find(const key_type &k) const
Find element.
std::map< size_type, size_type > evaluate_sparsity() const
Get information on the sparsity of the set.
Implementation of piranha::boost_load() via the Boost API.
void rehash(const size_type &new_size, unsigned n_threads=1u)
Rehash set.
const_iterator end() const
Const end iterator.
Default implementation of piranha::boost_save().
iterator_impl< key_type > _m_iterator
Mutable iterator.
iterator begin()
Begin iterator.
void _update_size(const size_type &new_size)
Force update of the number of elements.
static enqueue_t< F &&, Args &&... > enqueue(unsigned n, F &&f, Args &&... args)
Enqueue task.
iterator find(const key_type &k)
Find element.
std::equal_to< term_type > key_equal
Functor type for comparing the items in the set.
iterator erase(const_iterator it)
Erase element.
Forward iterator type trait.
size_type bucket_count() const
Number of buckets.
size_type size() const
Number of elements contained in the set.
Default functor for the implementation of piranha::msgpack_convert().
#define piranha_throw(exception_type,...)
Exception-throwing macro.
void operator()(hash_set< T, Hash, Pred > &h, const msgpack::object &o, msgpack_format f) const
Call operator.
void clear()
Remove all elements.
Default functor for the implementation of piranha::msgpack_pack().
Exception for signalling division by zero.
msgpack_format
Serialization format for msgpack.
size_type _bucket(const key_type &k) const
Index of destination bucket (low-level).
size_type _bucket_from_hash(const std::size_t &hash) const
Index of destination bucket from hash value.
const_iterator begin() const
Const begin iterator.
hash_set(const InputIterator &begin, const InputIterator &end, const size_type &n_buckets=0u, const hasher &h=hasher{}, const key_equal &k=key_equal{})
Constructor from range.
std::pair< iterator, bool > insert(U &&k)
Insert element.
size_type bucket(const key_type &k) const
Index of destination bucket.
_m_iterator _m_begin()
Mutable begin iterator.
_m_iterator _m_end()
Mutable end iterator.
const_iterator _find(const key_type &k, const size_type &bucket_idx) const
Find element (low-level).
Implementation of piranha::boost_save() via the Boost API.
const list & _get_bucket_list(const size_type &idx) const
Const reference to list in bucket.
double load_factor() const
Load factor.
std::hash< term_type > hasher
Functor type for the calculation of hash values.
Detect the presence of piranha::msgpack_pack().
void operator()(msgpack::packer< Stream > &p, const hash_set< T, Hash, Pred > &h, msgpack_format f) const
Call operator.
iterator _unique_insert(U &&k, const size_type &bucket_idx)
Insert unique element (low-level).
std::size_t size_type
Size type.
bool empty() const
Test for empty set.
Type trait to detect piranha::safe_cast().
void boost_save(Archive &ar, const T &x)
Save to Boost archive.
term_type key_type
Key type.
Type trait to detect equality function objects.
iterator_impl< key_type const > iterator
Iterator type.
Type trait to detect hash function objects.
Type trait for well-behaved container elements.
hash_set(const hasher &h=hasher{}, const key_equal &k=key_equal{})
Default constructor.
hash_set(const size_type &n_buckets, const hasher &h=hasher{}, const key_equal &k=key_equal{}, unsigned n_threads=1u)
Constructor from number of buckets.
typename list::const_iterator local_iterator
Local iterator.
#define PIRANHA_TT_CHECK(tt,...)
Macro for static type trait checks.
hash_set & operator=(hash_set &&other) noexcept
Move assignment operator.
hash_set(hash_set &&other) noexcept
Move constructor.
hash_set(const hash_set &other)
Copy constructor.
double max_load_factor() const
Maximum load factor.
hash_set(std::initializer_list< U > list)
Constructor from initializer list.
void swap(hash_set &other)
Swap content.
iterator const_iterator
Const iterator type.