#ifndef _ATLALLOC2_H_INCLUDED
#define _ATLALLOC2_H_INCLUDED

#pragma warning (push)
#include <atlbase.h>
#pragma warning (pop)

#pragma warning (push)
#include <atlmem.h>
#pragma warning (pop)

#pragma warning (push)
#include <atlstr.h>
#pragma warning (pop)

namespace ATL2 {

typedef ATL::CAtlStringA CStringBlob;

template< typename T, int t_nFixedBytes = 128>
class CTempBufferEx
{
public:
    CTempBufferEx( __in ATL::IAtlMemMgr *pMemMgr = &m_crtHeap ) throw() :
	m_p( NULL ), m_pMemMgr( pMemMgr )
    {
    }

    CTempBufferEx( size_t nElements,
	__in ATL::IAtlMemMgr *pMemMgr = &m_crtHeap ) //throw(...)
	: m_p( NULL ), m_pMemMgr( pMemMgr )
    {
	Allocate( nElements );
    }

    ~CTempBufferEx() throw()
    {
	if( m_p != reinterpret_cast< T* >( m_abFixedBuffer ) )
	{
	    FreeHeap();
	}
    }

    operator T*() const throw()
    {
	return( m_p );
    }
    T* operator->() const throw()
    {
	ATLASSERT( m_p != NULL );
	return( m_p );
    }

    T* Allocate( size_t nElements ) //throw(...)
    {
	return( AllocateBytes( nElements*sizeof( T ) ) );
    }

    T* Reallocate( size_t nElements ) //throw(...)
    {
	size_t nNewSize = nElements*sizeof( T ) ;

	if (m_p == NULL)
	    return AllocateBytes(nNewSize);

	if (nNewSize > t_nFixedBytes)
	{
	    if( m_p == reinterpret_cast< T* >( m_abFixedBuffer ) )
	    {
		// We have to allocate from the heap and copy the contents into the new buffer
		AllocateHeap(nNewSize);
		memcpy(m_p, m_abFixedBuffer, t_nFixedBytes);
	    }
	    else
	    {
		ReAllocateHeap( nNewSize );
	    }
	}
	else
	{
	    m_p = reinterpret_cast< T* >( m_abFixedBuffer );
	}

	return m_p;
    }

    T* AllocateBytes( size_t nBytes )
    {
	ATLASSERT( m_p == NULL );
	if( nBytes > t_nFixedBytes )
	{
	    AllocateHeap( nBytes );
	}
	else
	{
	    m_p = reinterpret_cast< T* >( m_abFixedBuffer );
	}

	return( m_p );
    }

private:
    ATL_NOINLINE void AllocateHeap( size_t nBytes )
    {
	T* p = static_cast< T* >( m_pMemMgr->Allocate( nBytes ) );
	if( p == NULL )
	{
	    AtlThrow( E_OUTOFMEMORY );
	}
	m_p = p;
    }

    ATL_NOINLINE void ReAllocateHeap( size_t nNewSize)
    {
	T* p = static_cast< T* >( m_pMemMgr->Reallocate(m_p, nNewSize) );
	if ( p == NULL )
	{
	    AtlThrow( E_OUTOFMEMORY );
	}
	m_p = p;
    }

    ATL_NOINLINE void FreeHeap() throw()
    {
	m_pMemMgr->Free( m_p );
    }

private:
    T* m_p;
    BYTE m_abFixedBuffer[t_nFixedBytes];
    ATL::IAtlMemMgr *m_pMemMgr;
public:
    static ATL::CCRTHeap m_crtHeap;
};

template< typename T, int t_nFixedBytes>
ATL::CCRTHeap CTempBufferEx<T, t_nFixedBytes>::m_crtHeap;

} // namespace ATL2

//#include "atlalloc.inl"

#ifndef _ATL2_NO_AUTOMATIC_NAMESPACE
using namespace ATL2;
#endif //!_ATL2_NO_AUTOMATIC_NAMESPACE

#endif // _ATLALLOC2_H_INCLUDED
