1 //////////////////////////////////////////////////////////////////////////////
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)
7 // See http://www.boost.org/libs/interprocess for documentation.
9 //////////////////////////////////////////////////////////////////////////////
11 #ifndef BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP
12 #define BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP
18 #include <boost/interprocess/detail/config_begin.hpp>
19 #include <boost/interprocess/detail/workaround.hpp>
24 #include <boost/interprocess/detail/utilities.hpp>
25 #include <boost/interprocess/containers/vector.hpp>
26 #include <boost/intrusive/unordered_set.hpp>
27 #include <boost/interprocess/allocators/allocator.hpp>
30 //!Describes index adaptor of boost::intrusive::unordered_set container, to use it
31 //!as name/shared memory index
33 namespace boost { namespace interprocess {
35 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
37 //!Helper class to define typedefs
39 template <class MapConfig>
40 struct iunordered_set_index_aux
43 MapConfig::segment_manager_base segment_manager_base;
46 segment_manager_base::void_pointer void_pointer;
48 typedef typename bi::make_unordered_set_base_hook
49 < bi::void_pointer<void_pointer>
50 >::type derivation_hook;
52 typedef typename MapConfig::template
53 intrusive_value_type<derivation_hook>::type value_type;
55 typedef typename MapConfig::
56 intrusive_compare_key_type intrusive_compare_key_type;
58 typedef std::equal_to<value_type> value_equal;
60 typedef typename MapConfig::char_type char_type;
64 bool operator()(const intrusive_compare_key_type &i, const value_type &b) const
66 return (i.m_len == b.name_length()) &&
67 (std::char_traits<char_type>::compare
68 (i.mp_str, b.name(), i.m_len) == 0);
71 bool operator()(const value_type &b, const intrusive_compare_key_type &i) const
73 return (i.m_len == b.name_length()) &&
74 (std::char_traits<char_type>::compare
75 (i.mp_str, b.name(), i.m_len) == 0);
78 bool operator()(const value_type &b1, const value_type &b2) const
80 return (b1.name_length() == b2.name_length()) &&
81 (std::char_traits<char_type>::compare
82 (b1.name(), b2.name(), b1.name_length()) == 0);
87 : std::unary_function<value_type, std::size_t>
89 std::size_t operator()(const value_type &val) const
91 const char_type *beg = ipcdetail::to_raw_pointer(val.name()),
92 *end = beg + val.name_length();
93 return boost::hash_range(beg, end);
96 std::size_t operator()(const intrusive_compare_key_type &i) const
98 const char_type *beg = i.mp_str,
100 return boost::hash_range(beg, end);
104 typedef typename bi::make_unordered_set
106 , bi::hash<hash_function>
107 , bi::equal<equal_function>
108 , bi::size_type<typename segment_manager_base::size_type>
110 typedef typename index_t::bucket_type bucket_type;
112 <bucket_type, segment_manager_base> allocator_type;
114 struct allocator_holder
116 allocator_holder(segment_manager_base *mngr)
119 allocator_type alloc;
120 bucket_type init_bucket;
123 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
125 //!Index type based in boost::intrusive::set.
126 //!Just derives from boost::intrusive::set
127 //!and defines the interface needed by managed memory segments
128 template <class MapConfig>
129 class iunordered_set_index
130 //Derive class from map specialization
131 : private iunordered_set_index_aux<MapConfig>::allocator_holder
132 , public iunordered_set_index_aux<MapConfig>::index_t
134 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
135 typedef iunordered_set_index_aux<MapConfig> index_aux;
136 typedef typename index_aux::index_t index_type;
137 typedef typename MapConfig::
138 intrusive_compare_key_type intrusive_compare_key_type;
139 typedef typename index_aux::equal_function equal_function;
140 typedef typename index_aux::hash_function hash_function;
141 typedef typename MapConfig::char_type char_type;
143 iunordered_set_index_aux<MapConfig>::allocator_type allocator_type;
145 iunordered_set_index_aux<MapConfig>::allocator_holder allocator_holder;
146 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
149 typedef typename index_type::iterator iterator;
150 typedef typename index_type::const_iterator const_iterator;
151 typedef typename index_type::insert_commit_data insert_commit_data;
152 typedef typename index_type::value_type value_type;
153 typedef typename index_type::bucket_ptr bucket_ptr;
154 typedef typename index_type::bucket_type bucket_type;
155 typedef typename index_type::bucket_traits bucket_traits;
156 typedef typename index_type::size_type size_type;
158 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
160 typedef typename index_aux::
161 segment_manager_base segment_manager_base;
163 static const std::size_t InitBufferSize = 64;
165 static bucket_ptr create_buckets(allocator_type &alloc, size_type num)
167 num = index_type::suggested_upper_bucket_count(num);
168 bucket_ptr buckets = alloc.allocate(num);
169 bucket_ptr buckets_init = buckets;
170 for(size_type i = 0; i < num; ++i){
171 new(to_raw_pointer(buckets_init++))bucket_type();
176 static size_type shrink_buckets
177 ( bucket_ptr buckets, size_type old_size
178 , allocator_type &alloc, size_type new_size)
180 if(old_size <= new_size )
182 size_type received_size;
183 if(!alloc.allocation_command
184 (boost::interprocess::try_shrink_in_place | boost::interprocess::nothrow_allocation, old_size, new_size, received_size, buckets).first){
188 for( bucket_type *p = ipcdetail::to_raw_pointer(buckets) + received_size
189 , *pend = ipcdetail::to_raw_pointer(buckets) + old_size
195 bucket_ptr shunk_p = alloc.allocation_command
196 (boost::interprocess::shrink_in_place | boost::interprocess::nothrow_allocation, received_size, received_size, received_size, buckets).first;
197 BOOST_ASSERT(buckets == shunk_p); (void)shunk_p;
199 bucket_ptr buckets_init = buckets + received_size;
200 for(size_type i = 0; i < (old_size - received_size); ++i){
201 to_raw_pointer(buckets_init++)->~bucket_type();
203 return received_size;
206 static bucket_ptr expand_or_create_buckets
207 ( bucket_ptr old_buckets, const size_type old_num
208 , allocator_type &alloc, const size_type new_num)
210 size_type received_size;
211 std::pair<bucket_ptr, bool> ret =
212 alloc.allocation_command
213 (boost::interprocess::expand_fwd | boost::interprocess::allocate_new, new_num, new_num, received_size, old_buckets);
214 if(ret.first == old_buckets){
215 bucket_ptr buckets_init = old_buckets + old_num;
216 for(size_type i = 0; i < (new_num - old_num); ++i){
217 new(to_raw_pointer(buckets_init++))bucket_type();
221 bucket_ptr buckets_init = ret.first;
222 for(size_type i = 0; i < new_num; ++i){
223 new(to_raw_pointer(buckets_init++))bucket_type();
230 static void destroy_buckets
231 (allocator_type &alloc, bucket_ptr buckets, size_type num)
233 bucket_ptr buckets_destroy = buckets;
234 for(size_type i = 0; i < num; ++i){
235 to_raw_pointer(buckets_destroy++)->~bucket_type();
237 alloc.deallocate(buckets, num);
240 iunordered_set_index<MapConfig>* get_this_pointer()
243 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
246 //!Constructor. Takes a pointer to the
247 //!segment manager. Can throw
248 iunordered_set_index(segment_manager_base *mngr)
249 : allocator_holder(mngr)
250 , index_type(bucket_traits(&get_this_pointer()->init_bucket, 1))
253 ~iunordered_set_index()
256 if(index_type::bucket_pointer() != bucket_ptr(&this->init_bucket)){
257 destroy_buckets(this->alloc, index_type::bucket_pointer(), index_type::bucket_count());
261 //!This reserves memory to optimize the insertion of n
262 //!elements in the index
263 void reserve(size_type new_n)
265 //Let's maintain a 1.0f load factor
266 size_type old_n = this->bucket_count();
269 bucket_ptr old_p = this->bucket_pointer();
270 new_n = index_type::suggested_upper_bucket_count(new_n);
274 if(old_p != bucket_ptr(&this->init_bucket))
275 new_p = expand_or_create_buckets(old_p, old_n, this->alloc, new_n);
277 new_p = create_buckets(this->alloc, new_n);
282 //Rehashing does not throw, since neither the hash nor the
283 //comparison function can throw
284 this->rehash(bucket_traits(new_p, new_n));
285 if(new_p != old_p && old_p != bucket_ptr(&this->init_bucket)){
286 destroy_buckets(this->alloc, old_p, old_n);
290 //!This tries to free unused memory
291 //!previously allocated.
294 size_type cur_size = this->size();
295 size_type cur_count = this->bucket_count();
296 bucket_ptr old_p = this->bucket_pointer();
298 if(!this->size() && old_p != bucket_ptr(&this->init_bucket)){
299 this->rehash(bucket_traits(bucket_ptr(&this->init_bucket), 1));
300 destroy_buckets(this->alloc, old_p, cur_count);
303 size_type sug_count = 0; //gcc warning
304 sug_count = index_type::suggested_upper_bucket_count(cur_size);
306 if(sug_count >= cur_count)
310 shrink_buckets(old_p, cur_count, this->alloc, sug_count);
316 //Rehashing does not throw, since neither the hash nor the
317 //comparison function can throw
318 this->rehash(bucket_traits(old_p, sug_count));
322 iterator find(const intrusive_compare_key_type &key)
323 { return index_type::find(key, hash_function(), equal_function()); }
325 const_iterator find(const intrusive_compare_key_type &key) const
326 { return index_type::find(key, hash_function(), equal_function()); }
328 std::pair<iterator, bool>insert_check
329 (const intrusive_compare_key_type &key, insert_commit_data &commit_data)
330 { return index_type::insert_check(key, hash_function(), equal_function(), commit_data); }
332 iterator insert_commit(value_type &val, insert_commit_data &commit_data)
334 iterator it = index_type::insert_commit(val, commit_data);
335 size_type cur_size = this->size();
336 if(cur_size > this->bucket_count()){
338 this->reserve(cur_size);
341 //Strong guarantee: if something goes wrong
342 //we should remove the insertion.
344 //We can use the iterator because the hash function
345 //can't throw and this means that "reserve" will
346 //throw only because of the memory allocation:
347 //the iterator has not been invalidated.
348 index_type::erase(it);
356 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
358 //!Trait class to detect if an index is an intrusive
360 template<class MapConfig>
361 struct is_intrusive_index
362 <boost::interprocess::iunordered_set_index<MapConfig> >
364 static const bool value = true;
366 #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
368 }} //namespace boost { namespace interprocess {
370 #include <boost/interprocess/detail/config_end.hpp>
372 #endif //#ifndef BOOST_INTERPROCESS_IUNORDERED_SET_INDEX_HPP