1 /* 2 * Copyright Andrey Semashev 2007 - 2020. 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 /*! 8 * \file attribute_set_impl.hpp 9 * \author Andrey Semashev 10 * \date 03.07.2010 11 * 12 * \brief This header is the Boost.Log library implementation, see the library documentation 13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. 14 */ 15 16 #ifndef BOOST_LOG_ATTRIBUTE_SET_IMPL_HPP_INCLUDED_ 17 #define BOOST_LOG_ATTRIBUTE_SET_IMPL_HPP_INCLUDED_ 18 19 #include <boost/log/detail/config.hpp> 20 #include <new> 21 #include <memory> 22 #include <limits> 23 #include <utility> 24 #include <algorithm> 25 #include <cstddef> 26 #include <boost/assert.hpp> 27 #include <boost/array.hpp> 28 #include <boost/intrusive/options.hpp> 29 #include <boost/intrusive/list.hpp> 30 #include <boost/intrusive/link_mode.hpp> 31 #include <boost/intrusive/derivation_value_traits.hpp> 32 #include <boost/log/attributes/attribute_set.hpp> 33 #include <boost/log/detail/allocator_traits.hpp> 34 #include <boost/log/detail/header.hpp> 35 36 #ifndef BOOST_LOG_HASH_TABLE_SIZE_LOG 37 // Hash table size will be 2 ^ this value 38 #define BOOST_LOG_HASH_TABLE_SIZE_LOG 4 39 #endif 40 41 #ifndef BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE 42 // Maximum pool size that each attribute set maintains 43 #define BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE 8 44 #endif 45 46 namespace boost { 47 48 BOOST_LOG_OPEN_NAMESPACE 49 50 //! A simple pooling allocator 51 template< typename T > 52 class pool_allocator : 53 public std::allocator< T > 54 { 55 public: 56 template< typename U > 57 struct rebind 58 { 59 typedef pool_allocator< U > other; 60 }; 61 62 typedef std::allocator< T > base_type; 63 64 #if BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE > 0 65 66 typedef typename log::aux::allocator_traits< base_type >::value_type value_type; 67 typedef typename log::aux::allocator_traits< base_type >::size_type size_type; 68 typedef typename log::aux::allocator_traits< base_type >::difference_type difference_type; 69 typedef typename log::aux::allocator_traits< base_type >::pointer pointer; 70 typedef typename log::aux::allocator_traits< base_type >::const_pointer const_pointer; 71 typedef value_type& reference; 72 typedef value_type const& const_reference; 73 74 private: 75 array< pointer, BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE > m_Pool; 76 size_type m_PooledCount; 77 78 public: pool_allocator()79 pool_allocator() : m_PooledCount(0) 80 { 81 } 82 pool_allocator(pool_allocator const & that)83 pool_allocator(pool_allocator const& that) : 84 base_type(static_cast< base_type const& >(that)), 85 m_PooledCount(0) 86 { 87 } 88 89 template< typename U > pool_allocator(pool_allocator<U> const & that)90 pool_allocator(pool_allocator< U > const& that) : 91 base_type(static_cast< typename pool_allocator< U >::base_type const& >(that)), 92 m_PooledCount(0) 93 { 94 } 95 ~pool_allocator()96 ~pool_allocator() 97 { 98 for (size_type i = 0; i < m_PooledCount; ++i) 99 { 100 log::aux::allocator_traits< base_type >::deallocate(*static_cast< base_type* >(this), m_Pool[i], 1); 101 } 102 } 103 operator =(pool_allocator const & that)104 pool_allocator& operator= (pool_allocator const& that) 105 { 106 base_type::operator= (static_cast< base_type const& >(that)); 107 return *this; 108 } 109 110 template< typename U > operator =(pool_allocator<U> const & that)111 pool_allocator& operator= (pool_allocator< U > const& that) 112 { 113 base_type::operator= ( 114 static_cast< typename pool_allocator< U >::base_type const& >(that)); 115 return *this; 116 } 117 allocate(size_type n,const void * hint=NULL)118 pointer allocate(size_type n, const void* hint = NULL) 119 { 120 if (BOOST_LIKELY(m_PooledCount > 0)) 121 { 122 --m_PooledCount; 123 return m_Pool[m_PooledCount]; 124 } 125 else 126 { 127 return log::aux::allocator_traits< base_type >::allocate(*static_cast< base_type* >(this), n, hint); 128 } 129 } 130 deallocate(pointer p,size_type n)131 void deallocate(pointer p, size_type n) 132 { 133 if (BOOST_LIKELY(m_PooledCount < m_Pool.size())) 134 { 135 m_Pool[m_PooledCount] = p; 136 ++m_PooledCount; 137 } 138 else 139 { 140 log::aux::allocator_traits< base_type >::deallocate(*static_cast< base_type* >(this), p, n); 141 } 142 } 143 144 #else 145 146 template< typename U > pool_allocator(pool_allocator<U> const & that)147 pool_allocator(pool_allocator< U > const& that) : 148 base_type(static_cast< typename pool_allocator< U >::base_type const& >(that)) 149 { 150 } 151 152 #endif // BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE > 0 153 }; 154 155 //! Attribute set implementation 156 struct attribute_set::implementation 157 { 158 public: 159 //! Attribute name identifier type 160 typedef key_type::id_type id_type; 161 162 //! Allocator type 163 typedef pool_allocator< node > node_allocator; 164 165 //! Node base class traits for the intrusive list 166 struct node_traits 167 { 168 typedef node_base node; 169 typedef node* node_ptr; 170 typedef node const* const_node_ptr; get_nextboost::attribute_set::implementation::node_traits171 static node* get_next(const node* n) { return n->m_pNext; } set_nextboost::attribute_set::implementation::node_traits172 static void set_next(node* n, node* next) { n->m_pNext = next; } get_previousboost::attribute_set::implementation::node_traits173 static node* get_previous(const node* n) { return n->m_pPrev; } set_previousboost::attribute_set::implementation::node_traits174 static void set_previous(node* n, node* prev) { n->m_pPrev = prev; } 175 }; 176 177 //! Contained node traits for the intrusive list 178 typedef intrusive::derivation_value_traits< 179 node, 180 node_traits, 181 intrusive::normal_link 182 > value_traits; 183 184 //! The container that allows to iterate through elements 185 typedef intrusive::list< 186 node, 187 intrusive::value_traits< value_traits >, 188 intrusive::constant_time_size< true > 189 > node_list; 190 191 //! A hash table bucket 192 struct bucket 193 { 194 //! Points to the first element in the bucket 195 node* first; 196 //! Points to the last element in the bucket (not the one after the last!) 197 node* last; 198 bucketboost::attribute_set::implementation::bucket199 bucket() : first(NULL), last(NULL) {} 200 }; 201 202 //! A list of buckets 203 typedef boost::array< bucket, 1u << BOOST_LOG_HASH_TABLE_SIZE_LOG > buckets; 204 205 //! Cleanup function object used to erase elements from the container 206 struct disposer 207 { 208 typedef void result_type; 209 disposerboost::attribute_set::implementation::disposer210 explicit disposer(node_allocator& alloc) : m_Allocator(alloc) 211 { 212 } operator ()boost::attribute_set::implementation::disposer213 void operator() (node* p) const 214 { 215 p->~node(); 216 m_Allocator.deallocate(p, 1); 217 } 218 219 private: 220 node_allocator& m_Allocator; 221 }; 222 223 private: 224 //! List of nodes 225 node_list m_Nodes; 226 //! Node allocator 227 node_allocator m_Allocator; 228 //! Hash table buckets 229 buckets m_Buckets; 230 231 public: implementationboost::attribute_set::implementation232 implementation() 233 { 234 } 235 implementationboost::attribute_set::implementation236 implementation(implementation const& that) : m_Allocator(that.m_Allocator) 237 { 238 node_list::const_iterator it = that.m_Nodes.begin(), end = that.m_Nodes.end(); 239 for (; it != end; ++it) 240 { 241 node* const n = m_Allocator.allocate(1, NULL); 242 new (n) node(it->m_Value.first, it->m_Value.second); 243 m_Nodes.push_back(*n); 244 245 bucket& b = get_bucket(it->m_Value.first.id()); 246 if (b.first == NULL) 247 b.first = b.last = n; 248 else 249 b.last = n; 250 } 251 } 252 ~implementationboost::attribute_set::implementation253 ~implementation() 254 { 255 m_Nodes.clear_and_dispose(disposer(m_Allocator)); 256 } 257 sizeboost::attribute_set::implementation258 size_type size() const { return m_Nodes.size(); } beginboost::attribute_set::implementation259 iterator begin() { return iterator(m_Nodes.begin().pointed_node()); } endboost::attribute_set::implementation260 iterator end() { return iterator(m_Nodes.end().pointed_node()); } 261 clearboost::attribute_set::implementation262 void clear() 263 { 264 m_Nodes.clear_and_dispose(disposer(m_Allocator)); 265 std::fill_n(m_Buckets.begin(), m_Buckets.size(), bucket()); 266 } 267 insertboost::attribute_set::implementation268 std::pair< iterator, bool > insert(key_type key, mapped_type const& data) 269 { 270 BOOST_ASSERT(!!key); 271 272 bucket& b = get_bucket(key.id()); 273 node* p = b.first; 274 if (p) 275 { 276 // The bucket is not empty, search among the elements 277 p = find_in_bucket(key, b); 278 if (p->m_Value.first == key) 279 return std::make_pair(iterator(p), false); 280 } 281 282 node* const n = m_Allocator.allocate(1, NULL); 283 new (n) node(key, data); 284 285 node_list::iterator it; 286 if (b.first == NULL) 287 { 288 // The bucket is empty 289 b.first = b.last = n; 290 it = m_Nodes.end(); 291 } 292 else if (p == b.last && key.id() > p->m_Value.first.id()) 293 { 294 // The new element should become the last element of the bucket 295 it = m_Nodes.iterator_to(*p); 296 ++it; 297 b.last = n; 298 } 299 else if (p == b.first) 300 { 301 // The new element should become the first element of the bucket 302 it = m_Nodes.iterator_to(*p); 303 b.first = n; 304 } 305 else 306 { 307 // The new element should be within the bucket 308 it = m_Nodes.iterator_to(*p); 309 } 310 311 m_Nodes.insert(it, *n); 312 313 return std::make_pair(iterator(n), true); 314 } 315 eraseboost::attribute_set::implementation316 void erase(iterator it) 317 { 318 typedef node_list::node_traits node_traits; 319 typedef node_list::value_traits value_traits; 320 321 node* p = static_cast< node* >(it.base()); 322 323 // Adjust bucket boundaries, if needed 324 bucket& b = get_bucket(it->first.id()); 325 if (p == b.first) 326 { 327 if (p == b.last) 328 { 329 // The erased element is the only one in the bucket 330 b.first = b.last = NULL; 331 } 332 else 333 { 334 // The erased element is the first one in the bucket 335 b.first = value_traits::to_value_ptr(node_traits::get_next(b.first)); 336 } 337 } 338 else if (p == b.last) 339 { 340 // The erased element is the last one in the bucket 341 b.last = value_traits::to_value_ptr(node_traits::get_previous(b.last)); 342 } 343 344 m_Nodes.erase_and_dispose(m_Nodes.iterator_to(*p), disposer(m_Allocator)); 345 } 346 findboost::attribute_set::implementation347 iterator find(key_type key) 348 { 349 bucket& b = get_bucket(key.id()); 350 node* p = b.first; 351 if (p) 352 { 353 // The bucket is not empty, search among the elements 354 p = find_in_bucket(key, b); 355 if (p->m_Value.first == key) 356 return iterator(p); 357 } 358 359 return end(); 360 } 361 362 private: 363 implementation& operator= (implementation const&); 364 365 //! The function returns a bucket for the specified element get_bucketboost::attribute_set::implementation366 bucket& get_bucket(id_type id) 367 { 368 return m_Buckets[id & (buckets::static_size - 1)]; 369 } 370 371 //! Attempts to find an element with the specified key in the bucket find_in_bucketboost::attribute_set::implementation372 node* find_in_bucket(key_type key, bucket const& b) 373 { 374 typedef node_list::node_traits node_traits; 375 typedef node_list::value_traits value_traits; 376 377 // All elements within the bucket are sorted to speedup the search. 378 node* p = b.first; 379 while (p != b.last && p->m_Value.first.id() < key.id()) 380 { 381 p = value_traits::to_value_ptr(node_traits::get_next(p)); 382 } 383 384 return p; 385 } 386 }; 387 388 BOOST_LOG_CLOSE_NAMESPACE // namespace log 389 390 } // namespace boost 391 392 #include <boost/log/detail/footer.hpp> 393 394 #endif // BOOST_LOG_ATTRIBUTE_SET_IMPL_HPP_INCLUDED_ 395