iunordered_set_index.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
  4. // Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // See http://www.boost.org/libs/interprocess for documentation.
  8. //
  9. //////////////////////////////////////////////////////////////////////////////
  10. #ifndef BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP
  11. #define BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP
  12. #ifndef BOOST_CONFIG_HPP
  13. # include <boost/config.hpp>
  14. #endif
  15. #
  16. #if defined(BOOST_HAS_PRAGMA_ONCE)
  17. # pragma once
  18. #endif
  19. #include <boost/interprocess/detail/config_begin.hpp>
  20. #include <boost/interprocess/detail/workaround.hpp>
  21. #include <boost/interprocess/detail/utilities.hpp>
  22. #include <boost/interprocess/allocators/allocator.hpp>
  23. #include <boost/container/vector.hpp>
  24. #include <boost/intrusive/unordered_set.hpp>
  25. #include <boost/intrusive/detail/minimal_pair_header.hpp>
  26. #include <boost/intrusive/detail/minimal_less_equal_header.hpp> //std::less
  27. #include <boost/container/detail/minimal_char_traits_header.hpp> //std::char_traits
  28. #include <boost/container/detail/placement_new.hpp>
  29. #include <boost/intrusive/detail/hash.hpp>
  30. //!\file
  31. //!Describes index adaptor of boost::intrusive::unordered_set container, to use it
  32. //!as name/shared memory index
  33. namespace boost { namespace interprocess {
  34. #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  35. //!Helper class to define typedefs
  36. //!from IndexTraits
  37. template <class MapConfig>
  38. struct iunordered_set_index_aux
  39. {
  40. typedef typename
  41. MapConfig::segment_manager_base segment_manager_base;
  42. typedef typename
  43. segment_manager_base::void_pointer void_pointer;
  44. typedef typename bi::make_unordered_set_base_hook
  45. < bi::void_pointer<void_pointer>
  46. >::type derivation_hook;
  47. typedef typename MapConfig::template
  48. intrusive_value_type<derivation_hook>::type value_type;
  49. typedef typename MapConfig::compare_key_type compare_key_type;
  50. typedef std::equal_to<value_type> value_equal;
  51. typedef typename MapConfig::char_type char_type;
  52. struct equal_function
  53. {
  54. bool operator()(const compare_key_type&i, const value_type &b) const
  55. {
  56. return (i.m_len == b.name_length()) &&
  57. (std::char_traits<char_type>::compare
  58. (i.mp_str, b.name(), i.m_len) == 0);
  59. }
  60. bool operator()(const value_type &b, const compare_key_type&i) const
  61. {
  62. return (i.m_len == b.name_length()) &&
  63. (std::char_traits<char_type>::compare
  64. (i.mp_str, b.name(), i.m_len) == 0);
  65. }
  66. bool operator()(const value_type &b1, const value_type &b2) const
  67. {
  68. return (b1.name_length() == b2.name_length()) &&
  69. (std::char_traits<char_type>::compare
  70. (b1.name(), b2.name(), b1.name_length()) == 0);
  71. }
  72. };
  73. struct hash_function
  74. {
  75. typedef value_type argument_type;
  76. typedef std::size_t result_type;
  77. std::size_t hash_char_range(const char_type* beg, const char_type* end) const
  78. {
  79. std::size_t seed = 0;
  80. while (beg != end) {
  81. boost::intrusive::detail::hash_combine_size_t(seed, boost::intrusive::detail::internal_hash_functor<char_type>()(*beg));
  82. ++beg;
  83. }
  84. return seed;
  85. }
  86. std::size_t operator()(const value_type &val) const
  87. {
  88. const char_type* beg = ipcdetail::to_raw_pointer(val.name()),
  89. * end = beg + val.name_length();
  90. return hash_char_range(beg, end);
  91. }
  92. std::size_t operator()(const compare_key_type&i) const
  93. {
  94. const char_type *beg = i.mp_str,
  95. *end = beg + i.m_len;
  96. return hash_char_range(beg, end);
  97. }
  98. };
  99. typedef typename bi::make_unordered_set
  100. < value_type
  101. , bi::hash<hash_function>
  102. , bi::equal<equal_function>
  103. , bi::size_type<typename segment_manager_base::size_type>
  104. >::type index_t;
  105. typedef typename index_t::bucket_type bucket_type;
  106. typedef allocator
  107. <bucket_type, segment_manager_base> allocator_type;
  108. struct allocator_holder
  109. {
  110. allocator_holder(segment_manager_base *mngr)
  111. : alloc(mngr)
  112. {}
  113. allocator_type alloc;
  114. bucket_type init_bucket;
  115. };
  116. };
  117. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  118. //!Index type based in boost::intrusive::set.
  119. //!Just derives from boost::intrusive::set
  120. //!and defines the interface needed by managed memory segments
  121. template <class MapConfig>
  122. class iunordered_set_index
  123. //Derive class from map specialization
  124. : private iunordered_set_index_aux<MapConfig>::allocator_holder
  125. , private iunordered_set_index_aux<MapConfig>::index_t
  126. {
  127. #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  128. typedef iunordered_set_index_aux<MapConfig> index_aux;
  129. typedef typename index_aux::index_t index_type;
  130. typedef typename index_aux::equal_function equal_function;
  131. typedef typename index_aux::hash_function hash_function;
  132. typedef typename MapConfig::char_type char_type;
  133. typedef typename
  134. iunordered_set_index_aux<MapConfig>::allocator_type allocator_type;
  135. typedef typename
  136. iunordered_set_index_aux<MapConfig>::allocator_holder allocator_holder;
  137. public:
  138. typedef typename MapConfig::compare_key_type compare_key_type;
  139. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  140. public:
  141. typedef typename index_type::iterator iterator;
  142. typedef typename index_type::const_iterator const_iterator;
  143. typedef typename index_type::insert_commit_data insert_commit_data;
  144. typedef typename index_type::value_type value_type;
  145. typedef typename index_type::bucket_ptr bucket_ptr;
  146. typedef typename index_type::bucket_type bucket_type;
  147. typedef typename index_type::bucket_traits bucket_traits;
  148. typedef typename index_type::size_type size_type;
  149. typedef typename index_type::difference_type difference_type;
  150. typedef value_type index_data_t;
  151. #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  152. private:
  153. typedef typename index_aux::
  154. segment_manager_base segment_manager_base;
  155. static const std::size_t InitBufferSize = 64;
  156. static bucket_ptr create_buckets(allocator_type &alloc, size_type num)
  157. {
  158. num = index_type::suggested_upper_bucket_count(num);
  159. bucket_ptr buckets = alloc.allocate(num);
  160. bucket_ptr buckets_init = buckets;
  161. for(size_type i = 0; i < num; ++i){
  162. ::new(to_raw_pointer(buckets_init++), boost_container_new_t())bucket_type();
  163. }
  164. return buckets;
  165. }
  166. static size_type shrink_buckets
  167. ( bucket_ptr buckets, size_type old_size
  168. , allocator_type &alloc, size_type new_size)
  169. {
  170. if(old_size <= new_size )
  171. return old_size;
  172. size_type received_size = new_size;
  173. if(!alloc.allocation_command
  174. (boost::interprocess::try_shrink_in_place | boost::interprocess::nothrow_allocation, old_size, received_size, buckets)){
  175. return old_size;
  176. }
  177. for( bucket_type *p = ipcdetail::to_raw_pointer(buckets) + received_size
  178. , *pend = ipcdetail::to_raw_pointer(buckets) + old_size
  179. ; p != pend
  180. ; ++p){
  181. p->~bucket_type();
  182. }
  183. bucket_ptr shunk_p = alloc.allocation_command
  184. (boost::interprocess::shrink_in_place | boost::interprocess::nothrow_allocation, received_size, received_size, buckets);
  185. BOOST_ASSERT(buckets == shunk_p); (void)shunk_p;
  186. bucket_ptr buckets_init = buckets + difference_type(received_size);
  187. for(size_type i = 0; i < (old_size - received_size); ++i){
  188. to_raw_pointer(buckets_init++)->~bucket_type();
  189. }
  190. return received_size;
  191. }
  192. static bucket_ptr expand_or_create_buckets
  193. ( bucket_ptr old_buckets, const size_type old_num
  194. , allocator_type &alloc, const size_type new_num)
  195. {
  196. size_type received_size = new_num;
  197. bucket_ptr reuse(old_buckets);
  198. bucket_ptr ret = alloc.allocation_command
  199. (boost::interprocess::expand_fwd | boost::interprocess::allocate_new, new_num, received_size, reuse);
  200. if(ret == old_buckets){
  201. bucket_ptr buckets_init = old_buckets + difference_type(old_num);
  202. for(size_type i = 0; i < (new_num - old_num); ++i){
  203. ::new(to_raw_pointer(buckets_init++), boost_container_new_t())bucket_type();
  204. }
  205. }
  206. else{
  207. bucket_ptr buckets_init = ret;
  208. for(size_type i = 0; i < new_num; ++i){
  209. ::new(to_raw_pointer(buckets_init++), boost_container_new_t())bucket_type();
  210. }
  211. }
  212. return ret;
  213. }
  214. static void destroy_buckets
  215. (allocator_type &alloc, bucket_ptr buckets, size_type num)
  216. {
  217. bucket_ptr buckets_destroy = buckets;
  218. for(size_type i = 0; i < num; ++i){
  219. to_raw_pointer(buckets_destroy++)->~bucket_type();
  220. }
  221. alloc.deallocate(buckets, num);
  222. }
  223. iunordered_set_index<MapConfig>* get_this_pointer()
  224. { return this; }
  225. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  226. public:
  227. using index_type::begin;
  228. using index_type::end;
  229. using index_type::size;
  230. using index_type::erase;
  231. //!Constructor. Takes a pointer to the
  232. //!segment manager. Can throw
  233. iunordered_set_index(segment_manager_base *mngr)
  234. : allocator_holder(mngr)
  235. , index_type(bucket_traits(&get_this_pointer()->init_bucket, 1))
  236. {}
  237. ~iunordered_set_index()
  238. {
  239. index_type::clear();
  240. if(index_type::bucket_pointer() != bucket_ptr(&this->init_bucket)){
  241. destroy_buckets(this->alloc, index_type::bucket_pointer(), index_type::bucket_count());
  242. }
  243. }
  244. //!This reserves memory to optimize the insertion of n
  245. //!elements in the index
  246. void reserve(size_type new_n)
  247. {
  248. //Let's maintain a 1.0f load factor
  249. size_type old_n = this->bucket_count();
  250. if(new_n <= old_n)
  251. return;
  252. bucket_ptr old_p = this->bucket_pointer();
  253. new_n = index_type::suggested_upper_bucket_count(new_n);
  254. bucket_ptr new_p;
  255. //This can throw
  256. BOOST_INTERPROCESS_TRY{
  257. if(old_p != bucket_ptr(&this->init_bucket))
  258. new_p = expand_or_create_buckets(old_p, old_n, this->alloc, new_n);
  259. else
  260. new_p = create_buckets(this->alloc, new_n);
  261. }
  262. BOOST_INTERPROCESS_CATCH(...){
  263. return;
  264. } BOOST_INTERPROCESS_CATCH_END
  265. //Rehashing does not throw, since neither the hash nor the
  266. //comparison function can throw
  267. this->rehash(bucket_traits(new_p, new_n));
  268. if(new_p != old_p && old_p != bucket_ptr(&this->init_bucket)){
  269. destroy_buckets(this->alloc, old_p, old_n);
  270. }
  271. }
  272. //!This tries to free unused memory
  273. //!previously allocated.
  274. void shrink_to_fit()
  275. {
  276. size_type cur_size = this->size();
  277. size_type cur_count = this->bucket_count();
  278. bucket_ptr old_p = this->bucket_pointer();
  279. if(!this->size() && old_p != bucket_ptr(&this->init_bucket)){
  280. this->rehash(bucket_traits(bucket_ptr(&this->init_bucket), 1));
  281. destroy_buckets(this->alloc, old_p, cur_count);
  282. }
  283. else{
  284. size_type sug_count = 0; //gcc warning
  285. sug_count = index_type::suggested_upper_bucket_count(cur_size);
  286. if(sug_count >= cur_count)
  287. return;
  288. BOOST_INTERPROCESS_TRY{
  289. shrink_buckets(old_p, cur_count, this->alloc, sug_count);
  290. }
  291. BOOST_INTERPROCESS_CATCH(...){
  292. return;
  293. } BOOST_INTERPROCESS_CATCH_END
  294. //Rehashing does not throw, since neither the hash nor the
  295. //comparison function can throw
  296. this->rehash(bucket_traits(old_p, sug_count));
  297. }
  298. }
  299. iterator find(const compare_key_type&key)
  300. { return index_type::find(key, hash_function(), equal_function()); }
  301. const_iterator find(const compare_key_type&key) const
  302. { return index_type::find(key, hash_function(), equal_function()); }
  303. std::pair<iterator, bool>insert_check
  304. (const compare_key_type&key, insert_commit_data &commit_data)
  305. { return index_type::insert_check(key, hash_function(), equal_function(), commit_data); }
  306. iterator insert_commit
  307. (const compare_key_type &, void *, index_data_t &val, insert_commit_data& commit_data)
  308. {
  309. iterator it = index_type::insert_commit(val, commit_data);
  310. size_type cur_size = this->size();
  311. if(cur_size > this->bucket_count()){
  312. BOOST_INTERPROCESS_TRY{
  313. this->reserve(cur_size);
  314. }
  315. BOOST_INTERPROCESS_CATCH(...){
  316. //Strong guarantee: if something goes wrong
  317. //we should remove the insertion.
  318. //
  319. //We can use the iterator because the hash function
  320. //can't throw and this means that "reserve" will
  321. //throw only because of the memory allocation:
  322. //the iterator has not been invalidated.
  323. index_type::erase(it);
  324. BOOST_INTERPROCESS_RETHROW
  325. } BOOST_INTERPROCESS_CATCH_END
  326. }
  327. return it;
  328. }
  329. };
  330. #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  331. //!Trait class to detect if an index is an intrusive
  332. //!index
  333. template<class MapConfig>
  334. struct is_intrusive_index
  335. <boost::interprocess::iunordered_set_index<MapConfig> >
  336. {
  337. static const bool value = true;
  338. };
  339. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  340. }} //namespace boost { namespace interprocess {
  341. #include <boost/interprocess/detail/config_end.hpp>
  342. #endif //#ifndef BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP