/* =============================================================================
 *  scpp_memory.hpp
 *  Copyright (c)  2003 - 2011  Nomura Kei
 *  LICENSE :
 *  LGPL (GNU Lesser General General Public License - Version 3,29 June 2007)
 *  http://www.gnu.org/copyleft/lesser.html
 * =============================================================================
 *
 * メモリ管理モジュール.
 */
#ifndef SCPP_MEMORY_HPP
#define SCPP_MEMORY_HPP

#include <new>
#include <string>

#include <scpp_compiler.hpp>


namespace scpp
{

	/**
	 * メモリ管理情報.
	 * メモリを確保したソースファイル名, 行番号, 関数名,
	 * 確保したメモリサイズを表します.
	 */
	struct MemoryInfo
	{
		const char* fileName;
		int         lineNumber;
		const char* functionName;
		int         size;
		int         _mark;
		MemoryInfo* _prev;
		MemoryInfo* _next;
		void*       _data;
	};


	/**
	 * メモリが確保, 開放, あるいはメモリ確保/開放時にエラーが発生した際のリスナインタフェース.
	 * ユーザは, 本リスナを実装し, MemoryManager::setHandler メソッドにて登録することで,
	 * メモリの確保, 開放, エラー発生時の通知を受信することが可能です.
	 *
	 * 本リスナは MemoryManager にて管理しているメモリ
	 * (MemoryManager::malloc, MemoryManager::calloc, MemoryManager::realloc)
	 * に対してのみ有効です.
	 *
	 * new や, malloc, calloc 等を対象としたい場合, SCPP_DEBUG を有効にしてください.
	 */
	class MemoryListener
	{
		public:
			virtual void notifyAlloc(const MemoryInfo& info) = 0;
			virtual void notifyFree(const MemoryInfo& info)   = 0;
			virtual void notifyError(const MemoryInfo& info, const std::string& msg)  = 0;
	};


	/**
	 * メモリ確保, 開放, メモリ確保/開放時にエラー発生した際のデフォルトのインタフェースです.
	 */
	class DefaultMemoryListener : public MemoryListener
	{
		public:
			DefaultMemoryListener();
			virtual ~DefaultMemoryListener();
			virtual void notifyAlloc(const MemoryInfo& info);
			virtual void notifyFree(const MemoryInfo& info);
			virtual void notifyError(const MemoryInfo& info, const std::string& msg);
	};


	/**
	 * メモリ管理.
	 * メモリの確保, 開放を行います.
	 * ハンドラを登録することで, メモリ確保, 開放, エラー発生時の通知を受信することができます.
	 * new 演算子呼び出し自の ファイル名, 行番号, 関数名はスレッドセーフではありません.
	 * C++11
	 */
	namespace MemoryManager
	{
		extern const char* fileName;
		extern int         lineNumber;
		extern const char* functionName;

		void  setListener(MemoryListener* handler);
		void  entries    (void (*handler)(const MemoryInfo& info));
		void  cleanup    (bool (*isCleanup)(const MemoryInfo& info));
		void  printMemoryInfo(const MemoryInfo& info);
		void* malloc     (                   std::size_t size, const char* file, int line, const char* func);
		void* calloc     (std::size_t nmemb, std::size_t size, const char* file, int line, const char* func);
		void* realloc    (void* ptr        , std::size_t size, const char* file, int line, const char* func);
		void  free       (void* ptr);
	}
}



// 以下, メモリ管理が有効な場合のみ
#ifdef SCPP_DEBUG

void* operator new(std::size_t size) throw(std::bad_alloc);
void* operator new(std::size_t size, const std::nothrow_t& t) throw();
void* operator new[](std::size_t size) throw(std::bad_alloc);
void* operator new[](std::size_t size, const std::nothrow_t& t) throw();
void  operator delete(void* p) throw();
void  operator delete(void* p, const std::nothrow_t& t) throw();
void  operator delete[](void* p) throw();
void  operator delete[](void* p, const std::nothrow_t& t) throw();

#define new                                             \
	((scpp::MemoryManager::fileName   = __FILE__,       \
	  scpp::MemoryManager::lineNumber = __LINE__,       \
	  scpp::MemoryManager::functionName = __FUNCTION__, \
	  0) && 0) ? 0 : new

#define malloc(size)         scpp::MemoryManager::malloc (       size, __FILE__, __LINE__, __FUNCTION__)
#define calloc(nmemb, size)  scpp::MemoryManager::calloc (nmemb, size, __FILE__, __LINE__, __FUNCTION__)
#define realloc(ptr, size)   scpp::MemoryManager::realloc(ptr  , size, __FILE__, __LINE__, __FUNCTION__)
#define free(ptr)            scpp::MemoryManager::free   (ptr)

#endif	// SCPP_DEBUG


#endif	// SCPP_MEMORY_HPP

