blob: 9c9886e9fcf49c28591a4ad73e11f19e6c5ca0e0 [file] [log] [blame]
/*
* 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 <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 beginning of the storage of the elements
node* m_pStorage;
//! 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_pStorage(storage),
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 - 1)];
}
//! 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.first)
{
// The new element should become the first element of the bucket
it = m_Nodes.iterator_to(*where);
b.first = p;
}
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
{
// 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>