/* vim: ts=4 sw=4 sts=4 ff=unix fenc=utf-8 : * ===================================================================== * sc_mmgr.c * Copyright (c) 2003 - 2011 sys0tem * LICENSE : * LGPL (GNU Lesser General Public License - Version 3,29 June 2007) * http://www.gnu.org/copyleft/lesser.html * or * EPL (Eclipse Public License - v1.0) * http://www.eclipse.org/legal/epl-v10.html * ===================================================================== * * メモリ管理 * * 管理の仕組み * * |<-------- 管理メモリ ------->| * +--------+--------------------+ * |管理領域|ユーザ使用メモリ領域| * +--------+--------------------+ * ↑ユーザにはこの位置を返す。 */ #include <string.h> #include <sc_error.h> /* 本来の malloc, free を使用するため SC_DEBUG を無効にする。 */ #ifdef SC_DEBUG #undef SC_DEBUG #endif #include <sc_mmgr.h> /* ===================================================================== * 変数定義 * ===================================================================== */ static bool SC_MMgr_initialized = false; static SC_MMgr SC_MMgr_head; /*< 管理メモリ先頭 */ static SC_MMgr SC_MMgr_tail; /*< 管理メモリ末尾 */ static SC_MMgr SC_MMgr_error; /*< エラー発生時用 */ static void (*SC_MMgr_mHandler)(SC_MMgr*) = NULL; static void (*SC_MMgr_fHandler)(SC_MMgr*) = NULL; static void (*SC_MMgr_eHandler)(SC_MMgr*) = NULL; /* ===================================================================== * プロトタイプ宣言 * ===================================================================== */ static bool SC_MMgr_isMatchEntry(SC_MMgr* mgr, const char* file, int line); static void SC_MMgr_init(void); static void SC_MMgr_add(SC_MMgr* entry); static void SC_MMgr_remove(SC_MMgr* entry); static void* SC_MMgr_allocate(size_t n, const char* file, int line); static void* SC_MMgr_reallocate(void* ptr, size_t n, const char* file, int line, bool isMng); /** * メモリ確保、開放、エラー発生時のハンドラを設定します。 * ハンドラを解除する場合、値にNULLを指定してください。 * * エラー時には、以下のエラー番号が設定されている可能性があります。 * <pre> * SC_ENOMEM : メモリ不足 * SC_EINVAL : 引数不正 * </pre> * * @param mHandler メモリ確保時のハンドラ * @param fHandler メモリ開放時のハンドラ * @param eHandler エラー発生時のハンドラ */ void SC_MMgr_setHandler( void (*mHandler)(SC_MMgr*), void (*fHandler)(SC_MMgr*), void (*eHandler)(SC_MMgr*)) { SC_MMgr_mHandler = mHandler; SC_MMgr_fHandler = fHandler; SC_MMgr_eHandler = eHandler; } /** * 管理している全メモリエントリを引数に、 * 指定されたハンドラを実行します。 * * @param handler ハンドラ */ void SC_MMgr_entries(void (*handler)(SC_MMgr*)) { SC_MMgr* entry; /* 一度も初期化されていない場合、何もしない */ if (SC_MMgr_initialized) { entry = SC_MMgr_head._next; while (entry != &SC_MMgr_tail) { handler(entry); entry = entry->_next; } } } /** * 指定されたソースファイル名、行番号で確保されたメモリを強制開放します。 * 既に開放済みの場合はなにもしません。 * fileがNULLの場合、すべての管理メモリを開放します。 * lineが負の値の場合、行番号は無視します。 * * @param file ファイル名 * @param line 行番号 */ void SC_MMgr_cleanup(const char* file, int line) { bool ret; SC_MMgr* entry; SC_MMgr* tmpEntry; /* 一度も初期化されていない場合、何もしない */ if (SC_MMgr_initialized) { entry = SC_MMgr_head._next; while (entry != &SC_MMgr_tail) { ret = SC_MMgr_isMatchEntry(entry, file, line); if (ret) { /* 条件に一致⇒メモリを開放する */ tmpEntry = entry; entry = entry->_next; SC_MMgr_remove(tmpEntry); tmpEntry->_mark = SC_MMgr_DELETED; tmpEntry->size = 0; free(tmpEntry); } else { entry = entry->_next; } } } } /** * 本来の malloc。 * * @param size サイズ * @return ポインタ */ void* SC_realMalloc(size_t size) { return malloc(size); } /** * 指定されたメモリサイズのメモリを確保します。 * * @param nmemb 確保するメモリの要素数 * @param size 1つのメモリ要求サイズ * @param file 呼び出し元ソースファイル * @param line 呼び出し元ソース行番号 */ void* SC_calloc(size_t nmemb, size_t size, const char* file, int line) { size_t n = nmemb * size; void* ptr = SC_MMgr_allocate(n, file, line); if (ptr != NULL) { memset(ptr, 0x00, n); } return ptr; } /** * 指定されたサイズのメモリを確保します。 * * @param size 確保するメモリサイズ * @param file 呼び出し元ソースファイル * @param line 呼び出し元ソース行番号 * @return 確保したメモリへのポインタ */ void* SC_malloc(size_t size, const char* file, int line) { void* ptr = SC_MMgr_allocate(size, file, line); return ptr; } /** * ポインタが示すメモリブロックのサイズを変更します。 * * @param ptr ポインタ * @param size 確保するメモリのサイズ * @param file 呼び出し元ソースファイル * @param line 呼び出し元ソース行番号 * @return 確保したメモリへのポインタ */ void* SC_realloc(void* ptr, size_t size, const char* file, int line) { SC_MMgr* entry; if (ptr == NULL) { /* ptr が NULL の場合、 malloc と等価 */ ptr = SC_malloc(size, file, line); return ptr; } if (size == 0) { /* size が 0 の場合 free と等価 */ SC_free(ptr); return NULL; } /* メモリ管理領域を取得 */ entry = (SC_MMgr*) ptr; entry--; switch (entry->_mark) { case SC_MMgr_ALLOCATED: /* 管理メモリ */ ptr = SC_MMgr_reallocate(entry, size, file, line, true); break; case SC_MMgr_DELETED: /* 削除済みメモリ */ ptr = SC_malloc(size, file, line); break; default: /* 管理外メモリ */ ptr = SC_MMgr_reallocate(ptr, size, file, line, false); break; } return ptr; } /** * 指定されたメモリを開放します。 * * @param ptr 開放するメモリへのポインタ */ void SC_free(void* ptr) { SC_MMgr* entry; if (ptr == NULL) { /* NULL ポインタに対してはなにもしない */ return; } /* メモリ管理領域を取得 */ entry = (SC_MMgr*) ptr; entry--; switch (entry->_mark) { case SC_MMgr_ALLOCATED: /* メモリ開放 */ SC_MMgr_remove(entry); entry->_mark = SC_MMgr_DELETED; entry->size = 0; free(entry); break; case SC_MMgr_DELETED: /* メモリ二重開放 */ SC_setError(SC_EINVAL); if (SC_MMgr_eHandler != NULL) { SC_MMgr_eHandler(entry); } break; default: /* 管理外メモリ開放 */ entry++; free(entry); break; } } /* ===================================================================== * 以下、内部でのみ使用する関数 * ===================================================================== */ /** * 初期化します。 * 初期化は一度だけ実施されます。 */ static void SC_MMgr_init(void) { if (!SC_MMgr_initialized) { SC_MMgr_initialized = true; SC_MMgr_head.file = SC_MMgr_tail.file = NULL; SC_MMgr_head.line = SC_MMgr_tail.line = 0; SC_MMgr_head._prev = SC_MMgr_head._next = &SC_MMgr_tail; SC_MMgr_tail._prev = SC_MMgr_tail._next = &SC_MMgr_head; SC_MMgr_head._mark = SC_MMgr_tail._mark = SC_MMgr_DELETED; /* エラー処理用 */ SC_MMgr_error.file = NULL; SC_MMgr_error.line = 0; SC_MMgr_error._mark = SC_MMgr_DELETED; SC_MMgr_error._prev = NULL; SC_MMgr_error._next = NULL; } } /** * エントリを追加します。 * * @param entry 追加するエントリ */ static void SC_MMgr_add(SC_MMgr* entry) { SC_MMgr_init(); entry->_next = &SC_MMgr_tail; entry->_prev = SC_MMgr_tail._prev; SC_MMgr_tail._prev->_next = entry; SC_MMgr_tail._prev = entry; if (SC_MMgr_mHandler != NULL) { /* ハンドラが設定されている場合、実行する */ SC_MMgr_mHandler(entry); } } /** * エントリを削除する。 * * @param entry 削除するエントリ */ static void SC_MMgr_remove(SC_MMgr* entry) { entry->_prev->_next = entry->_next; entry->_next->_prev = entry->_prev; if (SC_MMgr_fHandler != NULL) { /* ハンドラが設定されている場合、実行する */ SC_MMgr_fHandler(entry); } } /** * 指定された size のメモリを確保します。 * 確保できない場合、NULL を返します。 * * @param size サイズ * @param file ファイル * @param line 行番号 * @return 確保したメモリへのポインタ */ static void* SC_MMgr_allocate(size_t n, const char* file, int line) { SC_MMgr* pm; pm = (SC_MMgr*) malloc(n + sizeof(SC_MMgr)); if (pm == NULL) { /* メモリ確保失敗 -> NULL を返す */ SC_setError(SC_ENOMEM); if (SC_MMgr_eHandler != NULL) { /* エラーハンドラ実行 */ SC_MMgr_error.size = n; SC_MMgr_error.file = file; SC_MMgr_error.line = line; SC_MMgr_eHandler(&SC_MMgr_error); } return NULL; } pm->_mark = SC_MMgr_ALLOCATED; pm->size = n; pm->file = file; pm->line = line; SC_MMgr_add(pm); return (pm + 1); } /** * 指定されたポインタが示すメモリブロックのサイズを n に変更します。 * * @param ptr ポインタ * @param n 変更後のサイズ * @param file 呼び出し元ソースファイル * @param line 呼び出し元ソース行番号 * @param isMng true/false (管理メモリ/管理外メモリ) * @return メモリへのポインタ */ static void* SC_MMgr_reallocate(void* ptr, size_t n, const char* file, int line, bool isMng) { SC_MMgr* pm; if (isMng) { SC_MMgr_remove(ptr); } pm = (SC_MMgr*) realloc(ptr, n + sizeof(SC_MMgr)); if (pm == NULL) { /* メモリ確保失敗 -> NULL を返す。 * 元の領域は開放されない(再管理する) */ SC_MMgr_add(ptr); SC_setError(SC_ENOMEM); if (SC_MMgr_eHandler != NULL) { /* エラーハンドラ実行 */ SC_MMgr_error.size = n; SC_MMgr_error.file = file; SC_MMgr_error.line = line; SC_MMgr_eHandler(&SC_MMgr_error); } return NULL; } if (!isMng) { /* 管理対象外メモリの場合 |<---- 新たな確保領域 ---->| +----------+---------------+ |元々の領域|追加分+管理領域| +----------+---------------+ ↓ +--------+----------+------+ |管理領域|元々の領域|追加分| +--------+----------+------+ */ memmove((pm + 1), pm, n); } /* 管理領域に情報を書き込む */ pm->_mark = SC_MMgr_ALLOCATED; pm->size = n; pm->file = file; pm->line = line; SC_MMgr_add(pm); return (pm + 1); } /** * 指定されたエントリが、指定されたソースファイル、行番号で * 確保されたメモリかどうか確認します。 * file が NULL の場合、常に ture を返します。 * line が 0 未満の場合、行番号は無視します。 * * @param mgr エントリ * @param file ファイル名 * @param line 行番号 * @return true/false (一致/不一致) */ static bool SC_MMgr_isMatchEntry(SC_MMgr* mgr, const char* file, int line) { int ret; if (file == NULL) { /* ファイル名指定なし */ return true; } ret = strcmp(file, mgr->file); if (ret == 0) { /* ファイル名一致 */ if ((line < 0) || (line == mgr->line)) { /* 行番号指定なし or 行番号一致 */ return true; } } return false; }