Blob Blame Raw


#ifndef TCG_POOL_H
#define TCG_POOL_H

// STD includes
#include "assert.h"

#include <vector>
#include <algorithm>
#include <functional>

// Discriminate rvalues support - either use Boost's config.hpp for automatic lookup,
// or deal with it manually

#ifndef TCG_RVALUES_SUPPORT
#include <boost/config.hpp>

#ifdef BOOST_NO_RVALUE_REFERENCES
#define TCG_RVALUES_SUPPORT 0
#else
#define TCG_RVALUES_SUPPORT 1
#endif
#endif

/*!
  \file     pool.h

  \brief    This file contains an implementation of the \a pool container concept.

  \details  A pool is a container that supports two fundamental operations, acquire() and
            release(), whose purpose is that of marking access operations to a resource
            that <I>should not be destroyed</I> when the access operation ends.

            Creation and destruction of resources can be a time-consuming task that could
            be avoided when it is known that resources could be reused - which is the very
            reason why caching can be a useful approach to some problems.

            The classical cache concept is nothing more than that of a standard associative
            map, tweaked to accomodate specific commodities - such as managing the cache
            size.

            However, sometimes resources could be considered to be effectively indentical,
            to the point that assigning a key is merely a way to keep the objects
            distinct in the cache. Users are in the condition to require only two functions,
            one to acquire() a resource, the other to release() it.

            The Pool concept we are introducing satisfies the following ruleset:

            <UL>
            <LI> The pool is a dynamical container which allocates resources by invoking the
                 objects' default constructor. </LI>
            <LI> Objects in the pool are \b not destructed, except when the pool is explicitly
                 \a squeezed by the user.</LI>
            <LI> Keys are indices, returned by the pool with an incremental ordering. </LI>
            <LI> An object which has been acquired cannot be acquired again until it has been
                 released </LI>
            </UL>
*/

namespace tcg
{

//****************************************************************************
//    TCG Indices Pool  class
//****************************************************************************

template <typename T = size_t, typename Cont = std::vector<T>>
class indices_pool
{
public:
	typedef T value_type;
	typedef Cont container_type;

private:
	T m_start;				//!< First index to be acquired
	T m_size;				//!< Current indices count
	Cont m_releasedIndices; //!< Priority queue of released indices

public:
	indices_pool(value_type start = 0) : m_start(start), m_size(0) {}
	~indices_pool() {}

	template <typename iter>
	indices_pool(value_type size, iter releasedBegin, iter releasedEnd, value_type start = 0)
		: m_start(start), m_size(size), m_releasedIndices(releasedBegin, releasedEnd)
	{
		std::make_heap(m_releasedIndices.begin(), m_releasedIndices.end(), std::greater<T>());
	}

	template <typename iter>
	indices_pool(iter acquiredBegin, iter acquiredEnd, value_type start = 0)
		: m_start(start)
	{
		if (acquiredBegin == acquiredEnd)
			m_size = 0;
		else {
			// Sort the acquired indices
			std::vector<T> acquired(acquiredBegin, acquiredEnd);

			std::sort(acquired.begin(), acquired.end());
			assert(acquired.front() >= m_start);

			// Scan them, adding holes in the acquired sequence to released indices
			m_size = acquired.back() - m_start + 1;

			assert(m_size >= (T)acquired.size());
			m_releasedIndices.reserve(m_size - acquired.size());

			T curIdx = m_start;

			typename std::vector<T>::iterator at, aEnd(acquired.end());
			for (at = acquired.begin(); at != aEnd; ++at, ++curIdx)
				for (; curIdx != *at; ++curIdx)
					m_releasedIndices.push_back(curIdx);

			std::make_heap(m_releasedIndices.begin(), m_releasedIndices.end(), std::greater<T>());
		}
	}

	value_type size() const { return m_size; }
	value_type releasedSize() const { return m_releasedIndices.size(); }
	value_type acquiredSize() const { return size() - releasedSize(); }

	const container_type &releasedIndices() const { return m_releasedIndices; }

	value_type acquire()
	{
		if (!m_releasedIndices.empty()) {
			value_type idx = m_releasedIndices.front();

			pop_heap(m_releasedIndices.begin(), m_releasedIndices.end(), std::greater<T>());
			m_releasedIndices.pop_back();

			return idx;
		}

		return m_start + m_size++;
	}

	void release(value_type idx)
	{
		m_releasedIndices.push_back(idx);
		push_heap(m_releasedIndices.begin(), m_releasedIndices.end(), std::greater<T>());
	}

	void clear()
	{
		m_size = 0;
		m_releasedIndices.clear();
	}

	//! Removes pool entries whose index is beyond that of the last acquired one.
	void squeeze()
	{
		if (m_releasedIndices.empty())
			return;

		std::sort_heap(m_releasedIndices.begin(), m_releasedIndices.end(), std::greater<T>()); // O(n logn)

		// Decrease the pool size as long as the back items are unused
		T lastIndex = m_start + m_size - 1;

		typename Cont::iterator ut, uEnd(m_releasedIndices.end());
		for (ut = m_releasedIndices.begin(); ut != uEnd && *ut == lastIndex; ++ut)
			--lastIndex, --m_size;

		if (ut != m_releasedIndices.begin())
			m_releasedIndices.swap(Cont(ut, uEnd));

		std::make_heap(m_releasedIndices.begin(), m_releasedIndices.end(), std::greater<T>()); // O(n)
	}

#if (TCG_RVALUES_SUPPORT > 0)

	indices_pool(const indices_pool &other)
		: m_start(other.m_start), m_size(other.m_size), m_releasedIndices(other.m_releasedIndices)
	{
	}

	indices_pool &operator=(const indices_pool &other)
	{
		m_start = other.m_start, m_size = other.m_size, m_releasedIndices = other.m_releasedIndices;
		return *this;
	}

	indices_pool(indices_pool &&other)
		: m_start(other.m_start), m_size(other.m_size), m_releasedIndices(std::forward<container_type &&>(other.m_releasedIndices)) {}

	indices_pool &operator=(indices_pool &&other)
	{
		m_start = other.m_start, m_size = other.m_size;
		m_releasedIndices = std::forward<container_type &&>(other.m_releasedIndices);
		return *this;
	}

#endif
};

//----------------------------------------------------------------

template <typename T, typename Cont = std::vector<T>>
class pool
{
public:
	typedef T value_type;
	typedef Cont container_type;
	typedef size_t size_type;

private:
	Cont m_objects;						 //!< Objects in the pool
	tcg::indices_pool<size_t> m_indices; //!< Indices Pool representing m_objects

public:
	pool() {}
	~pool() {}

	const tcg::indices_pool<size_t> &indices() const { return m_indices; }

	size_type size() const
	{
		assert(m_indices.size() <= m_objects.size());
		return m_objects.size();
	}
	size_type releasedSize() const { return m_objects.size() - m_indices.acquiredSize(); }
	size_type acquiredSize() const { return m_indices.acquiredSize(); }

	const value_type &operator[](size_type idx) const { return m_objects[idx]; }
	value_type &operator[](size_type idx) { return m_objects[idx]; }

	size_type acquire()
	{
		size_t result = m_indices.acquire();
		if (result >= m_objects.size()) {
			assert(result == m_objects.size());
			m_objects.push_back(T());
		}

		return result;
	}

	void release(size_type idx) { m_indices.release(idx); }

	void releaseAll() { m_indices.clear(); }
	void clear()
	{
		releaseAll();
		m_objects.clear();
	}

	//! Removes pool entries whose index is beyond that of the last acquired one.
	void squeeze()
	{
		m_indices.squeeze();
		m_objects.resize(m_indices.size());
	}
};

} // namespace tcg

#endif // TCG_POOL_H