Newer
Older
c-interpreter / modules / libkc / include / kc_memory.h
Nomura Kei on 9 Aug 2023 9 KB UPDATE
/**
 * @file   kc_memory.h
 * @brief  KC メモリ管理モジュール
 * @copyright  2003 - 2023  Nomura Kei
 * @depends
 *	kc.h
 *	kc_memory.c
 */
#ifndef KC_MEMORY_H
#define KC_MEMORY_H

#include <threads.h>

#include <kc.h>


#ifdef __cplusplus
extern "C" {
namespace kc {
using namespace std;
#endif



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

/**
 * メモリ状態
 */
typedef enum
{
    KC_MEMORY_DELETED               = 0x55AA0000,	//!< 解放済み
    KC_MEMORY_ALLOCATED             = 0x55AA1111,	//!< 確保済み
    KC_MEMORY_ALLOCATED_NEW         = 0x55AA2222,	//!< new により確保済み
    KC_MEMORY_ALLOCATED_NEW_ARRAY   = 0x55AA4444	//!< new[] により確保済み
} KcMemoryMark;


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



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

/**
 * メモリエントリ。
 */
typedef struct KcMemoryEntry_
{
    int                     size;                   //!< 確保サイズ
    KcMemoryMark            mark;                   //!< 確保メモリ状態
    const char*             file;                   //!< メモリ確保ファイル名
    const char*             func;                   //!< メモリ確保関数名
    int                     line;                   //!< メモリ確保行番号
    struct KcMemoryEntry_*  _prev;                  //!< 前の管理メモリポインタ
    struct KcMemoryEntry_*  _next;                  //!< 次の管理メモリポインタ
    void*                   data;                   //!< データ
    // 構造体末尾の配列に限りサイズ省略可能 (C99 : incomplete array)
} KcMemoryEntry;



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

/**
 * メモリ確保、解放、エラー発生時の通知用リスナ。
 */
typedef struct
{
	/**
	 * メモリ確保の際に呼び出されます。
	 *
	 * @param entry 確保されたメモリエントリ
	 */
	void (*allocate)(const KcMemoryEntry* entry);


	/**
	 * メモリ解放の際に呼び出されます。
	 *
	 * @param entry 解放されるメモリエントリ
	 */
	void (*free)(const KcMemoryEntry* entry);


	/**
	 * エラー発生時に呼び出されます。
	 *
	 * @param entry エラーが発生したメモリエントリ (NULL の場合があります。)
	 * @param msg   エラー発生時のメッセージ
	 */
	void (*error)(const KcMemoryEntry* entry, const char* msg);

} KcMemoryListener;



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

/** メモリ管理で扱うバッファサイズ			*/
#define KC_MEMORY_MAX_BUFFER_SIZE (4096)


/**
 * メモリの確保、解放を管理します。
 */
typedef struct KcMemoryManager_
{
	/**
	 * メモリ確保、解放、エラー発生時に通知するためのリスナを登録します。
	 *
	 * @param listener 登録するリスナ
	 * @return true/false (リスナ登録成功/リスナ登録失敗)
	 */
	bool (*set_listener)(KcMemoryListener* listener);


	/**
	 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
	 * ハンドラの戻りが false の場合、呼び出しを終了します。
	 *
	 * @param handler ハンドラ
	 * @param info    ハンドラの第二引数に渡される付加情報
	 * @return true/false (実行成功/実行失敗)
	 */
	bool (*entries)(bool (*handler)(const KcMemoryEntry* entry, void* info), void* info);


	/**
	 * 管理しているメモリエントリを引数に指定されたハンドラを実行します。
	 * ハンドラの戻り値が true の場合、該当するメモリを解放します。
	 * new で確保されたメモリが解放される際、デストラクタは呼び出されないため注意が必要です。
	 *
	 * @param handler ハンドラ
	 * @param info    ハンドラの第二引数に渡される付加情報
	 * @return true 固定
	 */
	bool (*freeif)(bool (*handler)(const KcMemoryEntry* entry, void* info), void* info);


	/**
	 * 管理しているメモリ情報のダンプデータを引数に指定されたハンドラを実行します。
	 *
	 * @param handler ハンドラ
	 * @param byte    ダンプするバイト数
	 * @param binary  true の場合、バイナリの16進数表現がダンプデータに追加されます。
	 * @param ascii   true の場合、ASCIIがダンプデータに追加されます。
	 * @param column  カラム数
	 */
	void (*dump)(bool (*handler)(const char* data),
			int bytes, bool binary, bool ascii, int column);


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


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


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


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


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


	// =========================================================================
	//  内部利用関数
	// =========================================================================

	/**
	 * [内部利用関数]
	 * KcMemoryManager を初期化します。
	 * 本関数は、KcMemoryManager の各関数が実行される際に呼び出され、
	 * 一度だけ初期化処理を実施します。
	 */
	void (*_init)(void);

	bool (*_add)(KcMemoryEntry* entry);
	bool (*_remove)(KcMemoryEntry* entry);
	void* (*_allocate)(size_t  alignment, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
	void* (*_reallocate)(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
	void* (*_reallocate_managed_ptr)(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
	void* (*_reallocate_invalid_ptr)(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
	void* (*_reallocate_unmanaged_ptr)(void* ptr, size_t size,
		KcMemoryMark mark, const char* file, const char* func, int line);
	void (*_deallocate)(void* ptr, KcMemoryMark expected_mark);


	// =========================================================================
	//  内部利用変数
	// =========================================================================
	KcMemoryListener    _listener;							//!< リスナ
	KcMemoryEntry       _head;								//!< 管理メモリの先頭
	KcMemoryEntry       _tail;								//!< 管理メモリの末尾
	KcMemoryEntry       _error;								//!< エラー発生時一時利用
	char                _tmpbuf[KC_MEMORY_MAX_BUFFER_SIZE];	//!< 一時利用のためのバッファ
	mtx_t*              _mutex;								//!< 同期実行利用のための Mutex

} KcMemoryManager;



/**
 * KcMemoryManager の唯一のインスタンス。
 */
extern KcMemoryManager* const  kc_memory_manager;


#ifdef KC_MEMORY_ENABLED
#define malloc(       size)  kc_memory_manager->malloc(       size, __FILE__, __func__, __LINE__)
#define calloc(nmemb, size)  kc_memory_manager->calloc(nmemb, size, __FILE__, __func__, __LINE__)
#define realloc(ptr , size)  kc_memory_manager->realloc(ptr , size, __FILE__, __func__, __LINE__)
#define free(ptr)            kc_memory_manager->free(ptr)
#else
#include <stdlib.h>
#endif


#ifdef __cplusplus
}   // namespace kc
}   // extern "C"
#endif
#endif  // KC_MEMORY_H