• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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