mirror of
https://github.com/boostorg/log.git
synced 2026-02-10 11:32:32 +00:00
570 lines
17 KiB
C++
570 lines
17 KiB
C++
/*
|
|
* 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 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 - 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>
|