/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Implements an EASTL allocator that uses an ICoreAllocator.
// However, this header file is not dependent on ICoreAllocator or its package.
///////////////////////////////////////////////////////////////////////////////

#pragma once

#if EASTL_CORE_ALLOCATOR_ENABLED


#include <EASTL/internal/config.h>

/// EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR
///
/// This allows the application to override the default name for the default global core allocator.
/// However, you must be careful in your usage of this, as if this file is shared between uses then 
/// you will need to be careful that your override of this doesn't conflict with others. 
///
#ifndef EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR
	#define EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR AllocatorType::GetDefaultAllocator
#endif



namespace EA
{
	namespace Allocator
	{
		/// CoreAllocatorAdapter
		///
		/// Implements the EASTL allocator interface.
		/// Allocates memory from an instance of ICoreAllocator or another class with an equivalent interface.
		/// ICoreAllocator is a pure-virtual memory allocation interface used by a number of EA games and 
		/// shared libraries. It's completely unrelated to EASTL, but it's prevalent enough that it's useful
		/// for EASTL to have a built-in adapter for this interface. ICoreAllocator is declared in the 
		/// CoreAllocator package icoreallocator_interface.h header, but CoreAllocatorAdapter can work with
		/// any equivalent interface, as defined below.
		///
		/// Expected interface:
		///     enum AllocFlags {
		///         kFlagTempMemory = 0,
		///         kFlagPermMemory = 1
		///     };
		///     
		///     struct CoreAllocator {
		///         void* Alloc(size_t size, const char* name, unsigned int allocFlags);
		///         void* Alloc(size_t size, const char* name, unsigned int allocFlags,     // Not required unless you are working with types that require custom alignment.
		///                      unsigned int align, unsigned int alignOffset = 0);
		///         void Free(void* block, size_t size = 0);
		///         static CoreAllocator* GetDefaultAllocator();
		///     };
		///
		/// Example usage:
		///     #include <coreallocator/icoreallocator_interface.h>
		///     typedef EA::Allocator::CoreAllocatorAdapter<EASTLTestCoreAllocator> Adapter;
		///     eastl::list<Widget, Adapter> widgetList(Adapter("UI/WidgetList", pSomeCoreAllocator));
		///     widgetList.push_back(Widget());
		///
		/// Example usage:
		///     #include <MyEquivalentCoreAllocatorInterface.h>
		///     eastl::list<Widget, CoreAllocatorAdapter<MyCoreAllocatorInterface> > widgetList;
		///     widgetList.push_back(Widget());
		///
		/// Example usage:
		///     #include <coreallocator/icoreallocator_interface.h>
		///     typedef EA::Allocator::CoreAllocatorAdapter<EASTLTestCoreAllocator> Adapter;
		///     typedef eastl::list<Widget, Adapter> WidgetList;
		///     CoreAllocatorFixed<WidgetList::node_type> widgetCoreAllocator(pFixedAllocatorForWidgetListValueType); // CoreAllocatorFixed is a hypothetical implementation of the ICoreAllocator interface.
		///     WidgetList widgetList(Adapter("UI/WidgetList", &widgetCoreAllocator));                                // Note that the widgetCoreAllocator is declared before and thus destroyed after the widget list.
		///
		template<class AllocatorType>
		class CoreAllocatorAdapter
		{
		public:
			typedef CoreAllocatorAdapter<AllocatorType> this_type;

		public:
			// To do: Make this constructor explicit, when there is no known code dependent on it being otherwise.
			CoreAllocatorAdapter(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME), AllocatorType* pAllocator = EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR());
			CoreAllocatorAdapter(const char* pName, AllocatorType* pAllocator, int flags);
			CoreAllocatorAdapter(const CoreAllocatorAdapter& x);
			CoreAllocatorAdapter(const CoreAllocatorAdapter& x, const char* pName);

			CoreAllocatorAdapter& operator=(const CoreAllocatorAdapter& x);

			void* allocate(size_t n, int flags = 0);
			void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0);
			void  deallocate(void* p, size_t n);

			AllocatorType* get_allocator() const;
			void           set_allocator(AllocatorType* pAllocator);

			int  get_flags() const;
			void set_flags(int flags);

			const char* get_name() const;
			void        set_name(const char* pName);

		public: // Public because otherwise VC++ generates (possibly invalid) warnings about inline friend template specializations.
			AllocatorType* mpCoreAllocator;
			int            mnFlags;    // Allocation flags. See ICoreAllocator/AllocFlags.

			#if EASTL_NAME_ENABLED
				const char* mpName; // Debug name, used to track memory.
			#endif
		};

		template<class AllocatorType>
		bool operator==(const CoreAllocatorAdapter<AllocatorType>& a, const CoreAllocatorAdapter<AllocatorType>& b);

		template<class AllocatorType>
		bool operator!=(const CoreAllocatorAdapter<AllocatorType>& a, const CoreAllocatorAdapter<AllocatorType>& b);



		/// EASTLICoreAllocator
		///
		/// Provides a standardized typedef for ICoreAllocator;
		/// 
		/// Example usage:
		///     eastl::list<Widget, EASTLICoreAllocator> widgetList("UI/WidgetList", pSomeCoreAllocator);
		///     widgetList.push_back(Widget());
		///
		class ICoreAllocator;
		class EASTLCoreAllocatorImpl;

		typedef CoreAllocatorAdapter<ICoreAllocator> EASTLICoreAllocatorAdapter;
		typedef CoreAllocatorAdapter<EASTLCoreAllocatorImpl> EASTLCoreAllocatorAdapter;
		typedef EASTLICoreAllocatorAdapter EASTLICoreAllocator;  // for backwards compatibility



		/// EASTLICoreDeleter
		///
		/// Implements a functor which can free memory from the specified
		/// ICoreAllocator interface.  This is a convenience object provided for
		/// users who wish to have EASTL containers deallocate memory obtained from
		/// ICoreAllocator interfaces.
		///
		template <class AllocatorType>
		class CoreDeleterAdapter
		{
		public:
			typedef CoreDeleterAdapter<AllocatorType> this_type;
			AllocatorType* mpCoreAllocator;

		public:
			CoreDeleterAdapter(AllocatorType* pAllocator = EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR()) EA_NOEXCEPT 
			: mpCoreAllocator(pAllocator) {}

			~CoreDeleterAdapter() EA_NOEXCEPT {}

			template <typename T>
			void operator()(T* p)
			{
				p->~T();
				mpCoreAllocator->Free(p);
			}

			CoreDeleterAdapter(const CoreDeleterAdapter& in) { mpCoreAllocator = in.mpCoreAllocator; }

			CoreDeleterAdapter(CoreDeleterAdapter&& in)
			{
				mpCoreAllocator = in.mpCoreAllocator;
				in.mpCoreAllocator = nullptr;
			}

			CoreDeleterAdapter& operator=(const CoreDeleterAdapter& in)
			{
				mpCoreAllocator = in.mpCoreAllocator;
				return *this;
			}

			CoreDeleterAdapter& operator=(CoreDeleterAdapter&& in)
			{
				mpCoreAllocator = in.mpCoreAllocator;
				in.mpCoreAllocator = nullptr;
				return *this;
			}
		};



		/// EASTLICoreDeleter
		///
		/// Provides a standardized typedef for ICoreAllocator implementations.
		///
		/// Example usage: 
		///     eastl::shared_ptr<A> foo(pA, EASTLCoreDeleter());
		///
		typedef CoreDeleterAdapter<ICoreAllocator> EASTLICoreDeleterAdapter;
		typedef CoreDeleterAdapter<EASTLCoreAllocatorImpl> EASTLCoreDeleterAdapter;

	} // namespace Allocator

} // namespace EA





///////////////////////////////////////////////////////////////////////////////
// Inlines
///////////////////////////////////////////////////////////////////////////////

namespace EA
{
	namespace Allocator
	{
		template<class AllocatorType>
		inline CoreAllocatorAdapter<AllocatorType>::CoreAllocatorAdapter(const char* EASTL_NAME(pName), AllocatorType* pCoreAllocator)
			: mpCoreAllocator(pCoreAllocator), mnFlags(0)
		{
			#if EASTL_NAME_ENABLED
				mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
			#endif
		}

		template<class AllocatorType>
		inline CoreAllocatorAdapter<AllocatorType>::CoreAllocatorAdapter(const char* EASTL_NAME(pName), AllocatorType* pCoreAllocator, int flags)
			: mpCoreAllocator(pCoreAllocator), mnFlags(flags)
		{
			#if EASTL_NAME_ENABLED
				mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
			#endif
		}

		template<class AllocatorType>
		inline CoreAllocatorAdapter<AllocatorType>::CoreAllocatorAdapter(const CoreAllocatorAdapter& x)
			: mpCoreAllocator(x.mpCoreAllocator), mnFlags(x.mnFlags)
		{
			#if EASTL_NAME_ENABLED
				mpName = x.mpName;
			#endif
		}

		template<class AllocatorType>
		inline CoreAllocatorAdapter<AllocatorType>::CoreAllocatorAdapter(const CoreAllocatorAdapter& x, const char* EASTL_NAME(pName))
			: mpCoreAllocator(x.mpCoreAllocator), mnFlags(x.mnFlags)
		{
			#if EASTL_NAME_ENABLED
				mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
			#endif
		}

		template<class AllocatorType>
		inline CoreAllocatorAdapter<AllocatorType>& CoreAllocatorAdapter<AllocatorType>::operator=(const CoreAllocatorAdapter& x)
		{
			mpCoreAllocator = x.mpCoreAllocator;
			mnFlags         = x.mnFlags;

			#if EASTL_NAME_ENABLED
				mpName = x.mpName;
			#endif
				
			return *this;
		}

		template<class AllocatorType>
		inline void* CoreAllocatorAdapter<AllocatorType>::allocate(size_t n, int /*flags*/)
		{
			// It turns out that EASTL itself doesn't use the flags parameter, 
			// whereas the user here might well want to specify a flags 
			// parameter. So we use ours instead of the one passed in.
			return mpCoreAllocator->Alloc(n, EASTL_NAME_VAL(mpName), (unsigned)mnFlags);
		}

		template<class AllocatorType>
		inline void* CoreAllocatorAdapter<AllocatorType>::allocate(size_t n, size_t alignment, size_t offset, int /*flags*/)
		{
			// It turns out that EASTL itself doesn't use the flags parameter, 
			// whereas the user here might well want to specify a flags 
			// parameter. So we use ours instead of the one passed in.
			return mpCoreAllocator->Alloc(n, EASTL_NAME_VAL(mpName), (unsigned)mnFlags, (unsigned)alignment, (unsigned)offset);
		}

		template<class AllocatorType>
		inline void CoreAllocatorAdapter<AllocatorType>::deallocate(void* p, size_t n)
		{
			return mpCoreAllocator->Free(p, n);
		}

		template<class AllocatorType>
		inline AllocatorType* CoreAllocatorAdapter<AllocatorType>::get_allocator() const
		{
			return mpCoreAllocator;
		}

		template<class AllocatorType>
		inline void CoreAllocatorAdapter<AllocatorType>::set_allocator(AllocatorType* pAllocator)
		{
			mpCoreAllocator = pAllocator;
		}

		template<class AllocatorType>
		inline int CoreAllocatorAdapter<AllocatorType>::get_flags() const
		{
			return mnFlags;
		}

		template<class AllocatorType>
		inline void CoreAllocatorAdapter<AllocatorType>::set_flags(int flags)
		{
			mnFlags = flags;
		}

		template<class AllocatorType>
		inline const char* CoreAllocatorAdapter<AllocatorType>::get_name() const
		{
			#if EASTL_NAME_ENABLED
				return mpName;
			#else
				return EASTL_ALLOCATOR_DEFAULT_NAME;
			#endif
		}

		template<class AllocatorType>
		inline void CoreAllocatorAdapter<AllocatorType>::set_name(const char* pName)
		{
			#if EASTL_NAME_ENABLED
				mpName = pName;
			#else
				(void)pName;
			#endif
		}



		template<class AllocatorType>
		inline bool operator==(const CoreAllocatorAdapter<AllocatorType>& a, const CoreAllocatorAdapter<AllocatorType>& b)
		{
			return (a.mpCoreAllocator == b.mpCoreAllocator) &&
				   (a.mnFlags         == b.mnFlags);
		}

		template<class AllocatorType>
		inline bool operator!=(const CoreAllocatorAdapter<AllocatorType>& a, const CoreAllocatorAdapter<AllocatorType>& b)
		{
			return (a.mpCoreAllocator != b.mpCoreAllocator) ||
				   (a.mnFlags         != b.mnFlags);
		}


	} // namespace Allocator

} // namespace EA


#endif // EASTL_CORE_ALLOCATOR_ENABLED








