Newer
Older
c-interpreter / modules / libkcpp / src / kc_memory.cpp
Nomura Kei on 11 Jun 2023 36 KB UPDATE
////////////////////////////////////////////////////////////////////////////////
//
//  メモリ管理モジュール (C++)
//  @copyright  2003 - 2023  Nomura Kei
//

#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstdio>
#include <cerrno>

// 常に本来の malloc, free を利用するため、KCPP_MEMORY_ENABLED を無効化する。
#ifdef KCPP_MEMORY_ENABLED
#undef KCPP_MEMORY_ENABLED
#endif
#include <kcpp_memory.hpp>


using namespace kcpp;
namespace kcpp
{


	////////////////////////////////////////////////////////////////////////////
	//
	// MemoryListener
	//

	/**
	 * MemoryListener を構築します。
	 */
	MemoryListener::MemoryListener() {
		// NOP
	}


	/**
	 * MemoryListener を破棄します。
	 */
	MemoryListener::~MemoryListener()
	{
		// NOP
	}


	/**
	 * メモリ確保時に呼び出されます。
	 *
	 * @param entry メモリエントリ(未使用)
	 */
	void MemoryListener::notifyAllocate([[maybe_unused]] const MemoryEntry& entry)
	{
		// NOP
	}


	/**
	 * メモリ解放時に呼び出されます。
	 *
	 * @param entry メモリエントリ(未使用)
	 */
	void MemoryListener::notifyFree([[maybe_unused]] const MemoryEntry& entry)
	{
		// NOP
	}


	/**
	 * エラー発生時に呼び出されます。
	 *
	 * @param entry メモリエントリ(未使用)
	 */
	void MemoryListener::notifyError([[maybe_unused]] const MemoryEntry& entry, [[maybe_unused]] const char* msg)
	{
		// NOP
	}



	////////////////////////////////////////////////////////////////////////////
	//
	// MemoryEntry ユーティリティ (initMemoryEntry)
	//

	/**
	 * メモリエントリに指定されたパラメータを設定、初期化します。
	 *
	 * @param entry 初期化設定するメモリエントリ
	 * @param size  確保サイズ
	 * @param mark  確保メモリ状態
	 * @param file  メモリ確保ファイル名
	 * @param func  メモリ確保関数名
	 * @param line  メモリ確保行番号
	 */
	void initMemoryEntry(MemoryEntry* entry,
		std::size_t size, MemoryMark mark, const char* file, const char* func, int line)
	{
		entry->file  = file;
		entry->func  = func;
		entry->line  = line;
		entry->size  = size;
		entry->_mark = mark;
		entry->_prev = nullptr;
		entry->_next = nullptr;
		entry->data  = (entry + 1);
	}



	////////////////////////////////////////////////////////////////////////////
	//
	// MemoryEntryManager (内部クラス)
	// メモリのエントリを管理します。
	//

	/**
	 * メモリエントリを管理するクラス。
	 */
	class MemoryEntryManager
	{
		public:
			static constexpr int MAX_BUFFER_SIZE = 256;

			MemoryEntryManager();
			virtual ~MemoryEntryManager();
			void add(MemoryEntry* entry);
			void remove(MemoryEntry* entry);
			void entries(bool (*handler)(const MemoryEntry& entry));
			void freeif(bool (*handler)(const MemoryEntry& entry));
			void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn);
		private:
			void dumpBinary(std::ostream& stream, void* data, size_t size, int limit);
			void dumpAscii(std::ostream& stream, void* data, size_t size, int limit);
			const char* toPaddingString(const char* str, int limit);
			const char* toHexString(unsigned char data);
			MemoryEntryManager(const MemoryEntryManager& mgr) = delete;
			MemoryEntryManager& operator=(const MemoryEntryManager& mgr) = delete;
			MemoryEntry head;
			MemoryEntry tail;
			std::recursive_mutex  entryMutex;
			char tmpbuf[MAX_BUFFER_SIZE];
	};


	/**
	 * MemoryEntryManager を構築します。
	 */
	MemoryEntryManager::MemoryEntryManager() :
		head { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr },
		tail { nullptr, nullptr, 0, 0, MEMORY_MARK_DELETED, nullptr, nullptr, nullptr },
		entryMutex(),
		tmpbuf{ 0 }
	{
		initMemoryEntry(&head, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0);
		initMemoryEntry(&tail, 0, MEMORY_MARK_DELETED, nullptr, nullptr, 0);
		head._prev = head._next = &tail;
		tail._prev = tail._next = &head;
	}


	/**
	 * MemoryEntryManager を破棄します。
	 */
	MemoryEntryManager::~MemoryEntryManager()
	{
		// NOP
	}


	/**
	 * 指定されたメモリエントリを追加します。
	 *
	 * @param entry 追加するメモリエントリ 
	 */
	void MemoryEntryManager::add(MemoryEntry* entry)
	{
		std::lock_guard<std::recursive_mutex> lock(entryMutex);

		// [tail] の一つ前に挿入する
		entry->_next        = &tail;
		entry->_prev        = tail._prev;
		tail._prev->_next   = entry;
		tail._prev          = entry;
	}


	/**
	 * 指定されたメモリエントリを削除します。
	 *
	 * @param entry 削除するメモリエントリ
	 */
	void MemoryEntryManager::remove(MemoryEntry* entry)
	{
		std::lock_guard<std::recursive_mutex> lock(entryMutex);

		// entry の前後を直接リンクさせる
		entry->_prev->_next = entry->_next;
		entry->_next->_prev = entry->_prev;
	}


	/**
	 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
	 * ハンドラの戻り値が false の場合、呼び出しを終了します。
	 *
	 * @param handler ハンドラ
	 */
	void MemoryEntryManager::entries(bool (*handler)(const MemoryEntry& entry))
	{
		std::lock_guard<std::recursive_mutex> lock(entryMutex);

		// 管理している全メモリエントリをループ
		// handler が false の場合は、停止する。
		bool isContinue = true;
		for (MemoryEntry* current = head._next; isContinue && (current != &tail); current = current->_next)
		{
			isContinue = handler(*current);
		}
	}


	/**
	 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
	 * ハンドラの戻り値が true の場合、該当するメモリを解放します。
	 * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。
	 *
	 * @param handler ハンドラ
	 */
	void MemoryEntryManager::freeif(bool (*handler)(const MemoryEntry& entry))
	{
		std::lock_guard<std::recursive_mutex> lock(entryMutex);

		// 管理している全メモリエントリをループ
		// handler が false の場合は、停止する。
		bool isFree = false;
		for (MemoryEntry* current = head._next; current != &tail; )
		{
			isFree = handler(*current);
			current = current->_next;
			if (isFree)
			{
				MemoryManager::free(current->_prev->data);
			}
		}
	}


	/**
	 * 管理しているメモリエントリをダンプします。
	 *
	 * @param stream ダンプ出力先ストリーム
	 * @param width  幅
	 * @param isDumpBinary バイナリダンプ
	 * @param isDumpAscii  ASCII ダンプ
	 * @param maxColumn    最大桁数
	 */
	void MemoryEntryManager::dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn)
	{
		std::lock_guard<std::recursive_mutex> lock(entryMutex);

		// ファイル名:行番号 (size=サイズ) [func=関数名] 部分の最大表示桁数を取得する。
		int infoColumn = maxColumn;
		infoColumn -= (isDumpBinary) ? (dumpByte * 3) + 2 : 0;
		infoColumn -= (isDumpAscii)  ? (dumpByte    ) + 3 : 0;
		if (infoColumn < 0)
		{
			infoColumn = 0;
		}

		// 管理している全メモリエントリをループ
		for (MemoryEntry* current = head._next; current != &tail; current = current->_next)
		{
			// ファイル名:行番号 (size=サイズ) [func=関数名] 部分出力
			std::stringstream ss;
			ss << current->file << ":" << current->line << " (size=" << current->size << ")"
				<< " [func=" << current->func << "]";
			stream << toPaddingString(ss.str().c_str(), infoColumn);

			// 16進数ダンプ
			if (isDumpBinary)
			{
				stream << " | ";
				dumpBinary(stream, current->data, current->size, dumpByte);
			}

			// ASCII ダンプ
			if (isDumpAscii)
			{
				stream << " | ";
				dumpAscii(stream, current->data, current->size, dumpByte);
			}

			stream << std::endl;
		}
	}


	/**
	 * 指定されたデータを指定されたストリームにダンプします。
	 *
	 * @param stream 出力先ストリーム
	 * @param data ダンプするデータのポインタ
	 * @param size ダンプするデータのサイズ
	 * @param limit ダンプする最大数
	 */
	void MemoryEntryManager::dumpBinary(std::ostream& stream, void* data, size_t size, int limit)
	{
		unsigned char* dataPtr = static_cast<unsigned char*>(data);
		int dataLen = (static_cast<int>(size) < limit) ? static_cast<int>(size) : limit;
		int idx = 0;
		for (; idx < dataLen; idx++)
		{
			if (idx != 0) { stream << " "; }
			stream << toHexString(dataPtr[idx]);
		}
		for (; idx < limit; idx++)
		{
			if (idx != 0) { stream << " "; }
			stream << "--";
		}
	}


	/**
	 * 指定されたデータを ASCII 文字で、指定されたストリームにダンプします。
	 *
	 * @param stream 出力先ストリーム
	 * @param data ダンプするデータのポインタ
	 * @param size ダンプするデータのサイズ
	 * @param limit ダンプする最大数
	 */
	void MemoryEntryManager::dumpAscii(std::ostream& stream, void* data, size_t size, int limit)
	{
		unsigned char* dataPtr = static_cast<unsigned char*>(data);
		int dataLen = (static_cast<int>(size) < limit) ? static_cast<int>(size) : limit;
		int idx = 0;
		for (; idx < dataLen; idx++)
		{
			stream << static_cast<char>((((0x20 <= dataPtr[idx]) && (dataPtr[idx] < 0x7F)) ? dataPtr[idx] : '.'));
		}
		for (; idx < limit; idx++)
		{
			 stream << " ";
		}
	}

	/**
	 * 指定された文字列を指定された文字数にパディングします。
	 * 指定された limit より文字数が多い場合は、limit まで文字列が切り詰められます。
	 *
	 * @param str 文字列
	 * @param limit 文字数
	 * @return 制限された文字列
	 */
	const char* MemoryEntryManager::toPaddingString(const char* str, int limit)
	{
		int maxLimit = (limit <= (MAX_BUFFER_SIZE - 1)) ? limit : (MAX_BUFFER_SIZE - 1);
		int len      = std::strlen(str);
		if (len < maxLimit)
		{
			memcpy(tmpbuf, str, len);
			memset((tmpbuf + len), ' ', (maxLimit - len));
		}
		else
		{
			memcpy(tmpbuf, str, maxLimit);
		}
		tmpbuf[maxLimit] = '\0';
		return tmpbuf;
	}


	/**
	 * 指定されたデータを16進数文字列に変換します。
	 *
	 * @param data 変換するデータ
	 * @return 16進数文字列
	 */
	const char* MemoryEntryManager::toHexString(unsigned char data)
	{
		static const char* HEX_STRINGS = "0123456789ABCDEF";
		tmpbuf[0] = HEX_STRINGS[(static_cast<int>(data) >> 4) & 0x0F];
		tmpbuf[1] = HEX_STRINGS[ static_cast<int>(data)       & 0x0F];
		tmpbuf[2] = '\0';
		return tmpbuf;
	}



	////////////////////////////////////////////////////////////////////////////
	//
	// MemoryManager
	// メモリ管理
	//

	namespace MemoryManager
	{
		namespace
		{
			// =================================================================
			//  内部定数
			// =================================================================
			int const PADDING						= sizeof(void*) * 2;

			// =================================================================
			//  内部関数プロトタイプ宣言
			// =================================================================
			std::new_handler getNewHandler();
			void* allocate(std::size_t size,                         MemoryMark mark, const char* file, const char* func, int line);
			void* allocate(std::size_t size, std::align_val_t align, MemoryMark mark, const char* file, const char* func, int line);
			void* reallocate(void* ptr, std::size_t size,            MemoryMark mark, const char* file, const char* func, int line);
			void  deallocate(void* ptr,                              MemoryMark expectedMark);

			// reallocate から呼び出される関数
			void* reallocateManagedPtr(void* ptr, std::size_t size,
				   	MemoryMark mark, const char* file, const char* func, int line);
			void* reallocateUnManagedPtr(void* ptr, std::size_t size,
				   	MemoryMark mark, const char* file, const char* func, int line);
			void* reallocateInvalidPtr(void* ptr, std::size_t size,
				   	MemoryMark mark, const char* file, const char* func, int line);

			// deallocate から呼び出される関数
			void deallocateEntry(MemoryEntry* entry);


			// =================================================================
			//  内部変数
			// =================================================================
			MemoryListener  defaultListener;
			MemoryListener* listener = &defaultListener;	//!< メモリ管理リスナ
			MemoryEntryManager entryMgr;

		}


		// =================================================================
		//  new 実施時の情報を一時保持するための変数
		// =================================================================
		thread_local const char* file;	//!< ファイル名
		thread_local const char* func;	//!< 関数
		thread_local int         line;	//!< 行番号

	
		/**
		 * メモリ確保, 解放, エラー発生時に通知を受信するリスナを登録します。
		 *
		 * @param l 登録するリスナ
		 */
		void setListener(MemoryListener& l)
		{
			listener = &l;
		}


		/**
		 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
		 * ハンドラの戻り値が false の場合、呼び出しを終了します。
		 *
		 * @param handler ハンドラ
		 */
		void entries(bool (*handler)(const MemoryEntry& entry))
		{
			entryMgr.entries(handler);
		}


		/**
		 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
		 * ハンドラの戻り値が true の場合、該当するメモリを解放します。
		 * new で確保したメモリが解放される際、デストラクタは呼び出されないため注意ください。
		 *
		 * @param handler ハンドラ
		 */
		void freeif(bool (*handler)(const MemoryEntry& entry))
		{
			entryMgr.freeif(handler);
		}


		/**
		 * 管理しているメモリエントリ情報をダンプします。
		 *
		 * @param stream ダンプ先ストリーム
		 * @param dumpByte ダンプするバイト数
		 * @param isDumpBinary バイナリをダンプする
		 * @param isDumpAscii  アスキーをダンプする
		 * @param maxColumn    最大表示桁数
		 */
		void dump(std::ostream& stream, int dumpByte, bool isDumpBinary, bool isDumpAscii, int maxColumn)
		{
			entryMgr.dump(stream, dumpByte, isDumpBinary, isDumpAscii, maxColumn);
		}


		/**
		 * 指定されたサイズのメモリを確保します。
		 *
		 * @param size  確保するメモリサイズ
		 * @param file  メモリ確保ファイル名
		 * @param func  メモリ確保関数名
		 * @param line  メモリ確保行番号
		 * @return 確保したメモリへのポインタ
		 */
		void* malloc(std::size_t size, const char* file, const char* func, int line)
		{
			void* ptr = allocate(size, MEMORY_MARK_ALLOCATED, file, func, line);
			return ptr;
		}


		/**
		 * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。
		 *
		 * @param nmemb 確保する要素数
		 * @param size  1要素のメモリサイズ
		 * @param file  メモリ確保ファイル名
		 * @param func  メモリ確保関数名
		 * @param line  メモリ確保行番号
		 * @return 確保したメモリへのポインタ
		 */
		void* cmalloc(std::size_t nmemb, std::size_t size, const char* file, const char* func, int line)
		{
			size_t n = nmemb * size;
			void* ptr = allocate(n, MEMORY_MARK_ALLOCATED, file, func, line);
			if (ptr != nullptr)
			{
				std::memset(ptr, 0x00, n);
			}
			return ptr;
		}


		/**
		 * 指定されたポインタが指すメモリサイズを変更します。
		 *
		 * @param ptr   メモリサイズを変更するポインタ
		 * @param size  変更後のメモリサイズ
		 * @param file  メモリ確保ファイル名
		 * @param func  メモリ確保関数名
		 * @param line  メモリ確保行番号
		 * @return 確保したメモリへのポインタ
		 */
		void* realloc(void* ptr, std::size_t size, const char* file, const char* func, int line)
		{
			void* nptr = reallocate(ptr, size, MEMORY_MARK_ALLOCATED, file, func, line);
			return nptr;
		}


		/**
		 * 指定されたメモリを解放します。
		 *
		 * @param ptr   解放するメモリへのポインタ
		 */
		void free(void* ptr)
		{
			deallocate(ptr, MEMORY_MARK_ALLOCATED);
		}


		namespace
		{
			// =================================================================
			//  内部関数の実装
			// =================================================================

			/**
			 * new_handler を取得します。
			 * @return new_handler
			 */
			std::new_handler getNewHandler()
			{
				std::new_handler p = std::set_new_handler(0);
				std::set_new_handler(p);
				return p;
			}


			/**
			 * 指定されたサイズのメモリを確保します。
			 *
			 * @param size  確保するメモリサイズ
			 * @param mark  メモリ確保情報
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void* allocate(std::size_t size, MemoryMark mark, const char* file, const char* func, int line)
			{
				MemoryEntry* entry;
			   
				// メモリ確保 [@see C++ Programming Language 3rd $14.4.5]
				for (;;)
				{
					entry = static_cast<MemoryEntry*>(std::malloc(size + sizeof(MemoryEntry) + PADDING));
					if (entry != nullptr) { break; }

					if (std::new_handler nhandler = getNewHandler())
					{
						nhandler();
					}
					else
					{	// メモリ確保失敗
						MemoryEntry errorEntry;
						initMemoryEntry(&errorEntry, size, mark, file, func, line);
						listener->notifyError(errorEntry, "can't allocate");
						return nullptr;
					}
				}

				initMemoryEntry(entry, size, mark, file, func, line);
				entryMgr.add(entry);
				listener->notifyAllocate(*entry);
				return (entry->data);
			}


			/**
			 * 指定されたサイズのメモリを確保します。
			 *
			 * @param align アライメント
			 * @param size  確保するメモリサイズ
			 * @param mark  メモリ確保情報
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void* allocate(std::size_t size, std::align_val_t align,
				   	MemoryMark mark, const char* file, const char* func, int line)
			{
				MemoryEntry* entry;
			   
				// メモリ確保 [@see C++ Programming Language 3rd $14.4.5]
				for (;;)
				{
					entry = static_cast<MemoryEntry*>(std::aligned_alloc(static_cast<std::size_t>(align), size + sizeof(MemoryEntry) + PADDING));
					if (entry != nullptr) { break; }

					if (std::new_handler nhandler = getNewHandler())
					{
						nhandler();
					}
					else
					{	// メモリ確保失敗
						MemoryEntry errorEntry;
						initMemoryEntry(&errorEntry, size, mark, file, func, line);
						listener->notifyError(errorEntry, "can't allocate");
						return nullptr;
					}
				}

				initMemoryEntry(entry, size, mark, file, func, line);
				entryMgr.add(entry);
				listener->notifyAllocate(*entry);
				return (entry->data);
			}



			/**
			 * 指定されたポインタが指すメモリサイズを変更します。
			 * ポインタが nullptr の場合、allocate を呼び出します。
			 *
			 * @param ptr   メモリサイズを変更するポインタ
			 * @param size  変更後のメモリサイズ
			 * @param mark  メモリ確保情報
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void* reallocate(void* ptr, std::size_t size, MemoryMark mark, const char* file, const char* func, int line)
			{
				if (ptr == nullptr)
				{
					return allocate(size, mark, file, func, line);
				}

				MemoryEntry* oldEntry = static_cast<MemoryEntry*>(ptr);
				oldEntry--;
				switch (oldEntry->_mark)
				{
					case MEMORY_MARK_DELETED:				// 削除済み -> 通常の allocate と同様とする。
						return allocate(size, mark, file, func,line);
					case MEMORY_MARK_ALLOCATED:				// 管理されたメモリの realloc
						return reallocateManagedPtr(ptr, size, mark, file, func, line);
					case MEMORY_MARK_ALLOCATED_NEW:			// 不正 (new で確保されたメモリへの realloc)
						return reallocateInvalidPtr(ptr, size, mark, file, func, line);
					case MEMORY_MARK_ALLOCATED_NEW_ARRAY:	// 不正 (new[] で確保されたメモリへの realloc)
						return reallocateInvalidPtr(ptr, size, mark, file, func, line);
					default:								// 管理外メモリの realloc
						return reallocateUnManagedPtr(ptr, size, mark, file, func, line);
				}
			}


			/**
			 * 指定されたポインタの指すメモリ領域を解放します。
			 * nullptr の場合何もしません。
			 * 管理されたメモリの場合、管理領域を合わせて解放します。
			 * 管理外メモリの場合、fee を実行します。
			 *
			 * @param ptr          解放するメモリへのポインタ
			 * @param expectedMark 期待するメモリ確保情報
			 */
			void deallocate(void* ptr, MemoryMark expectedMark)
			{
				if (ptr == nullptr)
				{
					return;
				}

				MemoryEntry* entry = static_cast<MemoryEntry*>(ptr);
				entry--;
				if (entry->_mark == expectedMark)
				{	// 期待するメモリ確保情報の場合、そのまま解放する。
					deallocateEntry(entry);
				}
				else
				{	// 期待しないメモリ確保情報の場合
					switch (entry->_mark)
					{
						case MEMORY_MARK_DELETED:				// 削除済み
							// Nothing to do.
							break;
						case MEMORY_MARK_ALLOCATED:				// 管理メモリ
							listener->notifyError(*entry, "warning free memory (please use free)");
							deallocateEntry(entry);
							break;
						case MEMORY_MARK_ALLOCATED_NEW:			// new により確保されたメモリ
							listener->notifyError(*entry, "warning free memory (please use delete)");
							deallocateEntry(entry);
							break;
						case MEMORY_MARK_ALLOCATED_NEW_ARRAY:	// new[] により確保されたメモリ
							listener->notifyError(*entry, "warning free memory (please use delete[])");
							deallocateEntry(entry);
							break;
						default:								// 管理外メモリ
							std::free(ptr);
							break;
					}
				}
			}


			/**
			 * 管理されたメモリ領域に対する realloc を実施します。
			 *
			 * @param ptr   メモリサイズを変更するポインタ
			 * @param size  変更後のメモリサイズ
			 * @param mark  メモリ確保情報
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void* reallocateManagedPtr(void* ptr, std::size_t size,
				   	MemoryMark mark, const char* file, const char* func, int line)
			{
				MemoryEntry* oldEntry = static_cast<MemoryEntry*>(ptr);
				oldEntry--;

				// メモリのエントリより削除する
				entryMgr.remove(oldEntry);
				MemoryEntry* entry = static_cast<MemoryEntry*>(std::realloc(oldEntry, size + sizeof(MemoryEntry) + PADDING));
				if (entry != nullptr)
				{	// メモリ確保成功
					// -> 管理領域の情報を更新して、メモリのエントリとして追加する。
					initMemoryEntry(entry, size, mark, file, func, line);
					entryMgr.add(entry);
					listener->notifyAllocate(*entry);
					return (entry->data);
				}
				else
				{	// メモリ確保失敗
					// エラーハンドラを実行して nullptr を返す。
					MemoryEntry errorEntry;
					initMemoryEntry(&errorEntry, size, mark, file, func, line);
					listener->notifyError(errorEntry, "can't reallocate");
					return nullptr;
				}
			}


			/**
			 * 管理外メモリ領域に対する realloc を実施します。
			 *
			 * @param ptr   メモリサイズを変更するポインタ
			 * @param size  変更後のメモリサイズ
			 * @param mark  メモリ確保情報
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ
			 */
			void* reallocateUnManagedPtr(void* ptr, std::size_t size,
				   	MemoryMark mark, const char* file, const char* func, int line)
			{
				// |<-- 新たな領域 ---------------->|
				// +------------+-------------------+
				// | 元々の領域 | 追加分 + 管理領域 |
				// +------------+-------------------+
				//  ↓
				//  ↓memmove で 元々の領域 + 追加分 を、
				//  ↓管理領域分を確保した先にコピーする
				//  ↓
				// +----------+------------+--------+
				// | 管理領域 | 元々の領域 | 追加分 |
				// +----------+------------+--------+
				MemoryEntry* entry = static_cast<MemoryEntry*>(std::realloc(ptr, size + sizeof(MemoryEntry) + PADDING));
				if (entry != NULL)
				{	// メモリ確保成功
					// memmove で 元々の領域 + 追加分をコピーして、メモリのエントリとして追加する。
					std::memmove((entry + 1), entry, size);
					initMemoryEntry(entry, size, mark, file, func, line);
					entryMgr.add(entry);
					listener->notifyAllocate(*entry);
					return (entry->data);
				}
				else
				{	// メモリ確保失敗
					// エラーハンドラを実行して nullptr を返す。
					MemoryEntry errorEntry;
					initMemoryEntry(&errorEntry, size, mark, file, func, line);
					listener->notifyError(errorEntry, "can't reallocate");
					return nullptr;
				}
			}


			/**
			 * 不正なメモリ領域に対する realloc のエラー処理を実施します。
			 *
			 * @param ptr   メモリサイズを変更するポインタ
			 * @param size  変更後のメモリサイズ
			 * @param mark  メモリ確保情報
			 * @param file  メモリ確保ファイル名
			 * @param func  メモリ確保関数名
			 * @param line  メモリ確保行番号
			 * @return 確保したメモリへのポインタ(nullptr 固定)
			 */
			void* reallocateInvalidPtr([[maybe_unused]] void* ptr, std::size_t size,
				   	MemoryMark mark, const char* file, const char* func, int line)
			{
				errno = EINVAL;
				MemoryEntry errorEntry;
				initMemoryEntry(&errorEntry, size, mark, file, func, line);
				listener->notifyError(errorEntry, "can't reallocate (invalid pointer)");
				return nullptr;
			}


			/**
			 * 指定されたメモリ管理およびデータ領域を解放します。
			 *
			 * @param entry 解放するメモリ管理領域へのポインタ
			 */
			void deallocateEntry(MemoryEntry* entry)
			{
				listener->notifyFree(*entry);
				entry->_mark = MEMORY_MARK_DELETED;
				entry->size  = 0;
				entryMgr.remove(entry);
				std::free(entry);
			}
		}

	}

}


////////////////////////////////////////////////////////////////////////////////
//
// new/delete 演算子のオーバライド
//

// C++17 (C++1z) 以降の new/delete 演算子


// =============================================================================
//  new 演算子
// =============================================================================
// 下記、順に
// (1) 記憶域確保
// (2) 例外送出なしで記憶域確保
// (3) デフォルトより大きいアライメント要求の記憶域確保
// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保

/**
 * new による記憶域確保。
 *
 * @param size 確保するメモリサイズ
 */
NODISCARD void* operator new(std::size_t size)
{
	void* p = MemoryManager::allocate(
			size,
			MEMORY_MARK_ALLOCATED_NEW,
			MemoryManager::file,
			MemoryManager::func,
			MemoryManager::line);
	if (p == nullptr)
	{
		throw std::bad_alloc();
	}
	return p;
}


/**
 * new による例外送出なしでの記憶域確保。
 *
 * @param size 確保するメモリサイズ
 */
NODISCARD void* operator new(std::size_t size, const std::nothrow_t&) noexcept
{
	void* p = MemoryManager::allocate(
			size,
			MEMORY_MARK_ALLOCATED_NEW,
			MemoryManager::file,
			MemoryManager::func,
			MemoryManager::line);
	return p;
}


/**
 * new によるデフォルトより大きいアライメント要求の記憶域確保。
 *
 * @param size      確保するメモリサイズ
 * @param alignment アライメント
 */
NODISCARD void* operator new(std::size_t size, std::align_val_t alignment)
{
	void* p = MemoryManager::allocate(
			size,
			alignment,
			MEMORY_MARK_ALLOCATED_NEW,
			MemoryManager::file,
			MemoryManager::func,
			MemoryManager::line);
	if (p == nullptr)
	{
		throw std::bad_alloc();
	}
	return p;
}


/**
 * new によるデフォルトより大きいアライメント要求の記憶域確保。
 *
 * @param size      確保するメモリサイズ
 * @param alignment アライメント
 */
NODISCARD void* operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept
{
	void* p = MemoryManager::allocate(
			size,
			alignment,
			MEMORY_MARK_ALLOCATED_NEW,
			MemoryManager::file,
			MemoryManager::func,
			MemoryManager::line);
	return p;
}


// =============================================================================
//  new[] 演算子
// =============================================================================
// 下記、順に
// (1) 記憶域確保
// (2) 例外送出なしで記憶域確保
// (3) デフォルトより大きいアライメント要求の記憶域確保
// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域確保

/**
 * new[] による記憶域確保。
 *
 * @param size 確保するメモリサイズ
 */
NODISCARD void* operator new[](std::size_t size)
{
	void* p = MemoryManager::allocate(
			size,
			MEMORY_MARK_ALLOCATED_NEW_ARRAY,
			MemoryManager::file,
			MemoryManager::func,
			MemoryManager::line);
	if (p == nullptr)
	{
		throw std::bad_alloc();
	}
	return p;
}


/**
 * new[] による例外送出なしでの記憶域確保。
 *
 * @param size 確保するメモリサイズ
 */
NODISCARD void* operator new[](std::size_t size, const std::nothrow_t&) noexcept
{
	void* p = MemoryManager::allocate(
			size,
			MEMORY_MARK_ALLOCATED_NEW_ARRAY,
			MemoryManager::file,
			MemoryManager::func,
			MemoryManager::line);
	return p;
}


/**
 * new[] によるデフォルトより大きいアライメント要求の記憶域確保。
 *
 * @param size      確保するメモリサイズ
 * @param alignment アライメント
 */
NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment)
{
	void* p = MemoryManager::allocate(
			size,
			alignment,
			MEMORY_MARK_ALLOCATED_NEW_ARRAY,
			MemoryManager::file,
			MemoryManager::func,
			MemoryManager::line);
	if (p == nullptr)
	{
		throw std::bad_alloc();
	}
	return p;
}


/**
 * new[] によるデフォルトより大きいアライメント要求の記憶域確保。
 *
 * @param size      確保するメモリサイズ
 * @param alignment アライメント
 */
NODISCARD void* operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept
{
	void* p = MemoryManager::allocate(
			size,
			alignment,
			MEMORY_MARK_ALLOCATED_NEW_ARRAY,
			MemoryManager::file,
			MemoryManager::func,
			MemoryManager::line);
	return p;
}

// =============================================================================
//  delete 演算子
// =============================================================================
// 下記、順に
// (1) 記憶域解放
// (2) 例外送出なしで確保された記憶域解放
// (3) デフォルトより大きいアライメント要求の記憶域解放
// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放
// (5) オブジェクトサイズが判明している記憶域解放
// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放


/**
 * delete により、記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 */
void operator delete(void* ptr) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW);
}


/**
 * delete により、例外送出なしで確保された記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 */
void operator delete(void* ptr, const std::nothrow_t&) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW);
}


/**
 * delete により、デフォルトより大きいアライメント要求の記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 * @param alignment アライメント
 */
void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW);
}


/**
 * delete により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 * @param alignment アライメント
 */
void operator delete(void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW);
}


/**
 * delete により、オブジェクトサイズが判明している記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 * @param size サイズ
 */
void operator delete(void* ptr, [[maybe_unused]] std::size_t size) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW);
}


/**
 * delete により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 * @param alignment アライメント
 */
void operator delete(void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW);
}



// =============================================================================
//  delete[] 演算子
// =============================================================================
// 下記、順に
// (1) 記憶域解放
// (2) 例外送出なしで確保された記憶域解放
// (3) デフォルトより大きいアライメント要求の記憶域解放
// (4) 例外送出なしでデフォルトより大きいアライメント要求の記憶域解放
// (5) オブジェクトサイズが判明している記憶域解放
// (6) オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域解放


/**
 * delete[] により、記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 */
void operator delete[](void* ptr) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY);
}


/**
 * delete[] により、例外送出なしで確保された記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 */
void operator delete[](void* ptr, const std::nothrow_t&) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY);
}


/**
 * delete[] により、デフォルトより大きいアライメント要求の記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 * @param alignment アライメント
 */
void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignment) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY);
}


/**
 * delete[] により、例外創出なしでデフォルトより大きいアライメント要求の記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 * @param alignment アライメント
 */
void operator delete[](void* ptr, [[maybe_unused]] std::align_val_t alignement, [[maybe_unused]] const std::nothrow_t&) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY);
}


/**
 * delete[] により、オブジェクトサイズが判明している記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 * @param size サイズ
 */
void operator delete[](void* ptr, [[maybe_unused]] std::size_t size) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY);
}


/**
 * delete[] により、オブジェクトサイズが判明しているデフォルトより大きいアライメント要求の記憶域を解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 * @param alignment アライメント
 */
void operator delete[](void* ptr, [[maybe_unused]] std::size_t size, [[maybe_unused]] std::align_val_t alignment) noexcept
{
	MemoryManager::deallocate(ptr, MEMORY_MARK_ALLOCATED_NEW_ARRAY);
}