Newer
Older
c-interpreter / modules / libkc / src / kc_memory.c
Nomura Kei on 9 Aug 2023 28 KB UPDATE
/**
 * @file  kc_memory.c
 * @brief メモリ管理モジュール
 * @copyright  2003 - 2023  Nomura Kei
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>



// 常に本来の malloc, free を利用するため、KC_MEMORY_ENABLED を無効化する。
#ifdef KC_MEMORY_ENABLED
#undef KC_MEMORY_ENABLED
#endif
#include <kc_memory.h>
#include <kc_memory_dump.h>
#include <kc_lock_guard.h>


////////////////////////////////////////////////////////////////////////////////
//
//  定数定義
//

/** パディング	*/
#define KC_MEMORY_PADDING (sizeof(void*) * 2)



////////////////////////////////////////////////////////////////////////////////
//
//  プロトタイプ宣言

// ----- KcMemoryEntry -----
static KcMemoryEntry* KcMemoryEntry_new(KcMemoryEntry* entry, size_t alignment, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
static void KcMemoryEntry_delete(KcMemoryEntry* entry);
static void KcMemoryEntry_set(KcMemoryEntry* entry,
		size_t size, KcMemoryMark mark, const char* file, const char* func, int line);

// ----- KcMemoryListener -----
static void KcMemoryListener_allocate(const KcMemoryEntry* entry);
static void KcMemoryListener_free(const KcMemoryEntry* entry);
static void KcMemoryListener_error(const KcMemoryEntry* entry, const char* msg);

// --- KcMemoryManager
static bool  KcMemoryManager_set_listener(KcMemoryListener* listener);
static bool  KcMemoryManager_entries(bool (*handler)(const KcMemoryEntry* entry, void* info), void* info);
static bool  KcMemoryManager_freeif(bool (*handler)(const KcMemoryEntry* entry, void* info), void* info);
static void  KcMemoryManager_dump(bool (*handler)(const char* data), int bytes, bool binary, bool ascii, int max_column);
static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line);
static void* KcMemoryManager_aligned_alloc(size_t alignment, size_t size, const char* file, const char* func, int line);
static void* KcMemoryManager_calloc(size_t nmemb, size_t size, const char* file, const char* func, int line);
static void* KcMemoryManager_realloc(void* ptr, size_t size, const char* file, const char* func, int line);
static void  KcMemoryManager_free(void* ptr);
static void  KcMemoryManager_init(void);
static void  KcMemoryManager_init_nop(void);
static bool  KcMemoryManager_add(KcMemoryEntry* entry);
static bool  KcMemoryManager_remove(KcMemoryEntry* entry);
static void* KcMemoryManager_allocate(size_t alignment, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
static void* KcMemoryManager_reallocate(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
static void* KcMemoryManager_reallocate_managed_ptr(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
static void* KcMemoryManager_reallocate_invalid_ptr(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
static void* KcMemoryManager_reallocate_unmanaged_ptr(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
static void KcMemoryManager_deallocate(void* ptr, KcMemoryMark expected_mark);



////////////////////////////////////////////////////////////////////////////////
//
//  変数
//

// --- 公開変数
static mtx_t           KcMemoryManager_mutex;
static KcMemoryManager KcMemoryManager_mmgr = {
	// --- 公開関数 ---
	.set_listener				= KcMemoryManager_set_listener,
	.entries					= KcMemoryManager_entries,
	.freeif						= KcMemoryManager_freeif,
	.dump						= KcMemoryManager_dump,
	.malloc						= KcMemoryManager_malloc,
	.aligned_alloc				= KcMemoryManager_aligned_alloc,
	.calloc						= KcMemoryManager_calloc,
	.realloc					= KcMemoryManager_realloc,
	.free						= KcMemoryManager_free,
	// --- 内部関数 ---
	._init						= KcMemoryManager_init,
	._add						= KcMemoryManager_add,
	._remove					= KcMemoryManager_remove,
	._allocate					= KcMemoryManager_allocate,
	._reallocate				= KcMemoryManager_reallocate,
	._reallocate_managed_ptr	= KcMemoryManager_reallocate_managed_ptr,
	._reallocate_invalid_ptr	= KcMemoryManager_reallocate_invalid_ptr,
	._reallocate_unmanaged_ptr	= KcMemoryManager_reallocate_unmanaged_ptr,
	._deallocate				= KcMemoryManager_deallocate,
	// --- 変数 ---
	._listener		= {
		.allocate	= KcMemoryListener_allocate,
		.free		= KcMemoryListener_free,
		.error		= KcMemoryListener_error
	},
	._head = {
		.size		= 0,
		.mark		= KC_MEMORY_DELETED,
		.file		= NULL,
		.func		= NULL,
		.line		= 0,
		._prev		= &KcMemoryManager_mmgr._tail,
		._next		= &KcMemoryManager_mmgr._tail,
		.data		= NULL
	},
	._tail = {
		.size		= 0,
		.mark		= KC_MEMORY_DELETED,
		.file		= NULL,
		.func		= NULL,
		.line		= 0,
		._prev		= &KcMemoryManager_mmgr._head,
		._next		= &KcMemoryManager_mmgr._head,
		.data		= NULL
	},
	._error = {
		.size		= 0,
		.mark		= KC_MEMORY_DELETED,
		.file		= NULL,
		.func		= NULL,
		.line		= 0,
		._prev		= NULL,
		._next		= NULL,
		.data		= NULL
	},
	._tmpbuf		= { 0 },
	._mutex			= &KcMemoryManager_mutex
};

KcMemoryManager* const kc_memory_manager = &KcMemoryManager_mmgr;



////////////////////////////////////////////////////////////////////////////////
//
//  公開関数 実装
//

// =============================================================================
//  KcMemoryMark
// =============================================================================

/**
 * 指定されたメモリ状態(mark)に対応する文字列表現を返します。
 * 返される文字列は、次の通り
 * - alloc  : malloc, calloc, realloc によりメモリが確保された
 * - new    : new によりメモリが確保された
 * - new[]  : new[] によりメモリが確保された
 * - delete : 削除済みメモリ
 * - other  : 不明
 *
 * @param mark メモリ状態
 * @return メモリ状態に対応する文字列表現
 */
const char* KcMemoryMark_to_string(int mark)
{
	switch (mark)
	{
		case KC_MEMORY_DELETED:				return "delete";
		case KC_MEMORY_ALLOCATED:			return "alloc ";
		case KC_MEMORY_ALLOCATED_NEW:		return "new   ";
		case KC_MEMORY_ALLOCATED_NEW_ARRAY:	return "new[] ";
		default:							return "other ";
	}
}



// =============================================================================
//  KcMemoryEntry
// =============================================================================


/**
 * KcMemoryEntry を構築します。
 * entry が NULL の場合、新規に KeMemoryEntry を構築します。
 * entry が NULL でない場合、entry の管理するメモリサイズを変更し、各種値を更新します。
 * 構築に失敗した場合、NULL を返します。
 *
 * @param entry  メモリエントリ
 * @param alignment アライメント
 * @param size   メモリサイズ
 * @param mark   メモリ状態
 * @param file   メモリ確保ファイル名
 * @param func   メモリ確保関数名
 * @param line   メモリ確保行番号
 * @return 構築した KcMemoryEntry
 */
static KcMemoryEntry* KcMemoryEntry_new(KcMemoryEntry* entry, size_t alignment, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line)
{
	KcMemoryEntry* new_entry;
	if ((entry == NULL) && (alignment > 0))
	{	// アライメント指定でメモリを確保する。
		new_entry = (KcMemoryEntry*) aligned_alloc(alignment,
				(size_t) (size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING));
	}
	else
	{
		new_entry = (KcMemoryEntry*) realloc(entry,
				(size_t) (size + sizeof(KcMemoryEntry) + KC_MEMORY_PADDING));
	}

	KcMemoryEntry_set(new_entry, size, mark, file, func, line);
	return new_entry;
}


/**
 * KcMemoryEntry を破棄します。
 *
 * @param entry 破棄するメモリエントリ
 */
static void KcMemoryEntry_delete(KcMemoryEntry* entry)
{
	entry->mark = KC_MEMORY_DELETED;
	entry->size = 0;
	free(entry);
}


/**
 * 指定された entry に、指定された値を設定します。
 * entry が NULL の場合、何もしません。
 *
 * @param entry  メモリエントリ
 * @param size   メモリサイズ
 * @param mark   メモリ状態
 * @param file   メモリ確保ファイル名
 * @param func   メモリ確保関数名
 * @param line   メモリ確保行番号
 */
static void KcMemoryEntry_set(KcMemoryEntry* entry,
		size_t size, KcMemoryMark mark, const char* file, const char* func, int line)
{
	if (entry != NULL)
	{
		entry->size  = size;
		entry->mark  = mark;
		entry->file  = file;
		entry->func  = func;
		entry->line  = line;
		entry->_prev = NULL;
		entry->_next = NULL;
		entry->data  = (entry + 1);
	}
}



// =============================================================================
//  KcMemoryListener
// =============================================================================


// -------------------------------------
//  allocate
// -------------------------------------
/**
 * メモリ確保の際に呼び出されるデフォルトのコールバック関数
 *
 * @param entry 確保されたメモリエントリ
 */
static void KcMemoryListener_allocate(const KcMemoryEntry* entry)
{
	// Nothing to do.
	UNUSED_VARIABLE(entry);
}


// -------------------------------------
//  free
// -------------------------------------
/**
 * メモリ解放の差異に呼び出されるデフォルトのコールバック関数
 *
 * @param entry 解放されるメモリエントリ
 */
static void KcMemoryListener_free(const KcMemoryEntry* entry)
{
	// Nothing to do.
	UNUSED_VARIABLE(entry);
}


// -------------------------------------
//  error 
// -------------------------------------
/**
 * エラー発生時に呼び出されるデフォルトのコールバック関数
 *
 * @param entry エラー発生したメモリエントリ (NULL の場合あり)
 * @param msg   エラー発生時のメッセージ
 */
static void KcMemoryListener_error(const KcMemoryEntry* entry, const char* msg)
{
	// Nothing to do.
	UNUSED_VARIABLE(entry);
	UNUSED_VARIABLE(msg);
}



// =============================================================================
//  KcMemoryManager
// =============================================================================


// -------------------------------------
//  set_listener
// -------------------------------------
/**
 * メモリ確保、解放、エラー発生時に通知するためのリスナを登録します。
 *
 * @param listener 登録するリスナ
 * @return true/false (リスナ登録成功/リスナ登録失敗)
 */
static bool KcMemoryManager_set_listener(KcMemoryListener* listener)
{
	kc_memory_manager->_init();
	kc_lock_guard(kc_memory_manager->_mutex)
	{	// リスナ関数を設定。
		// 関数が NULL の場合は、デフォルトの関数を設定する。
		kc_memory_manager->_listener.allocate = (listener->allocate != NULL)
			? listener->allocate : KcMemoryListener_allocate;
		kc_memory_manager->_listener.free = (listener->free != NULL)
			? listener->free : KcMemoryListener_free;
		kc_memory_manager->_listener.error = (listener->error != NULL)
			? listener->error : KcMemoryListener_error;
	}
	return true;
}


// -------------------------------------
//  entries
// -------------------------------------
/**
 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
 * ハンドラの戻り値が false の場合、呼び出しを終了します。
 *
 * @param handler ハンドラ
 * @param info    ハンドラの第二引数に渡される付加情報
 * @return true/false (実行成功/実行失敗)
 */
static bool KcMemoryManager_entries(bool (*handler)(const KcMemoryEntry* entry, void* info), void* info)
{
	kc_memory_manager->_init();
	kc_lock_guard(kc_memory_manager->_mutex)
	{
		bool is_continue = true;
		for (
			KcMemoryEntry* current = kc_memory_manager->_head._next;
			is_continue && (current != &(kc_memory_manager->_tail));
			current = current->_next)
		{
			is_continue = handler(current, info);
		}
	}
	return true;
}


// -------------------------------------
//  freeif
// -------------------------------------
/**
 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
 * ハンドラの戻り値が true の場合、該当するメモリを解放します。
 * new で確保されたメモリが解放される際、デストラクタは呼び出されないため注意が必要です。
 *
 * @param handler ハンドラ
 * @param info    ハンドラの第二引数に渡される付加情報
 * @return true 固定
 */
static bool KcMemoryManager_freeif(bool (*handler)(const KcMemoryEntry* entry, void* info), void* info)
{
	kc_memory_manager->_init();
	kc_lock_guard(kc_memory_manager->_mutex)
	{
		bool is_free = false;
		for (
			KcMemoryEntry* current = kc_memory_manager->_head._next;
			current != &(kc_memory_manager->_tail);
			/* NOP */
			)
		{
			is_free = handler(current, info);
			current = current->_next;
			if (is_free)
			{
				kc_memory_manager->free(current->_prev->data);
			}
		}
	}
	return true;
}


// -------------------------------------
//  dump
// -------------------------------------
/**
 * 管理しているメモリ情報のダンプデータを引数に指定されたハンドラを実行します。
 *
 * @param handler ハンドラ
 * @param bytes   ダンプするバイト数
 * @param binary  true の場合、バイナリの16進数表現がダンプデータに追加されます。
 * @param ascii   true の場合、ASCII がダンプデータに追加されます。
 * @param column  カラム数
 */
static void KcMemoryManager_dump(bool (*handler)(const char* data),
		int bytes, bool binary, bool ascii, int column)
{
	kc_memory_manager->_init();
	kc_lock_guard(kc_memory_manager->_mutex)
	{
		bool is_continue = true;
		for (
			KcMemoryEntry* current = kc_memory_manager->_head._next;
			is_continue && (current != &(kc_memory_manager->_tail));
			current = current->_next)
		{
			is_continue = kc_memory_dump(
							kc_memory_manager->_tmpbuf,
							KC_MEMORY_MAX_BUFFER_SIZE,
							current,
							bytes,
							binary,
							ascii,
							column);
			if (is_continue)
			{	// エラーでなければハンドラを実行する。
				is_continue = handler(kc_memory_manager->_tmpbuf);
			}
		}
	}
}


/**
 * 指定されたサイズのメモリを確保します。
 *
 * @param size  確保するメモリサイズ
 * @param file  メモリ確保ファイル名
 * @psram func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_malloc(size_t size, const char* file, const char* func, int line)
{
	void* ptr = kc_memory_manager->_allocate(0, size, KC_MEMORY_ALLOCATED, file, func, line);
	return ptr;
}


/**
 * アライメント指定付きで、指定されたサイズのメモリを確保します。
 *
 * @param alignemnt アライメント
 * @param size  確保するメモリサイズ
 * @param file  メモリ確保ファイル名
 * @psram func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_aligned_alloc(size_t alignment, size_t size, const char* file, const char* func, int line)
{
	void* ptr = kc_memory_manager->_allocate(alignment, size, KC_MEMORY_ALLOCATED, file, func, line);
	return ptr;
}


/**
 * 指定されたサイズの nmemb 個の要素からなるメモリを確保します。
 * 確保したメモリ領域の内容は、0x00 で初期化されます。
 *
 * @param nmemb  確保するメモリ要素数
 * @param size   1要素あたりのメモリサイズ
 * @param file  メモリ確保ファイル名
 * @psram func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_calloc(size_t nmemb, size_t size, const char* file, const char* func, int line)
{
	size_t n = nmemb * size;
	void* ptr = kc_memory_manager->_allocate(size, n, KC_MEMORY_ALLOCATED, file, func, line);
	if (ptr != NULL)
	{
		memset(ptr, 0x00, n);
	}
	return ptr;
}


/**
 * 指定されたポインタが指すメモリサイズを変更します。
 *
 * @param ptr   メモリサイズを変更するポインタ
 * @param size  変更後のメモリサイズ
 * @param file  メモリ確保ファイル名
 * @psram func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_realloc(void* ptr, size_t size, const char* file, const char* func, int line)
{
	void* new_ptr = kc_memory_manager->_reallocate(ptr, size, KC_MEMORY_ALLOCATED, file, func, line);
	return new_ptr;
}


/**
 * 指定されたメモリを解放します。
 *
 * @param ptr 解放するメモリへのポインタ
 */
static void  KcMemoryManager_free(void* ptr)
{	// malloc, calloc 等で確保されたメモリを解放する。
	kc_memory_manager->_deallocate(ptr, KC_MEMORY_ALLOCATED);
}



// -------------------------------------
//  _init (初回目呼出し)
// -------------------------------------
/**
 * [内部利用関数]
 * KcMemoryManager を初期化します。
 * 内部で利用する mutex を初期化します。
 */
static void KcMemoryManager_init(void)
{	// _init に初期化ダミー関数を設定し、
	// 2回目以降本関数が Call されないようにする。
	kc_memory_manager->_init = KcMemoryManager_init_nop;

	// mutex を初期化する。
	int result = mtx_init(kc_memory_manager->_mutex, mtx_plain | mtx_recursive);
	if (result != thrd_success)
	{	// 基本的に失敗しないが、失敗した場合は、mutex に NULL を設定する。
		perror("kc_memory : can't init mutex");
		kc_memory_manager->_mutex = NULL;
	}
}


// -------------------------------------
//  _init  (2回目呼び出し以降)
// -------------------------------------
/**
 * KcMemoryManager の初期化ダミー関数。
 * _init の2回目以降の実行は、本関数が Call されます。
 */
static void KcMemoryManager_init_nop(void)
{
	// NOP
}


// -------------------------------------
//  _add
// -------------------------------------
/**
 * [内部利用関数]
 * 指定されたメモリエントリをメモリ管理に追加します。
 *
 * @param entry 追加するメモリエントリ
 * @return true/false (追加実施/追加失敗)
 */
static bool KcMemoryManager_add(KcMemoryEntry* entry)
{
	kc_memory_manager->_init();
	kc_lock_guard(kc_memory_manager->_mutex)
	{
		// [tail] の 1つ前に挿入する
		entry->_next                            = &(kc_memory_manager->_tail);
		entry->_prev                            = kc_memory_manager->_tail._prev;
		kc_memory_manager->_tail._prev->_next   = entry;
		kc_memory_manager->_tail._prev          = entry;
	}
	return true;
}


// -------------------------------------
//  _remove
// -------------------------------------
/**
 * [内部利用関数]
 * 指定されたメモリエントリをメモリ管理より削除します。
 *
 * @param entry 削除するメモリエントリ
 * @return true/false (削除実施/削除失敗)
 */
static bool KcMemoryManager_remove(KcMemoryEntry* entry)
{
	kc_memory_manager->_init();
	kc_lock_guard(kc_memory_manager->_mutex)
	{
		// entry の前後を直接リンクさせる
		entry->_prev->_next = entry->_next;
		entry->_next->_prev = entry->_prev;
	}
	return true;
}


// -------------------------------------
//  _allocate
// -------------------------------------
/**
 * [内部利用関数]
 * 指定されたサイズのメモリを確保します。
 * 確保に失敗した場合、NULL を返します。
 * alignment > 0 の場合、アライメント指定でメモリを確保します。
 *
 * @param alignment アライメント
 * @param size      確保するメモリサイズ
 * @param file      メモリ確保ファイル名
 * @param func      メモリ確保関数名
 * @param line      メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_allocate(size_t alignment, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line)
{
	void* data_ptr = NULL;
	KcMemoryEntry* entry = KcMemoryEntry_new(NULL, alignment, size, mark, file, func, line);
	if (entry != NULL)
	{	// メモリ確保成功
		kc_memory_manager->_add(entry);
		kc_memory_manager->_listener.allocate(entry);
		data_ptr = entry->data;
	}
	else
	{	// メモリ確保失敗  => エラー通知する。
		KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line);
		kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't allocate");
	}
	return data_ptr;
}


// -------------------------------------
//  _reallocate
// -------------------------------------
/**
 * [内部利用関数]
 * 指定された ptr のメモリサイズを変更します。
 * ptr = NULL の場合は、KcMemoryManager_allocate の alignemt = 0 と同様の動作となります。
 * 確保に失敗した場合、NULL を返します。
 *
 * @param ptr   ポインタ
 * @param size  確保するメモリサイズ
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_reallocate(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line)
{
	if (ptr == NULL)
	{
		return kc_memory_manager->_allocate(0, size, mark, file, func, line);
	}

	void* data_ptr = NULL;
	KcMemoryEntry* entry = (KcMemoryEntry*) ptr;
	entry--;
	switch (entry->mark)
	{
		case KC_MEMORY_DELETED:				// 削除済みメモリ => 通常の allocate
			data_ptr = kc_memory_manager->_allocate(0, size, mark, file, func, line);
			break;
		case KC_MEMORY_ALLOCATED:			// 管理されたメモリの realloc
			data_ptr = kc_memory_manager->_reallocate_managed_ptr(ptr, size, mark, file, func, line);
			break;
		case KC_MEMORY_ALLOCATED_NEW:		// new で確保されたメモリの realloc のためエラー
			data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line);
			break;
		case KC_MEMORY_ALLOCATED_NEW_ARRAY:	// new[] で確保されたメモリの realloc のためエラー
			data_ptr = kc_memory_manager->_reallocate_invalid_ptr(ptr, size, mark, file, func, line);
			break;
		default:							// 管理外メモリの realloc
			data_ptr = kc_memory_manager->_reallocate_unmanaged_ptr(ptr, size, mark, file, func, line);
			break;
	}
	return data_ptr;
}


// -------------------------------------
//  _reallocate_managed_ptr
// -------------------------------------
/**
 * [内部利用関数]
 * 管理されたメモリ領域に対する realloc を実施します。
 * ※指定するポインタは、必ず管理されたメモリ領域である必要があります。
 *
 * @param ptr   ポインタ
 * @param size  確保するメモリサイズ
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_reallocate_managed_ptr(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line)
{
	KcMemoryEntry* entry = (KcMemoryEntry*) ptr;
	entry--;

	// (A) 一旦メモリを管理から外す。
	kc_memory_manager->_remove(entry);

	void* data_ptr = NULL;
	KcMemoryEntry* new_entry = KcMemoryEntry_new(entry, 0, size, mark, file, func, line);
	if (new_entry != NULL)
	{	// メモリ確保成功
		kc_memory_manager->_listener.free(entry);
		kc_memory_manager->_add(new_entry);
		kc_memory_manager->_listener.allocate(new_entry);
		data_ptr = new_entry->data;
	}
	else
	{	// メモリ確保失敗  => エラー通知する。
		KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line);
		kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate");

		// (B) 古いメモリ領域は残っているため、(A) のメモリを管理対象に戻す。
		kc_memory_manager->_add(entry);
	}
	return data_ptr;
}


// -------------------------------------
//  _reallocate_invalid_ptr
// -------------------------------------
/**
 * [内部利用関数]
 * new, new[] で確保されたメモリに対する realloc 実施によるエラー処理を実施します。
 *
 * @param ptr   ポインタ
 * @param size  確保するメモリサイズ
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_reallocate_invalid_ptr(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line)
{
	UNUSED_VARIABLE(ptr);
	KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line);
	kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate (invalid pointer)");
	errno = EINVAL;
	return NULL;
}


// -------------------------------------
//  _reallocate_unmanaged_ptr
// -------------------------------------
/**
 * [内部利用関数]
 * 管理外メモリ領域に対する realloc を実施します。
 *
 * @param ptr   ポインタ
 * @param size  確保するメモリサイズ
 * @param file  メモリ確保ファイル名
 * @param func  メモリ確保関数名
 * @param line  メモリ確保行番号
 * @return 確保したメモリへのポインタ
 */
static void* KcMemoryManager_reallocate_unmanaged_ptr(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line)
{
	// |
	// +----------+-------------------+
	// | 元の領域 | 追加分 + 管理領域 |
	// +----------+-------------------+
	// ↓
	// ↓ memmove で 元の領域 + 追加分を、
	// ↓ 管理領域分を確保した先にコピーする
	// ↓
	// +----------+----------+--------+
	// | 管理領域 | 元の領域 | 追加分 |
	// +----------+----------+--------+
	void* data_ptr = NULL;
	KcMemoryEntry* new_entry = KcMemoryEntry_new(ptr, 0, size, mark, file, func, line);
	if (new_entry != NULL)
	{	// メモリ確保成功
		// memmove で 元の領域 + 追加分 をコピーして、メモリエントリとして追加する。
		memmove((new_entry + 1), new_entry, size);
		kc_memory_manager->_add(new_entry);
		kc_memory_manager->_listener.allocate(new_entry);
		data_ptr = new_entry->data;
	}
	else
	{	// メモリ確保失敗  => エラー通知する。
		KcMemoryEntry_set(&(kc_memory_manager->_error), size, mark, file, func, line);
		kc_memory_manager->_listener.error(&kc_memory_manager->_error, "can't reallocate");
	}
	return data_ptr;
}


// -------------------------------------
//  _deallocate
// -------------------------------------
/**
 * [内部利用関数]
 * 指定されたポインタのメモリを解放します。
 *
 * @param ptr           解放するメモリへのポインタ
 * @param expected_mark 期待するメモリ状態
 */
static void KcMemoryManager_deallocate(void* ptr, KcMemoryMark expected_mark)
{
	if (ptr == NULL)
	{
		return;
	}

	KcMemoryEntry* entry = (KcMemoryEntry*) ptr;
	entry--;
	if (entry->mark == expected_mark)
	{	// 期待するメモリ状態の場合、そのまま解放する。
		kc_memory_manager->_listener.free(entry);
		kc_memory_manager->_remove(entry);
		KcMemoryEntry_delete(entry);
	}
	else
	{	// 期待通りでない場合、メモリ状態に応じて警告を通知する。
		switch (entry->mark)
		{
			case KC_MEMORY_DELETED:							// 削除済みメモリ
				// Nothing to do.
				break;
			case KC_MEMORY_ALLOCATED:						// malloc 等で確保されたメモリ
				kc_memory_manager->_listener.error(entry, "invalid free memory (please use free)");
				kc_memory_manager->_listener.free(entry);
				kc_memory_manager->_remove(entry);
				KcMemoryEntry_delete(entry);
				break;
			case KC_MEMORY_ALLOCATED_NEW:					// new で確保されたメモリ
				kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete)");
				kc_memory_manager->_listener.free(entry);
				kc_memory_manager->_remove(entry);
				KcMemoryEntry_delete(entry);
				break;
			case KC_MEMORY_ALLOCATED_NEW_ARRAY:				// new[] で確保されたメモリ
				kc_memory_manager->_listener.error(entry, "invalid free memory (please use delete[])");
				kc_memory_manager->_listener.free(entry);
				kc_memory_manager->_remove(entry);
				KcMemoryEntry_delete(entry);
				break;
			default:										// 管理外メモリ
				free(ptr);
				break;
		}
	}
}