diff options
Diffstat (limited to 'src/third_party/boost/boost/container/detail/node_pool_impl.hpp')
-rw-r--r-- | src/third_party/boost/boost/container/detail/node_pool_impl.hpp | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/src/third_party/boost/boost/container/detail/node_pool_impl.hpp b/src/third_party/boost/boost/container/detail/node_pool_impl.hpp new file mode 100644 index 00000000000..9ee9e311c01 --- /dev/null +++ b/src/third_party/boost/boost/container/detail/node_pool_impl.hpp @@ -0,0 +1,367 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2005-2011. 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) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_CONTAINER_DETAIL_NODE_POOL_IMPL_HPP +#define BOOST_CONTAINER_DETAIL_NODE_POOL_IMPL_HPP + +#if (defined _MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif + +#include "config_begin.hpp" +#include <boost/container/container_fwd.hpp> +#include <boost/container/detail/workaround.hpp> +#include <boost/container/detail/utilities.hpp> +#include <boost/intrusive/pointer_traits.hpp> +#include <boost/intrusive/set.hpp> +#include <boost/intrusive/slist.hpp> +#include <boost/container/detail/type_traits.hpp> +#include <boost/container/detail/math_functions.hpp> +#include <boost/container/detail/mpl.hpp> +#include <boost/container/detail/pool_common.hpp> +#include <boost/assert.hpp> +#include <cstddef> +#include <functional> //std::unary_function + +namespace boost { +namespace container { +namespace container_detail { + +template<class SegmentManagerBase> +class private_node_pool_impl +{ + //Non-copyable + private_node_pool_impl(); + private_node_pool_impl(const private_node_pool_impl &); + private_node_pool_impl &operator=(const private_node_pool_impl &); + + //A node object will hold node_t when it's not allocated + public: + typedef typename SegmentManagerBase::void_pointer void_pointer; + typedef typename node_slist<void_pointer>::slist_hook_t slist_hook_t; + typedef typename node_slist<void_pointer>::node_t node_t; + typedef typename node_slist<void_pointer>::node_slist_t free_nodes_t; + typedef typename SegmentManagerBase::multiallocation_chain multiallocation_chain; + typedef typename SegmentManagerBase::size_type size_type; + + private: + typedef typename bi::make_slist + < node_t, bi::base_hook<slist_hook_t> + , bi::linear<true> + , bi::constant_time_size<false> >::type blockslist_t; + public: + + //!Segment manager typedef + typedef SegmentManagerBase segment_manager_base_type; + + //!Constructor from a segment manager. Never throws + private_node_pool_impl(segment_manager_base_type *segment_mngr_base, size_type node_size, size_type nodes_per_block) + : m_nodes_per_block(nodes_per_block) + , m_real_node_size(lcm(node_size, size_type(alignment_of<node_t>::value))) + //General purpose allocator + , mp_segment_mngr_base(segment_mngr_base) + , m_blocklist() + , m_freelist() + //Debug node count + , m_allocated(0) + {} + + //!Destructor. Deallocates all allocated blocks. Never throws + ~private_node_pool_impl() + { this->purge_blocks(); } + + size_type get_real_num_node() const + { return m_nodes_per_block; } + + //!Returns the segment manager. Never throws + segment_manager_base_type* get_segment_manager_base()const + { return container_detail::to_raw_pointer(mp_segment_mngr_base); } + + void *allocate_node() + { return priv_alloc_node(); } + + //!Deallocates an array pointed by ptr. Never throws + void deallocate_node(void *ptr) + { priv_dealloc_node(ptr); } + + //!Allocates a singly linked list of n nodes ending in null pointer. + multiallocation_chain allocate_nodes(const size_type n) + { + //Preallocate all needed blocks to fulfill the request + size_type cur_nodes = m_freelist.size(); + if(cur_nodes < n){ + priv_alloc_block(((n - cur_nodes) - 1)/m_nodes_per_block + 1); + } + + //We just iterate the needed nodes to get the last we'll erase + typedef typename free_nodes_t::iterator free_iterator; + free_iterator before_last_new_it = m_freelist.before_begin(); + for(size_type j = 0; j != n; ++j){ + ++before_last_new_it; + } + + //Cache the first node of the allocated range before erasing + free_iterator first_node(m_freelist.begin()); + free_iterator last_node (before_last_new_it); + + //Erase the range. Since we already have the distance, this is O(1) + m_freelist.erase_after( m_freelist.before_begin() + , ++free_iterator(before_last_new_it) + , n); + + //Now take the last erased node and just splice it in the end + //of the intrusive list that will be traversed by the multialloc iterator. + multiallocation_chain chain; + chain.incorporate_after(chain.before_begin(), &*first_node, &*last_node, n); + m_allocated += n; + return boost::move(chain); + } + + void deallocate_nodes(multiallocation_chain chain) + { + typedef typename multiallocation_chain::iterator iterator; + iterator it(chain.begin()), itend(chain.end()); + while(it != itend){ + void *pElem = &*it; + ++it; + priv_dealloc_node(pElem); + } + } + + //!Deallocates all the free blocks of memory. Never throws + void deallocate_free_blocks() + { + typedef typename free_nodes_t::iterator nodelist_iterator; + typename blockslist_t::iterator bit(m_blocklist.before_begin()), + it(m_blocklist.begin()), + itend(m_blocklist.end()); + free_nodes_t backup_list; + nodelist_iterator backup_list_last = backup_list.before_begin(); + + //Execute the algorithm and get an iterator to the last value + size_type blocksize = get_rounded_size + (m_real_node_size*m_nodes_per_block, (size_type) alignment_of<node_t>::value); + + while(it != itend){ + //Collect all the nodes from the block pointed by it + //and push them in the list + free_nodes_t free_nodes; + nodelist_iterator last_it = free_nodes.before_begin(); + const void *addr = get_block_from_hook(&*it, blocksize); + + m_freelist.remove_and_dispose_if + (is_between(addr, blocksize), push_in_list(free_nodes, last_it)); + + //If the number of nodes is equal to m_nodes_per_block + //this means that the block can be deallocated + if(free_nodes.size() == m_nodes_per_block){ + //Unlink the nodes + free_nodes.clear(); + it = m_blocklist.erase_after(bit); + mp_segment_mngr_base->deallocate((void*)addr); + } + //Otherwise, insert them in the backup list, since the + //next "remove_if" does not need to check them again. + else{ + //Assign the iterator to the last value if necessary + if(backup_list.empty() && !m_freelist.empty()){ + backup_list_last = last_it; + } + //Transfer nodes. This is constant time. + backup_list.splice_after + ( backup_list.before_begin() + , free_nodes + , free_nodes.before_begin() + , last_it + , free_nodes.size()); + bit = it; + ++it; + } + } + //We should have removed all the nodes from the free list + BOOST_ASSERT(m_freelist.empty()); + + //Now pass all the node to the free list again + m_freelist.splice_after + ( m_freelist.before_begin() + , backup_list + , backup_list.before_begin() + , backup_list_last + , backup_list.size()); + } + + size_type num_free_nodes() + { return m_freelist.size(); } + + //!Deallocates all used memory. Precondition: all nodes allocated from this pool should + //!already be deallocated. Otherwise, undefined behaviour. Never throws + void purge_blocks() + { + //check for memory leaks + BOOST_ASSERT(m_allocated==0); + size_type blocksize = get_rounded_size + (m_real_node_size*m_nodes_per_block, (size_type)alignment_of<node_t>::value); + typename blockslist_t::iterator + it(m_blocklist.begin()), itend(m_blocklist.end()), aux; + + //We iterate though the NodeBlock list to free the memory + while(!m_blocklist.empty()){ + void *addr = get_block_from_hook(&m_blocklist.front(), blocksize); + m_blocklist.pop_front(); + mp_segment_mngr_base->deallocate((void*)addr); + } + //Just clear free node list + m_freelist.clear(); + } + + void swap(private_node_pool_impl &other) + { + BOOST_ASSERT(m_nodes_per_block == other.m_nodes_per_block); + BOOST_ASSERT(m_real_node_size == other.m_real_node_size); + std::swap(mp_segment_mngr_base, other.mp_segment_mngr_base); + m_blocklist.swap(other.m_blocklist); + m_freelist.swap(other.m_freelist); + std::swap(m_allocated, other.m_allocated); + } + + private: + + struct push_in_list + { + push_in_list(free_nodes_t &l, typename free_nodes_t::iterator &it) + : slist_(l), last_it_(it) + {} + + void operator()(typename free_nodes_t::pointer p) const + { + slist_.push_front(*p); + if(slist_.size() == 1){ //Cache last element + ++last_it_ = slist_.begin(); + } + } + + private: + free_nodes_t &slist_; + typename free_nodes_t::iterator &last_it_; + }; + + struct is_between + : std::unary_function<typename free_nodes_t::value_type, bool> + { + is_between(const void *addr, std::size_t size) + : beg_(static_cast<const char *>(addr)), end_(beg_+size) + {} + + bool operator()(typename free_nodes_t::const_reference v) const + { + return (beg_ <= reinterpret_cast<const char *>(&v) && + end_ > reinterpret_cast<const char *>(&v)); + } + private: + const char * beg_; + const char * end_; + }; + + //!Allocates one node, using single segregated storage algorithm. + //!Never throws + node_t *priv_alloc_node() + { + //If there are no free nodes we allocate a new block + if (m_freelist.empty()) + priv_alloc_block(); + //We take the first free node + node_t *n = (node_t*)&m_freelist.front(); + m_freelist.pop_front(); + ++m_allocated; + return n; + } + + //!Deallocates one node, using single segregated storage algorithm. + //!Never throws + void priv_dealloc_node(void *pElem) + { + //We put the node at the beginning of the free node list + node_t * to_deallocate = static_cast<node_t*>(pElem); + m_freelist.push_front(*to_deallocate); + BOOST_ASSERT(m_allocated>0); + --m_allocated; + } + + //!Allocates several blocks of nodes. Can throw + void priv_alloc_block(size_type num_blocks = 1) + { + if(!num_blocks) + return; + size_type blocksize = + get_rounded_size(m_real_node_size*m_nodes_per_block, (size_type)alignment_of<node_t>::value); + + try{ + for(size_type i = 0; i != num_blocks; ++i){ + //We allocate a new NodeBlock and put it as first + //element in the free Node list + char *pNode = reinterpret_cast<char*> + (mp_segment_mngr_base->allocate(blocksize + sizeof(node_t))); + char *pBlock = pNode; + m_blocklist.push_front(get_block_hook(pBlock, blocksize)); + + //We initialize all Nodes in Node Block to insert + //them in the free Node list + for(size_type i = 0; i < m_nodes_per_block; ++i, pNode += m_real_node_size){ + m_freelist.push_front(*new (pNode) node_t); + } + } + } + catch(...){ + //to-do: if possible, an efficient way to deallocate allocated blocks + throw; + } + } + + //!Deprecated, use deallocate_free_blocks + void deallocate_free_chunks() + { this->deallocate_free_blocks(); } + + //!Deprecated, use purge_blocks + void purge_chunks() + { this->purge_blocks(); } + + private: + //!Returns a reference to the block hook placed in the end of the block + static node_t & get_block_hook (void *block, size_type blocksize) + { + return *reinterpret_cast<node_t*>(reinterpret_cast<char*>(block) + blocksize); + } + + //!Returns the starting address of the block reference to the block hook placed in the end of the block + void *get_block_from_hook (node_t *hook, size_type blocksize) + { + return (reinterpret_cast<char*>(hook) - blocksize); + } + + private: + typedef typename boost::intrusive::pointer_traits + <void_pointer>::template rebind_pointer<segment_manager_base_type>::type segment_mngr_base_ptr_t; + + const size_type m_nodes_per_block; + const size_type m_real_node_size; + segment_mngr_base_ptr_t mp_segment_mngr_base; //Segment manager + blockslist_t m_blocklist; //Intrusive container of blocks + free_nodes_t m_freelist; //Intrusive container of free nods + size_type m_allocated; //Used nodes for debugging +}; + + +} //namespace container_detail { +} //namespace container { +} //namespace boost { + +#include <boost/container/detail/config_end.hpp> + +#endif //#ifndef BOOST_CONTAINER_DETAIL_ADAPTIVE_NODE_POOL_IMPL_HPP |