diff options
Diffstat (limited to 'src/boost/libs/log/src/attribute_value_set.cpp')
-rw-r--r-- | src/boost/libs/log/src/attribute_value_set.cpp | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/src/boost/libs/log/src/attribute_value_set.cpp b/src/boost/libs/log/src/attribute_value_set.cpp new file mode 100644 index 00000000..14d9752a --- /dev/null +++ b/src/boost/libs/log/src/attribute_value_set.cpp @@ -0,0 +1,570 @@ +/* + * Copyright Andrey Semashev 2007 - 2015. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file attribute_value_set.cpp + * \author Andrey Semashev + * \date 19.04.2007 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include <boost/log/detail/config.hpp> +#include <new> +#include <memory> +#include <boost/array.hpp> +#include <boost/intrusive/options.hpp> +#include <boost/intrusive/list.hpp> +#include <boost/intrusive/link_mode.hpp> +#include <boost/intrusive/derivation_value_traits.hpp> +#include <boost/log/attributes/attribute_name.hpp> +#include <boost/log/attributes/attribute_value.hpp> +#include <boost/log/attributes/attribute_value_set.hpp> +#include "alignment_gap_between.hpp" +#include "attribute_set_impl.hpp" +#include "stateless_allocator.hpp" +#include <boost/log/detail/header.hpp> + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +BOOST_FORCEINLINE attribute_value_set::node_base::node_base() : + m_pPrev(NULL), + m_pNext(NULL) +{ +} + +BOOST_FORCEINLINE attribute_value_set::node::node(key_type const& key, mapped_type& data, bool dynamic) : + node_base(), + m_Value(key, mapped_type()), + m_DynamicallyAllocated(dynamic) +{ + m_Value.second.swap(data); +} + +//! Container implementation +struct attribute_value_set::implementation +{ +public: + typedef key_type::id_type id_type; + +private: + typedef attribute_set::implementation attribute_set_impl_type; + typedef boost::log::aux::stateless_allocator< char > stateless_allocator; + + //! Node base class traits for the intrusive list + struct node_traits + { + typedef node_base node; + typedef node* node_ptr; + typedef node const* const_node_ptr; + static node* get_next(const node* n) { return n->m_pNext; } + static void set_next(node* n, node* next) { n->m_pNext = next; } + static node* get_previous(const node* n) { return n->m_pPrev; } + static void set_previous(node* n, node* prev) { n->m_pPrev = prev; } + }; + + //! Contained node traits for the intrusive list + typedef intrusive::derivation_value_traits< + node, + node_traits, + intrusive::normal_link + > value_traits; + + //! A container that provides iteration through elements of the container + typedef intrusive::list< + node, + intrusive::value_traits< value_traits >, + intrusive::constant_time_size< true > + > node_list; + + //! A hash table bucket + struct bucket + { + //! Points to the first element in the bucket + node* first; + //! Points to the last element in the bucket (not the one after the last!) + node* last; + + bucket() : first(NULL), last(NULL) {} + }; + + //! A list of buckets + typedef boost::array< bucket, 1u << BOOST_LOG_HASH_TABLE_SIZE_LOG > buckets; + + //! Element disposer + struct disposer + { + typedef void result_type; + void operator() (node* p) const BOOST_NOEXCEPT + { + if (!p->m_DynamicallyAllocated) + p->~node(); + else + delete p; + } + }; + +private: + //! Pointer to the source-specific attributes + attribute_set_impl_type* m_pSourceAttributes; + //! Pointer to the thread-specific attributes + attribute_set_impl_type* m_pThreadAttributes; + //! Pointer to the global attributes + attribute_set_impl_type* m_pGlobalAttributes; + + //! The container with elements + node_list m_Nodes; + //! The pointer to the end of the allocated elements within the storage + node* m_pEnd; + //! The pointer to the end of storage + node* m_pEOS; + + //! Hash table buckets + buckets m_Buckets; + +private: + //! Constructor + implementation( + node* storage, + node* eos, + attribute_set_impl_type* source_attrs, + attribute_set_impl_type* thread_attrs, + attribute_set_impl_type* global_attrs + ) : + m_pSourceAttributes(source_attrs), + m_pThreadAttributes(thread_attrs), + m_pGlobalAttributes(global_attrs), + m_pEnd(storage), + m_pEOS(eos) + { + } + + //! Destructor + ~implementation() + { + m_Nodes.clear_and_dispose(disposer()); + } + + //! The function allocates memory and creates the object + static implementation* create( + size_type element_count, + attribute_set_impl_type* source_attrs, + attribute_set_impl_type* thread_attrs, + attribute_set_impl_type* global_attrs) + { + // Calculate the buffer size + const size_type header_size = sizeof(implementation) + + aux::alignment_gap_between< implementation, node >::value; + const size_type buffer_size = header_size + element_count * sizeof(node); + + implementation* p = reinterpret_cast< implementation* >(stateless_allocator().allocate(buffer_size)); + node* const storage = reinterpret_cast< node* >(reinterpret_cast< char* >(p) + header_size); + new (p) implementation(storage, storage + element_count, source_attrs, thread_attrs, global_attrs); + + return p; + } + +public: + //! The function allocates memory and creates the object + static implementation* create( + attribute_set const& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count) + { + return create( + source_attrs.m_pImpl->size() + thread_attrs.m_pImpl->size() + global_attrs.m_pImpl->size() + reserve_count, + source_attrs.m_pImpl, + thread_attrs.m_pImpl, + global_attrs.m_pImpl); + } + + //! The function allocates memory and creates the object + static implementation* create( + attribute_value_set const& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count) + { + implementation* p = create( + source_attrs.m_pImpl->size() + thread_attrs.m_pImpl->size() + global_attrs.m_pImpl->size() + reserve_count, + NULL, + thread_attrs.m_pImpl, + global_attrs.m_pImpl); + p->copy_nodes_from(source_attrs.m_pImpl); + return p; + } + + //! The function allocates memory and creates the object + static implementation* create( + BOOST_RV_REF(attribute_value_set) source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count) + { + implementation* p = source_attrs.m_pImpl; + source_attrs.m_pImpl = NULL; + p->m_pThreadAttributes = thread_attrs.m_pImpl; + p->m_pGlobalAttributes = global_attrs.m_pImpl; + return p; + } + + //! The function allocates memory and creates the object + static implementation* create(size_type reserve_count) + { + return create(reserve_count, NULL, NULL, NULL); + } + + //! Creates a copy of the object + static implementation* copy(implementation* that) + { + // Create new object + implementation* p = create(that->size(), NULL, NULL, NULL); + + // Copy all elements + p->copy_nodes_from(that); + + return p; + } + + //! Destroys the object and releases the memory + static void destroy(implementation* p) + { + const size_type buffer_size = reinterpret_cast< char* >(p->m_pEOS) - reinterpret_cast< char* >(p); + p->~implementation(); + stateless_allocator().deallocate(reinterpret_cast< stateless_allocator::pointer >(p), buffer_size); + } + + //! Returns the pointer to the first element + node_base* begin() + { + freeze(); + return m_Nodes.begin().pointed_node(); + } + //! Returns the pointer after the last element + node_base* end() + { + return m_Nodes.end().pointed_node(); + } + + //! Returns the number of elements in the container + size_type size() + { + freeze(); + return m_Nodes.size(); + } + + //! Looks for the element with an equivalent key + node_base* find(key_type key) + { + // First try to find an acquired element + bucket& b = get_bucket(key.id()); + node* p = b.first; + if (p) + { + // The bucket is not empty, search among the elements + p = find_in_bucket(key, b); + if (p->m_Value.first == key) + return p; + } + + // Element not found, try to acquire the value from attribute sets + return freeze_node(key, b, p); + } + + //! Freezes all elements of the container + void freeze() + { + if (m_pSourceAttributes) + { + freeze_nodes_from(m_pSourceAttributes); + m_pSourceAttributes = NULL; + } + if (m_pThreadAttributes) + { + freeze_nodes_from(m_pThreadAttributes); + m_pThreadAttributes = NULL; + } + if (m_pGlobalAttributes) + { + freeze_nodes_from(m_pGlobalAttributes); + m_pGlobalAttributes = NULL; + } + } + + //! Inserts an element + std::pair< node*, bool > insert(key_type key, mapped_type const& mapped) + { + bucket& b = get_bucket(key.id()); + node* p = find_in_bucket(key, b); + if (!p || p->m_Value.first != key) + { + p = insert_node(key, b, p, mapped); + return std::pair< node*, bool >(p, true); + } + else + { + return std::pair< node*, bool >(p, false); + } + } + +private: + //! The function returns a bucket for the specified element + bucket& get_bucket(id_type id) + { + return m_Buckets[id & (buckets::static_size - 1u)]; + } + + //! Attempts to find an element with the specified key in the bucket + node* find_in_bucket(key_type key, bucket const& b) + { + typedef node_list::node_traits node_traits; + typedef node_list::value_traits value_traits; + + // All elements within the bucket are sorted to speedup the search. + node* p = b.first; + while (p != b.last && p->m_Value.first.id() < key.id()) + { + p = value_traits::to_value_ptr(node_traits::get_next(p)); + } + + return p; + } + + //! Acquires the attribute value from the attribute sets + node_base* freeze_node(key_type key, bucket& b, node* where) + { + attribute_set::iterator it; + if (m_pSourceAttributes) + { + it = m_pSourceAttributes->find(key); + if (it != m_pSourceAttributes->end()) + { + // The attribute is found, acquiring the value + return insert_node(key, b, where, it->second.get_value()); + } + } + + if (m_pThreadAttributes) + { + it = m_pThreadAttributes->find(key); + if (it != m_pThreadAttributes->end()) + { + // The attribute is found, acquiring the value + return insert_node(key, b, where, it->second.get_value()); + } + } + + if (m_pGlobalAttributes) + { + it = m_pGlobalAttributes->find(key); + if (it != m_pGlobalAttributes->end()) + { + // The attribute is found, acquiring the value + return insert_node(key, b, where, it->second.get_value()); + } + } + + // The attribute is not found + return m_Nodes.end().pointed_node(); + } + + //! The function inserts a node into the container + node* insert_node(key_type key, bucket& b, node* where, mapped_type data) + { + node* p; + if (m_pEnd != m_pEOS) + { + p = m_pEnd++; + new (p) node(key, data, false); + } + else + { + p = new node(key, data, true); + } + + node_list::iterator it; + if (b.first == NULL) + { + // The bucket is empty + b.first = b.last = p; + it = m_Nodes.end(); + } + else if (where == b.last && key.id() > where->m_Value.first.id()) + { + // The new element should become the last element of the bucket + it = m_Nodes.iterator_to(*where); + ++it; + b.last = p; + } + else if (where == b.first) + { + // The new element should become the first element of the bucket + it = m_Nodes.iterator_to(*where); + b.first = p; + } + else + { + // The new element should be within the bucket + it = m_Nodes.iterator_to(*where); + } + + m_Nodes.insert(it, *p); + + return p; + } + + //! Acquires attribute values from the set of attributes + void freeze_nodes_from(attribute_set_impl_type* attrs) + { + attribute_set::const_iterator it = attrs->begin(), end = attrs->end(); + for (; it != end; ++it) + { + key_type key = it->first; + bucket& b = get_bucket(key.id()); + node* p = b.first; + if (p) + { + p = find_in_bucket(key, b); + if (p->m_Value.first == key) + continue; // the element is already frozen + } + + insert_node(key, b, p, it->second.get_value()); + } + } + + //! Copies nodes of the container + void copy_nodes_from(implementation* from) + { + // Copy all elements + node_list::iterator it = from->m_Nodes.begin(), end = from->m_Nodes.end(); + for (; it != end; ++it) + { + node* n = m_pEnd++; + mapped_type data = it->m_Value.second; + new (n) node(it->m_Value.first, data, false); + m_Nodes.push_back(*n); + + // Since nodes within buckets are ordered, we can simply append the node to the end of the bucket + bucket& b = get_bucket(n->m_Value.first.id()); + if (b.first == NULL) + b.first = b.last = n; + else + b.last = n; + } + } +}; + +//! The constructor creates an empty set +BOOST_LOG_API attribute_value_set::attribute_value_set( + size_type reserve_count +) : + m_pImpl(implementation::create(reserve_count)) +{ +} + +//! The constructor adopts three attribute sets to the set +BOOST_LOG_API attribute_value_set::attribute_value_set( + attribute_set const& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count +) : + m_pImpl(implementation::create(source_attrs, thread_attrs, global_attrs, reserve_count)) +{ +} + +//! The constructor adopts three attribute sets to the set +BOOST_LOG_API attribute_value_set::attribute_value_set( + attribute_value_set const& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count +) : + m_pImpl(implementation::create(source_attrs, thread_attrs, global_attrs, reserve_count)) +{ +} + +//! The constructor adopts three attribute sets to the set +BOOST_LOG_API void attribute_value_set::construct( + attribute_value_set& source_attrs, + attribute_set const& thread_attrs, + attribute_set const& global_attrs, + size_type reserve_count +) +{ + m_pImpl = implementation::create(boost::move(source_attrs), thread_attrs, global_attrs, reserve_count); +} + +//! Copy constructor +BOOST_LOG_API attribute_value_set::attribute_value_set(attribute_value_set const& that) +{ + if (that.m_pImpl) + m_pImpl = implementation::copy(that.m_pImpl); + else + m_pImpl = NULL; +} + +//! Destructor +BOOST_LOG_API attribute_value_set::~attribute_value_set() BOOST_NOEXCEPT +{ + if (m_pImpl) + { + implementation::destroy(m_pImpl); + m_pImpl = NULL; + } +} + +// Iterator generators +BOOST_LOG_API attribute_value_set::const_iterator +attribute_value_set::begin() const +{ + return const_iterator(m_pImpl->begin(), const_cast< attribute_value_set* >(this)); +} + +BOOST_LOG_API attribute_value_set::const_iterator +attribute_value_set::end() const +{ + return const_iterator(m_pImpl->end(), const_cast< attribute_value_set* >(this)); +} + +//! The method returns number of elements in the container +BOOST_LOG_API attribute_value_set::size_type +attribute_value_set::size() const +{ + return m_pImpl->size(); +} + +//! Internal lookup implementation +BOOST_LOG_API attribute_value_set::const_iterator +attribute_value_set::find(key_type key) const +{ + return const_iterator(m_pImpl->find(key), const_cast< attribute_value_set* >(this)); +} + +//! The method acquires values of all adopted attributes. Users don't need to call it, since will always get an already frozen set. +BOOST_LOG_API void attribute_value_set::freeze() +{ + m_pImpl->freeze(); +} + +//! Inserts an element into the set +BOOST_LOG_API std::pair< attribute_value_set::const_iterator, bool > +attribute_value_set::insert(key_type key, mapped_type const& mapped) +{ + std::pair< node*, bool > res = m_pImpl->insert(key, mapped); + return std::pair< const_iterator, bool >(const_iterator(res.first, this), res.second); +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include <boost/log/detail/footer.hpp> |