Newer
Older
libkc / modules / src / kc_memory.c
/**
 * @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_memory_entry_inner.h"
#include "kc_memory_listener_inner.h"

#include <kc_lock_guard.h>

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

// --- KcMemory
static void KcMemory_dump_leak(void);
static bool KcMemory_print(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;

// =============================================================================
//  KcMemory
// =============================================================================
static KcMemoryListener KcMemory_listener;

/**
 * メモリ管理を開始します。
 *
 * 本関数を実行することで、次の動作が実施されます。
 * (1) メモリ確保失敗時に、エラー情報を出力します。
 * (2) プログラム終了時に、解放されていないメモリを出力します。
 * (3) detail が true の場合、メモリ確保/解放時に出力します。
 * ※出力先は、いずれも stderr となります。
 *
 * @param detail true/false (メモリ確保/解放の情報を出力する/出力しない)
 */
void KcMemory_start(bool detail)
{
	kc_memory_manager->_init();
	atexit(KcMemory_dump_leak);
	if (detail)
	{
		KcMemory_listener.allocate = KcMemoryListener_dump_allocate;
		KcMemory_listener.free = KcMemoryListener_dump_free;
	}
	else
	{
		KcMemory_listener.allocate = NULL;
		KcMemory_listener.free = NULL;
	}
	KcMemory_listener.error = KcMemoryListener_dump_error;
	kc_memory_manager->set_listener(&KcMemory_listener);
}

/**
 * 現在確保されているメモリ情報を出力します。
 */
void KcMemory_dump(void)
{
	kc_memory_manager->dump(KcMemory_print, 16, true, true, 130);
}

/**
 * メモリリークしているメモリ情報をダンプします。
 */
static void KcMemory_dump_leak(void)
{
	if (kc_memory_manager->_head._next != &(kc_memory_manager->_tail))
	{
		fprintf(stderr, "##### Leak Memory #####\n");
		KcMemory_dump();
	}
}

/**
 * 指定されたメッセージを標準エラーに出力し、true を返します。
 *
 * @param msg 標準エラーに出力するメッセージ
 * @return true (固定)
 */
static bool KcMemory_print(const char *msg)
{
	fprintf(stderr, "%s", msg);
	return true;
}

// =============================================================================
//  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 = KcMemoryDump_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  メモリ確保ファイル名
 * @param 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  メモリ確保ファイル名
 * @param 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  メモリ確保ファイル名
 * @param 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\n");
	}
	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\n");

		// (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)\n");
	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\n");
	}
	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)\n");
			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)\n");
			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[])\n");
			kc_memory_manager->_listener.free(entry);
			kc_memory_manager->_remove(entry);
			KcMemoryEntry_delete(entry);
			break;
		default: // 管理外メモリ
			free(ptr);
			break;
		}
	}
}